设计模式------代理模式
一,简介
代理模式是java常用的设计模式之一,它为目标类提供一个代理类,由代理类在原有的基础上进行增强,意味着不会改变原有的核心代码,就跟我们常说的中介是一样的,代理模式说白了就是使用一个类来代替我们另一个类的功能.
举个例子:作为程序员的我,一直单身,每逢过节回家,父母就会安排媒人来介绍姑娘进行相亲,这个时候媒人会安排姑娘进行见面,这个过程中,姑娘充当的角色是我们的目标对象,而媒人充当的就是我们的中介(代理),由媒人安排详情,进一步考虑如果我与姑娘一见钟情了,那避免不了结婚,因此会找婚姻办事处去处理我们的结婚计划,比如宴席,婚姻流程等等,我们只需要站在属于自己的舞台上就行了.那么这个过程,婚姻办事处就是咱们的代理中介,上图.
以上就描述了代理之间的关系
二,代理模式分类
代理模式通常个有两种不同的形式
①静态代理
②动态代理
而动态代理又可以分为jdk动态代理和cglib动态代理
三,优缺点
1.优点
①职责清晰
②高扩展性
③智能化
2.缺点
①系统的复杂性增加
②由于新加了一个类,并在源类的基础上进行了增强,因此可能系统的性能会出现问题
鉴于代理模式的优缺点,我们在项目的实际开发中可以灵活的使用.
四,静态代理
静态代理是指代理类由我们程序员去创建,意味着我们的程序在启动之前.class的文件就已经生成了,我们现在的开发无外乎就是增删改查,只是变着法子,业务逻辑不一样而已,假如我现在有一个场景:我有一套增删改查的方法,现在呢,我想要在他的每一个方法执行之前都加一个日志记录的功能,方便我后期的维护,那么怎么实现呢,接下来咱们一起实现一下
代码实现
1.创建接口
/** * 用户信息接口 */public interface UserDao { public void add(); public void delete(); public void alert(); public void query();}
2.创建目标实现类
package com.dahai.proxy.staticproxy;/** * @program: Spring-Study * @description:目标实现类 * @author: Mr ZHAN * @create: 2022-05-26 16:04 **/public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("************[增加]了一个用户************"); } @Override public void delete() { System.out.println("************[删除]了一个用户************"); } @Override public void alert() { System.out.println("************[修改]了一个用户************"); } @Override public void query() { System.out.println("************[查询]了一个用户************"); }}
那么问题来了,我现在要增加日志在以上的每一个方法里面,我不可能在每一个方法里面都去加日志,这样会导致系统的可维护性变差,咱们的开发除了遵循开闭原则之外,我们还要保证他的可维护性,也就意味着我们要在不改变原有代码的基础上进行增强,那么就需要代理模式来实现了.
3.创建代理类
package com.dahai.proxy.staticproxy;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 16:09 **/public class UserDaoProxyIpml implements UserDao { //注入目标对象 private UserDao userDao; //设置值(通过set方式注入) public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void add() { log("add"); userDao.add(); } @Override public void delete() { log("delete"); userDao.delete(); } @Override public void alert() { log("alert"); userDao.alert(); } @Override public void query() { log("query"); userDao.query(); } /** * 日志 * * @param message */ public void log(String message) { System.out.println("*********[" + "]**增加了一条日志"); }}
4.main测试
package com.dahai.proxy.staticproxy;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 16:17 **/public class UserDaoTest { public static void main(String[] args) { //目标类 UserDaoImpl userDao = new UserDaoImpl(); //增强类 UserDaoProxyIpml userDaoProxyIpml = new UserDaoProxyIpml(); //设置增强值 userDaoProxyIpml.setUserDao(userDao); //调用增强后的方法 userDaoProxyIpml.add(); }}
结果
可以看到,成功的使用了静态代理对我们的业务逻辑进行了加强,但是使用静态代理会带来一定的问题,如果我们需要增强的方法有1000个呢,我们需要給每一个类都进行加强,因此也会产生1000个代理类,这样的方式不仅会使开发代理困难,而且还会使我们的程序可维护性降低.因此我们需要通过动态代理的模式来实现.
五,动态代理
动态代理通常我们又会分为jdk动态代理和cglib动态代理,我们熟知的spring的aop就是在动态动态代理的基础上实现的,默认使用jdk动态代理,如果使用cglib需要在spring的aop配置文件里面开启.
1.jdk动态代理
jdk动态代理是基于接口实现的,代理类实现被代理类的接口,并实现里面的方法,再次基础上进行扩展,jdk的动态代理有两个核心
①java.lang.reflect.Proxy类,它里面的newProxyInstance(ClassLoader loader, 类[] interfaces, InvocationHandler h)方法目的用来创建代理对象.
②InvocationHandler处理器
代码实现:
UserDao与实现类与上面一样
代理类
package com.dahai.proxy.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 16:47 **/public class UserDaoDynamicProxy { //目标增强对象 private Object target; //设置值(使用set) public void setTarget(Object target) { this.target = target; } /** * 获取代理对象 * @return */ public Object getInstance(){Object proxyInstance= Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); //执行目标方法 method.invoke(target, args); return proxy; } }); return proxyInstance; } public void log(String message){ System.out.println("****************["+message+"]****加入了日志"); }}
测试
/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 16:47 **/public class UserDaoDynamicProxyTest { public static void main(String[] args) { //创建目标对象 UserDaoImpl userDao=new UserDaoImpl(); //获取代理对象 UserDaoDynamicProxy userDaoDynamicProxy = new UserDaoDynamicProxy(); userDaoDynamicProxy.setTarget(userDao); UserDao instance = (UserDao) userDaoDynamicProxy.getInstance(); //执行方法 instance.add(); }}
通过测试发现可以实现与静态代理同样的效果
优点:相对于静态代理,动态代理大大减少了开发任务,同时减少了对业务接口的依赖,降低了耦合度。
缺点:Proxy是所有动态生成的代理的共同的父类,因此服务类必须是接口的形式,不能是普通类的形式,因为Java无法实现多继承。
2.cglib动态代理
jdk动态代理的实现是通过实现接口来完成的,那么对于没有接口的类来说呢,那么就要用到cglib动态代理来实现了,cglib动态代理的实现底层是通过字节码技术来实现的,通过字节码技术为一个类创建一个子类,通过重写父类方法,拦截所有父类的方法,进行横切来实现动态加强,jdk与cglib动态代理都是实现spring aop的基础.
Cglib子类代理实现方法:
(1)引入cglib的jar文件,asm的jar文件
(2)代理的类不能为final
(3)目标业务对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
代码实现
①引入jar
org.ow2.asm asm 9.3 cglib cglib-nodep 3.3.0
②创建目标类
package com.dahai.proxy.cglibproxy;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 17:22 **/public class Rent { public void byHouse(){ System.out.println("买大房子"); }}
③创建代理
package com.dahai.proxy.cglibproxy;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.invoke.MethodHandle;import java.lang.reflect.Method;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 17:23 **/public class CglibProxyInstance implements MethodInterceptor { //目标对象 private Object target; //工具类 private Enhancer enhancer = new Enhancer(); public CglibProxyInstance(Object target) { this.target = target; } //获取代理对象 public Object getProxy() { //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //返回对象 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); method.invoke(target,objects); System.out.println("付钱"); return o; }}
④测试
package com.dahai.proxy.cglibproxy;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/** * @program: Spring-Study * @description: * @author: Mr ZHAN * @create: 2022-05-26 17:23 **/public class CglibProxyInstanceTest { public static void main(String[] args) { //创建目标对象 Rent rent = new Rent(); //获取代理对象 CglibProxyInstance cglibProxyInstance = new CglibProxyInstance(rent); Rent proxy = (Rent) cglibProxyInstance.getProxy(); //调用方法 proxy.byHouse(); }}
通过测试我们同样可以发现增强成功.
六,总结
总而言之,设计模式就是帮助我们开发的,因此我们需要在使用的过程中根据真实情况去选择静态代理还是动态代理,是使用cglib还是jdk,但是万变不离其宗,代理模式遵循以下步骤
①接口
②目标类实现接口
③创建获取代理类对象
④使用
注意:转载请注明出处