标签:
1. 代理模式
一句话总结:为其他对象提供一种代理以控制对这个对象的访问。
千篇一律的介绍:代理模式是常用的java设计模式,他的特征是代理类与委托类(或目标类)有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
2. 动态代理: JDK动态代理和CGLIB动态代理
2.1 JDK动态代理:
a. JDK动态代理是面向接口的,必须提供一个委托类和代理类都要实现的接口
b. JDK动态代理的实现主要使用java.lang.reflect包里的Proxy类和InvocationHandler接口。
实现:
接口:
1 public interface ProxyInterface { 2 public void proxyMethod(String name); 3 }
目标类:
1 public class TargetObject implements ProxyInterface { 2 @Override 3 public void proxyMethod(String name) { 4 System.out.println(this.getClass().getName() + " --->:" + name); 5 } 6 }
代理的最终操作类: 为通过反射机制实现的代理类提供方法的实现。实现InvocationHandler接口的invoke方法做代理类要做的事情
1 public class JDKInvocationHandler implements InvocationHandler { 2 private Object target; 3 4 public JDKInvocationHandler(Object target) { 5 this.target = target; 6 } 7 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //参数说明, proxy是生成的代理对象, method是代理的方法, args是方法接收的参数, 此代理类会代理目标类的所有方法 10 System.out.println("begin invoke...." + proxy.getClass().getName()); //目标类方法调用之前预处理 11 12 method.invoke(target, args); 13 14 System.out.println("end invoke..."); //目标类方法调用完成之后的后续处理 15 return null; 16 } 17 }
测试:
1 public class Main { 2 public static void main(String[] args){ 3 TargetObject target = new TargetObject(); 4 5 JDKInvocationHandler handler = new JDKInvocationHandler(target); //建立代理类和目标类之间的关联关系 6 7 ProxyInterface proxy = (ProxyInterface) Proxy.newProxyInstance( //通过反射机制创建和目标类实现相同接口的代理类 8 target.getClass().getClassLoader(), 9 target.getClass().getInterfaces(), 10 handler 11 ); 12 13 proxy.proxyMethod("this reflect method use jdk origin method!!!"); //调用代理类的代理方法 14 } 15 }
输出:
begin invoke....com.sun.proxy.$Proxy0 //目标类方法调用之前预处理的输出 com.mrlu.proxy.jdk.TargetObject --->:this reflect method use jdk origin method!!! //目标类提供的实际服务的输出 end invoke... //目标类方法调用之后处理的输出
总结: 通过输出可以看到, 实际调用的是com.sun.proxy.$Proxy0代理类的方法, 在代理类内部又调用了com.mrlu.proxy.jdk.TargetObject目标类的方法
注意:
1. JDKInvocationHandler类的invoke方法,是最终通过放射生成的代理类的所有方法的实现,所以调用反射类的所有方法都会有预处理和调用之后处理的逻辑
2. method.invoke(target, args); 是通过反射机制调用目标类的相同方法, 此处注意第一个参数代表要调用的方法所在的对象,一般都是目标对象。此处感觉和JS中的call和apply有点类似
若此处写成了代理对象:method.invoke(proxy, args); //proxy是代理对象
在执行的时候会无限递归下去, 因为通过调用代理对象的方法,会执行额外的处理逻辑,然后method.invoke(proxy, args);又调用代理对象的方法,又继续执行额外的处理逻辑, 然后不断重复执行,方法永远不能返回,无限的递归下去
2.2 CGLIB动态代理
a. 使用CGLIB动态代理不要求必须有接口,生成的代理对象是目标对象的子类对象,所以需要代理的方法不能是private或者final或者static的
b. 使用CGLIB动态代理需要有对cglib的jar包依赖
实现:
目标类:
1 public class TargetObject { 2 public void proxyMethod(String name) { 3 System.out.println(this.getClass().getName() + " --->:" + name); 4 } 5 private void privateMethod(){ 6 System.out.println("this is private method"); 7 } 8 public final void finalMethod(){ 9 System.out.println("this is final method"); 10 } 11 public static void staticMethod(){ 12 System.out.println("this is static method"); 13 } 14 }
代理的最终操作类:
1 public class InvokeHandler implements MethodInterceptor{ 2 private Enhancer enhancer = new Enhancer(); 3 4 public Object getProxy(Class clazz){ 5 enhancer.setSuperclass(clazz); //这两个set是必须的 6 enhancer.setCallback(this); 7 return enhancer.create(); 8 } 9 10 @Override 11 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 12 System.out.println("begin invoke... target object:" + o.getClass().getName()); 13 System.out.println("begin invoke... methodProxy object:" + methodProxy); 14 System.out.println("method info:" + method.getName()); 15 16 // method.invoke(o, objects); 17 methodProxy.invokeSuper(o, objects); 18 19 System.out.println("end invoke..."); 20 return null; 21 } 22 }
测试:
1 public class Main { 2 public static void main(String[] args){ 3 InvokeHandler invokeHandler = new InvokeHandler(); 4 5 TargetObject targetObject = (TargetObject) invokeHandler.getProxy(TargetObject.class); 6 7 targetObject.proxyMethod("this reflect method use cglib method!!!"); 8 targetObject.finalMethod(); 9 targetObject.staticMethod(); 10 }
输出:
begin invoke... target object:com.mrlu.proxy.cglib.TargetObject$$EnhancerByCGLIB$$afe67711 begin invoke... methodProxy object:org.springframework.cglib.proxy.MethodProxy@58e50f2c method info:proxyMethod com.mrlu.proxy.cglib.TargetObject$$EnhancerByCGLIB$$afe67711 --->:this reflect method use cglib method!!! end invoke...
this is final method
this is static method
总结:
1. 通过输出可以看出,最终调用的是com.mrlu.proxy.cglib.TargetObject的子类(也是代理类)com.mrlu.proxy.cglib.TargetObject$$EnhancerByCGLIB$$afe67711的方法
2. private,final和static修饰的方法不能被代理
注意:
1. CGLIB是通过实现目标类的子类来实现代理,不需要定义接口
2. 生成代理对象使用最多的是通过Enhancer和继承了Callback接口的MethodInterceptor接口来生成代理对象,设置callback对象的作用是当调用代理对象方法的时候会交给callback对象的来处理
3. 创建子类对象是通过使用Enhancer类的对象,通过设置enhancer.setSuperClass(Class class)和enhancer.setCallback(Callback callback)来创建代理对象
解释MethodInterceptor接口的intercept方法:
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
参数说明: Object var1代表的是子类代理对象, Method var2代表的是要调用的方法反射对象, 第三个参数是传递给调用方法的参数,前三个参数和JDK的InvocationHandler接口的invoke方法中参数含义是一样的
第四个参数MethodProxy对象是cglib生成的用来代替method对象的,使用此对象会比jdk的method对象的效率要高
如果使用method对象来调用目标对象的方法: method.invoke(var1, var3),则会陷入无限递归循环中, 因为此时的目标对象是目标类的子代理类对象
MethodProxy类提供了两个invoke方法:
public Object invokeSuper(Object obj, Object[] args) throws Throwable;
public Object invoke(Object obj, Object[] args) throws Throwable;
注意此时应该使用invokeSuper()方法,顾名思义调用的是父类的方法,若使用invoke方法,则需要提供一个目标类对象,但我们只有目标类子类代理对象,所以会陷入无限递归循环中。
CGLIB所创建的动态代理对象的性能比JDK所创建的动态代理对象的性能高很多,但创建动态代理对象时比JDK创建动态代理对象要花费更长的时间。
后记: 补充一些反射知识
1. 要想获取类的私有属性或者私有方法,则需要使用TargetObject.class.getDeclaredFields()和TargetObject.class.getDeclaredMethods(), 然后调用field和method的setAccessible(true)方法;
2. 使用TargetObject.class.getFields()和TargetObject.class.getMethods();方法是拿不到private,protected,和deafault修饰的属性和方法的
3. 通过子类获取父类的私有方法,需使用SubObject.class.getSuperclass().getDeclaredMethods(); 直接使用SubObject.class.getDeclaredMethods();只能拿到子类的所有方法(属性类似)
标签:
原文地址:http://www.cnblogs.com/stefanking/p/5059325.html