动态代理:http://www.cnblogs.com/MOBIN/p/5597215.html
远程代理&虚拟代理&缓冲代理:http://blog.csdn.net/will130/article/details/50729535
动态代理机制详解(JDK CGLIB,Javassist,ASM):http://blog.csdn.net/luanlouis/article/details/24589193
动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成)
CGLIB
RealSubject:真实角色。需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此
Proxy:代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作将统一的流程控制都放到代理角色中处理!
装饰模式vs代理模式
装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器
interface IMusicBox { public void sing(); }
定义具体目标类
class MusicBox implements IMusicBox { public void sing() { System.out.println("im singing"); } }
定义代理
public class StaticMusicBoxProxy implements IMusicBox { // 以真实角色作为代理角色的属性 private IMusicBox box; public StaticMusicBoxProxy(IMusicBox box) { this.box = box; } public void sing() { before(); box.sing(); after(); } public void before() { System.out.println("before sing do something"); } public void after() { System.out.println("after sing do something"); } }
客户端调用
MusicBox box = new MusicBox(); IMusicBox bo = (IMusicBox) new StaticMusicBoxProxy(box); bo.sing();
输出
一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点
JDK自带的动态代理(只能针对实现了接口的类生成代理)
获取RealSubject上的所有接口列表
确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXX
根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码
将对应的字节码转换为对于的class对象
创建InvocationHandler实例handler,用来处理Proxy所有方法的调用
Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象
public interface IHello{ void sayHello(); void sayBye(); }
定义目标对象
static class Hello implements IHello{ public void sayHello() { System.out.println("Hello world!!"); } @Override public void sayBye() { System.out.println("Hello bye!!"); } }
定义InvocationHandler
class MyInvocationHandler implements InvocationHandler{ //目标对象 private Object target; public MyInvocationHandler(Object target){ this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("------插入前置通知代码-------------"); //执行相应的目标方法 Object rs = method.invoke(target,args); System.out.println("------插入后置处理代码-------------"); return rs; } }
调用
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { MyInvocationHandler handler=new MyInvocationHandler(new Hello()); IHello iHello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), new Class<?>[]{IHello.class},handler); iHello.sayHello(); }
输出
动态代理实现原理
上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤(红色标准部分):
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
其实,我们最应该关注的是 Class<?> cl = getProxyClass0(loader, intfs);这句,这里产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键,由于是动态生成的类文件,我这里不具体进入分析如何产生的这个类文件,只需要知道这个类文件时缓存在java虚拟机中的,我们可以通过下面的方法将其打印到文件里面,一睹真容:
public static void printProxyClass(){ byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", IHello.class.getInterfaces()); String path = "E:/proxy.class"; try( FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); } catch (Exception e) { } }
jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件临时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的该类实例。通过对这个生成的代理类源码的查看,我们很容易能看出,实际调用的是MyInvocationHandler的invoker方法。
我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能
查找A上的所有非final的public类型的方法定义
将这些方法的定义转换成字节码
将组成的字节码转换成相应的代理的class对象
实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和Jdk动态代理InvocationHandler的功能和角色是一样的)
class Hello { public void sayHello() { System.out.println("Hello world!!"); } public void sayBye() { System.out.println("Hello bye!!"); } }
定义通用proxy
public class CglibProxy implements MethodInterceptor { private Enhancer enhance = new Enhancer(); public Object getProxy(Class<?> clazz) { enhance.setSuperclass(clazz); enhance.setCallback(this); return enhance.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before do something"); proxy.invoke(obj, args); System.out.println("after do something"); return null; } }
调用
public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Hello h = (Hello) proxy.getProxy(Hello.class); h.sayBye(); }