JAVA中的代理 2020-03-14 16:16 # 代理 > 代理模式是一种经典的设计模式,其意义在于生成代理对象,在服务提供方和使用方之间充当一个媒介,控制真实对象的访问。 拦截器在 Spring AOP 与 Spring MVC 中都有应用。在 Spring AOP 中, - 针对接口做代理默认使用的是 JDK 自带的 Proxy+InvocationHandler - 针对类做代理使用的是 Cglib 代理的作用:**不修改目标代码的情况下,控制对目标的访问**。可以在其前后加上自己的业务处理代码,甚至阻止对目标方法的访问,有点类似于过滤器和拦截器。 -  - 外部调用目标fun()方法时,使用代理类的方法,而不是原始的B类去操作对象,这样如果要对原始的fun()类添加权限控制,业务校验等,可以直接在代理类中添加业务代码,而不需要改变原有B类的核心功能代码。 Java中的代理分为**静态代理**和**动态代理**。静态代理比较简单,不够灵活,适用面比较窄,实际的应用中使用的基本都是动态代理。Java中的动态代理实现包括**JDK原生的动态代理**和**CGLIB动态代理**。JDK动态代理必须要有接口,而CGLIB可以不要接口。 ## 静态代理 > 静态代理的代码实现跟上面的图一摸一样 ### 代码 `IUserDao` **A** ```java package ProxyUtil.hx.insist; public interface IUserDao { int addUser(String name); void delUser(int id); int updateUser(String name); } ``` `UserDao` **B** ```java package ProxyUtil.hx.insist; public class UserDao implements IUserDao { @Override public int addUser(String name) { System.out.println("USER ADD..."); return 1; } @Override public void delUser(int id) { System.out.println("USER DEL..."); } @Override public int updateUser(String name) { System.out.println("USER UPDATE"); return 0; } } ``` `UserDaoStaticProxy` **代理类** ```java package ProxyUtil.hx.insist; /** * 静态代理 类 */ public class UserDaoStaticProxy implements IUserDao { private IUserDao userDao; public UserDaoStaticProxy(IUserDao userDao) { this.userDao = userDao; } //用户直接使用代理类,后续需要在基础功能上添加业务代码时,就可以在代理对象里面扩展功能,不用修改基础功能, @Override public int addUser(String name) { checkSecurity(); int i = userDao.addUser(name); if(i!=1) System.out.println("本次操作失败..."); return i; } @Override public void delUser(int id) { checkSecurity(); userDao.delUser(id); } @Override public int updateUser(String name) { checkSecurity(); int i = userDao.updateUser(name); if(i!=1) System.out.println("本次操作失败..."); return i; } //统一的安全检查 private void checkSecurity(){ System.out.println("Security check..."); } } ``` `Client` 客户端 ```java package ProxyUtil.hx.insist; /** * 静态代理 测试 */ public class Client { public static void main(String[] args) { UserDaoProxy userDaoProxy = new UserDaoProxy(new UserDao()); userDaoProxy.addUser("111"); userDaoProxy.delUser(1); userDaoProxy.updateUser("222"); } } //输出 /* Security check... USER ADD... Security check... USER DEL... Security check... USER UPDATE 本次操作失败... */ ``` ### 总结 静态代理简单易理解,但灵活性非常受限,**要代理哪个类**,**必须手动写一个目标接口的实现类**,**要代理多少个目标接口**,**就得写多少个代理类**。且静态代理中对于原生类(目标类)中的每个方法都要设置一种处理方案,原生类中有100个方法,代理类如果对这100个方法做的处理是一样的(例如在进入方法前校验参数),那么就要写100个代理方法,非常繁琐。动态代理比较灵活,只需要写一个代理类即可。 ## 动态代理 ### 代码 `IUserDao`和`UserDao`仍然使用之前的。 `UserDaoDynamicProxy` 代理类 ```java package ProxyUtil.hx.insist; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理 类 */ public class UserDaoDynamicProxy implements InvocationHandler { private Object target; //得到代理对象 public Object get(Object target){ this.target = target; //生成代理对象 Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return o; } /** * 动态代理,调用代理对象的方法时,其实都是调用的target目标对象的对应方法,所有额外的业务处理都在invoke()里面写,所以只能做统一的处理,不能对每个方法分别处理 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { checkSecurity(); Object invoke = method.invoke(target, args); return invoke; }//invoke()反射技术 //统一处理 private void checkSecurity(){ System.out.println("Security check..."); } } ``` `Client2` 客户端 ```java package ProxyUtil.hx.insist; /** * 动态代理测试 */ public class Client2 { public static void main(String[] args) { UserDaoDynamicProxy userDaoDynamicProxy = new UserDaoDynamicProxy(); //得到代理对象 IUserDao userDao = (IUserDao) userDaoDynamicProxy.get(new UserDao()); //使用代理对象进行操作 userDao.addUser("111"); userDao.delUser(1); userDao.updateUser("222"); } } /* Security check... USER ADD... Security check... USER DEL... Security check... USER UPDATE */ ``` **我们也可以新增一个基础功能类** `ICropDao` **A** ```java package ProxyUtil.hx.insist; public interface ICropDao { void sow(); void harvest(); } ``` `CropDao` **B** ```java package ProxyUtil.hx.insist; public class CropDao implements ICropDao { @Override public void sow() { System.out.println("Crop sow..."); } @Override public void harvest() { System.out.println("Crop harvest..."); } } ``` 动态代理类不变,客户端测试: `Client2` ```java package ProxyUtil.hx.insist; /** * 动态代理测试 */ public class Client2 { public static void main(String[] args) { UserDaoDynamicProxy userDaoDynamicProxy = new UserDaoDynamicProxy(); //得到代理对象 ICropDao cropDao = (ICropDao) userDaoDynamicProxy.get(new CropDao()); //使用代理对象进行操作 cropDao.sow(); cropDao.harvest(); } } //输出 /* Security check... Crop sow... Security check... Crop harvest... */ ``` ### 总结 动态代理非常简便,且代码量少,只需要写一个代理类,即可代理不同的目标类(因为代理类内部是Object target),但是这种情况下也有缺点:无法为每个类自定义代理需求,所有的代理方式都是统一的。(所以代理才会被应用到统一的处理:事务,日志等) JAVA的动态代理必须要有接口,如果没有接口,就必须使用CGLIB的动态代理了。 ### 动态代理做拦截器 `Shopping` ```java package ProxyUtil.InterceptorDemo; public interface Shopping { void buy(); } ``` `Client` 基础功能客户端 ```java package ProxyUtil.InterceptorDemo; public class Client implements Shopping { @Override public void buy() { System.out.println("我想买这件商品..."); } } ``` `Interceptor` 拦截器接口 ```java package ProxyUtil.InterceptorDemo; import java.lang.reflect.Method; public interface Interceptor { boolean before(Object proxy, Object target, Method method, Object[] args); void around(Object proxy, Object target, Method method, Object[] args); void after(Object proxy, Object target, Method method, Object[] args); } ``` `MyInterceptor` 自定义拦截器 ```java package ProxyUtil.InterceptorDemo; import java.lang.reflect.Method; public class MyInterceptor implements Interceptor { @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("before"); return true; } @Override public void around(Object proxy, Object target, Method method, Object[] args) { System.out.println("around"); } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("after"); } } ``` `InterceptorProxy` 拦截器代理类,将自定义拦截器和原对象建立关系,实现得到代理对象后,执行代理对象的方法就是执行加了拦截器具体实现的原方法的效果。 ```java package ProxyUtil.InterceptorDemo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class InterceptorProxy implements InvocationHandler { private Object target = null; Interceptor interceptor = null; /** * 构造方法传入(自定义)拦截器 * @param interceptor 拦截器 */ public InterceptorProxy(Interceptor interceptor) { this.interceptor = interceptor; } /** * 通过原对象,得到代理对象 * @param target 目标对象 * @return */ public Object bind(Object target){ this.target = target; Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return o; } /** * 代理方法逻辑,原对象的所有方法都会被此方法代理。换言之执行想要执行原对象的所有方法都是执行的此方法,此方法将原对象的方法包装后执行 * @param proxy 代理对象 * @param method 执行方法 * @param args 执行方法参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(interceptor==null){ method.invoke(target,args);//没有传入拦截器,就直接执行原方法 } Object result = null; if(interceptor.before(proxy,target,method,args)){//拦截器的before()返回true result = method.invoke(target, args); }else {//拦截器的before()返回false interceptor.around(proxy,target,method,args); } interceptor.after(proxy,target,method,args); return result;//当拦截器的before()返回true时,将结果返回 } } ``` `InterceptorTest` 拦截器测试 ```java package ProxyUtil.InterceptorDemo; public class InterceptorTest { public static void main(String[] args) { //将自定义拦截器配入拦截器代理类中 InterceptorProxy interceptor = new InterceptorProxy(new MyInterceptor()); //通过原对象得到代理对象 Shopping proxyO = (Shopping) interceptor.bind(new Client()); //执行代理对象的方法,就是执行拦截器的拦截方法和原方法 proxyO.buy(); } } //输出 /* before 我想买这件商品... after */ ``` 拦截器原理:  ## CGLIB动态代理 cglib代理需要引入cglib的依赖: ```xml <!--cglib及其依赖包--> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>7.1</version> </dependency> ``` ### 代码 `AirlineCompany` 目标类 ```java package ProxyUtil; /** * 航空公司 原生类 */ public class AirlineCompany { /** * 卖票 * @return true表示出票成功, false表示出票失败 */ public boolean saleTickets() { System.out.println("航空公司出票"); return true; } } ``` `PlaneTicketAgent` 代理类 ```java package ProxyUtil; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 机票代理商 代理类 */ public class PlaneTicketAgent implements MethodInterceptor { /** * 拦截被代理对象中除final之外的所有方法. * 所有生成的代理方法(除过final修饰的方法)都调用此方法而不是原始方法, * 原始方法可以由使用方法对象的普通反射调用, 或者使用MethodProxy对象调用(这种方式更快). * 之所以final修饰的方法不能被cglib代理, 是因为cglib采用extends被代理对象的方式来重写相应的方法, * 以此来拦截目标方法并在目标方法前后加上自己的业务处理代码. 而final修饰的方式是不能被重写的. * @param proxyObj 代理对象 * @param method 被拦截的方法 * @param param 方法参数 * @param methodProxy 代理方法 * @return 与代理方法的签名兼容的返回值 */ @Override public Object intercept(Object proxyObj, Method method, Object[] param, MethodProxy methodProxy) throws Throwable { // 可以在此处添加前置业务处理代码 System.out.println("由机票代理商来卖票"); Object result = methodProxy.invokeSuper(proxyObj, param); // 可以在此处添加后置业务处理代码 if ((boolean) result) { System.out.println("已顺利出票"); } else { System.out.println("出票遇到一点麻烦"); } return result; } } ``` `CglibProxyTest` 代理类 测试 ```java package ProxyUtil; import net.sf.cglib.proxy.Enhancer; /** * cglib做动态代理 测试 */ public class CglibProxyTest { public static void main(String[] args) { //创建Enhancer实例 cglib的一个generator Enhancer enhancer = new Enhancer(); //设置原生类 enhancer.setSuperclass(AirlineCompany.class); //设置代理类 enhancer.setCallback(new PlaneTicketAgent()); //生成代理类的实例 AirlineCompany airlineCompany = (AirlineCompany) enhancer.create(); boolean result = airlineCompany.saleTickets(); if(result){ System.out.println("出票成功"); }else { System.out.println("出票失败"); } } } //输出 /* 由机票代理商来卖票 航空公司出票 已顺利出票 出票成功 */ ``` cglib结构图:  - 在cglib中,我们自己写的代理类其实是“拦截器”,Enhancer才是真正的代理类,用来将所需要加的业务处理(拦截器处理)和原生类方法(基础功能客户端)联合在一起。 --END--
发表评论