代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。
interface Subject { void request(); } class RealSubject implements Subject { public void request(){ System.out.println("RealSubject"); } } class Proxy implements Subject { private RealSubject realSubject; public Proxy(RealSubject realSubject){ this.subject = subject; } public void request(){ System.out.println("begin"); subject.request(); System.out.println("end"); } } public class ProxyTest { public static void main(String args[]) { RealSubject subject = new RealSubject(); Proxy p = new Proxy(subject); p.request(); } }
静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多;同时,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理技术包括JDK、CGLIB、Javassist、ASM等。
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
动态代理必须分为两个部分:
1、代理对象和真实对象建立代理关系。
2、实现代理对象的代理逻辑方法。
JDK动态代理是java.lang.reflect.*包提供的方式,必须借助一个接口才能产生代理对象。
定义接口和实现类:
interface Subject { void request(); } class RealSubject implements Subject { public void request(){ System.out.println("RealSubject"); } }
JDK动态代理绑定和代理逻辑实现:
package com.wlz; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author zhiqiang zhang * @create 2019-02-13-09:26 **/ public class JdkProxyExample implements InvocationHandler { //真实对象 private Object target = null; /** * 建立代理对象与真实对象的代理关系,并返回代理对象。这里使用bind方法完成, * @param target 真实对象 * @return 代理对象 */ public Object bind(Object target){ //用类的属性target保存了真实对象 this.target=target; //建立代理对象,newProxyInstance方法包含三个参数; // 1、类加载器,这里使用target本身的类加载器; // 2、指定生成的动态代理对象的接口,这里放在target实现的接口下。代理对象就可以这样声明:HelloWorld proxy = xxxxxx; // 3、定义实现方法逻辑的代理类,this表示当前对象,他必须实现InvocationHandler接口的invoke方法,他就是代理逻辑方法的现实方法。 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); } /** * 代理方法逻辑 * @param proxy 代理对象,就是bind方法生成的对象 * @param method 当前调度方法 * @param args 当前方法参数 * @return 代理结果返回 * @throws Throwable 异常 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入代理逻辑方法"); System.out.println("在调度真实对象之前的服务"); Object object=method.invoke(target,args);//相当于调用sayHelloWorld方法,通过反射实现 System.out.println("在调度真实对象之后的服务"); return object; } }
测试:
public static void TestJdkProxy(){ JdkProxyExample jdkProxyExample=new JdkProxyExample(); //绑定关系,因为挂在接口Subject下,所以声明代理对象RealSubject proxy Subject proxy=(Subject)jdkProxyExample.bind(new RealSubject()); //注意,此时HelloWorld对象已经是一个代理对象,它会进入代理的逻辑方法invoke里 proxy.request(); }
运行结果:
进入代理逻辑方法 在调度真实对象之前的服务 Hello World! 在调度真实对象之后的服务
优点:相比于静态代理,动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
缺点:必须提供接口才能使用
相较于JDK动态代理,不需要提供接口,只要一个非抽象类就能实现动态代理。
新建非抽象类:
package com.wlz; /** * @author zhiqiang zhang * @create 2019-02-13-10:41 **/ public class User { public void sayHello(String name){ System.out.println("hello "+name); } }
cglib动态代理:
package com.wlz; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-13-10:57 **/ public class CglibProxyExample implements MethodInterceptor { /** * 生成CGLIB代理对象 * @param cls Class类 * @return Class类的CGLIB代理对象 */ public Object getProxy(Class cls){ //CGLIB enhancer增强类对象 Enhancer enhancer=new Enhancer(); //设置增强类型 enhancer.setSuperclass(cls); //定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法 enhancer.setCallback(this); //生成并返回代理对象 return enhancer.create(); } /** * 代理逻辑方法 * @param object 代理对象 * @param method 方法 * @param objects 方法参数 * @param methodProxy 方法处理 * @return 代理逻辑返回 * @throws Throwable 抛出异常 */ @Override public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("调用真实对象前"); //CGLIB反射调用真实对象 Object result=methodProxy.invokeSuper(object,objects); System.out.println("调用真实对象后"); return result; } }
测试:
public static void TestCglibProxy(){ CglibProxyExample cglibProxyExample=new CglibProxyExample(); User user=(User) cglibProxyExample.getProxy(User.class); user.sayHello("张三"); }
输出结果:
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/Users/zhangzhiqiang/.m2/repository/cglib/cglib/3.2.10/cglib-3.2.10.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release 调用真实对象前 hello 张三 调用真实对象后
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用CGLIB代理
程学设计者通常将动态代理设计为拦截器接口供开发者使用,提供拦截器接口的方法、含义和作用即可。
用JDK动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口Interceptor
package com.wlz; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-13-16:59 **/ public interface Interceptor { /** * * @param proxy 代理对象 * @param target 真实对象 * @param method 方法 * @param args 方法参数 * @return */ 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
package com.wlz; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-13-17:07 **/ public class MyInterceptor implements Interceptor { @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.err.println("反射前的逻辑"); return false;//不反射被代理对象原有方法 } @Override public void around(Object proxy, Object target, Method method, Object[] args) { System.err.println("反射方法后的逻辑"); } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.err.println("取代了被代理对象的方法"); } }
在JDK动态代理中使用拦截器
package com.wlz; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author zhiqiang zhang * @create 2019-02-13-17:16 **/ public class InterceptorJdkProxy implements InvocationHandler { private Object target;//真实对象 private String interceptorClass=null;//拦截器权限定名 //构造器 public InterceptorJdkProxy(Object target, String interceptorClass) { this.target = target; this.interceptorClass = interceptorClass; } /** * 绑定委托对象并返回一个代理占位 * @param target 真实对象 * @param interceptorClass * @return 代理对象 */ public static Object bind(Object target,String interceptorClass){ //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InterceptorJdkProxy(target,interceptorClass)); } /** * 通过代理对象调用方法,首先进入这个方法 * @param proxy 代理对象 * @param method 方法,被调用方法 * @param args 方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果设置拦截器则直接反射原有方法 if(interceptorClass==null){ return method.invoke(target,args); } Object result=null; //通过反射生成拦截器 Interceptor interceptor=(Interceptor)Class.forName(interceptorClass).newInstance(); //调用前置方法 if(interceptor.before(proxy,target,method,args)){ //反射原有方法 result= method.invoke(target,args); }else{ //返回false,执行around方法 interceptor.around(proxy,target,method,args); } //调用后置方法 interceptor.after(proxy,target,method,args); return result; } }
开发者只要知道拦截器的作用就可以编写拦截器,编写完之后设置拦截器
设计者完成动态代理的逻辑
拦截器进一步简化动态代理的使用方法,让程序变得更简单。
测试
public static void TsetInterceptor(){ Subject subject=(Subject)InterceptorJdkProxy.bind(new RealSubject(),"com.wlz.MyInterceptor"); subject.request(); }
运行结果:
反射前的逻辑 反射方法后的逻辑 取代了被代理对象的方法
参考:https://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html
当一个对象在一条链上被多个拦截器拦截处理(拦截器也可以选择不拦截处理它)时,我们把这样的设计模式称为责任链模式,它用于一个对象在多个角色中传递的场景。
定义多个拦截器:
package com.wlz; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-14-10:10 **/ public class Interceptor1 implements Interceptor { @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器1的before方法"); return true; } @Override public void around(Object proxy, Object target, Method method, Object[] args) { } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器1的after方法"); } } package com.wlz; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-14-10:12 **/ public class Interceptor2 implements Interceptor { @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器2的before方法"); return true; } @Override public void around(Object proxy, Object target, Method method, Object[] args) { } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器2的after方法"); } } package com.wlz; import java.lang.reflect.Method; /** * @author zhiqiang zhang * @create 2019-02-14-10:16 **/ public class Interceptor3 implements Interceptor { @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器3的before方法"); return true; } @Override public void around(Object proxy, Object target, Method method, Object[] args) { } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器3的after方法"); } }
测试:
public static void TsetResponsibilityList(){ Subject subject1=(Subject)InterceptorJdkProxy.bind(new RealSubject(),"com.wlz.Interceptor1"); Subject subject2=(Subject)InterceptorJdkProxy.bind(subject1,"com.wlz.Interceptor2"); Subject subject3=(Subject)InterceptorJdkProxy.bind(subject2,"com.wlz.Interceptor3"); subject3.request(); }
运行结果:
拦截器3的before方法 拦截器2的before方法 拦截器1的before方法 卖票 拦截器1的after方法 拦截器2的after方法 拦截器3的after方法
责任链的优点在于我们可以在传递链上加入新的拦截器,增加拦截逻辑;缺点是增加代理和反射,而代理和反射的性能不高。