静态代理
代理在开发中非常常见,可以指程序的设计模式也可以指服务器的工作模式。通俗的将,代理就是不能直接访问某个事物时,通过访问一个中间事物,再由中间事物访问目标事物。这似乎是多此一举,为什么要这么做呢?原因也各不相同,可能是客观原因不得不这么做,例如国内访问youtube服务器就必须通过代理服务器;也可能是设计时不想两个事物之间的耦合关系过强,例如Java中的线程:
class MyTask implements Runnable { @Override public void run() { //二者都实现了相同的接口 ... } } class Thread implements Runnable { .... Runnable r; //代理角色持有真实角色的引用 public Thread(Runnable r) { this.r = r; } ..... @Override public void run() { //二者都实现了相同的接口 ... r.run(); } } new Thread(new MyTask()); //将一个被代理角色(真实角色)传递给代理角色持有
Thread类只是一个代理类,它的run()方法实际是运行了被代理类的run()方法。静态代理的几个条件:
1. 代理角色持有真实角色的引用。
2. 二者都实现了相同的接口,即二者有相同的方法。这样,代理角色在方法中调用真实角色的同名方法即可完成静态代理。
动态代理 ——JDK代理
为什么需要动态代理呢?因为静态代理比较局限,需要实现真实角色的接口才能进行代理,即需要一个对一个编写代理类。动态代理下则有一个专门的类使用反射技术根据真实角色生成代理角色,为此我们需要在程序跑起来后将真实角色的类型,类加载器,实现的接口信息给出。由于代理对象是在运行时生成的,因此这种代理模式也叫动态代理。其中JDK代理是指利用JDK包中的工具类实现,代理角色和真实角色必须实现相同的接口。
java.lang.reflect.Proxy类的生成代理角色的方法如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException //参数依次是:真实角色的类加载器、真实角色的接口、实现了InvocationHandler接口的类实例
java.lang.reflect.InvocationHandler接口如下:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } //参数分别表示:代理角色、方法名、方法的参数列表。这个方法即是代理角色执行真实角色方法的地方,可以直接调用真实角色的同名方法,也可自己添加一些逻辑后调用
使用例子:
public class Handler implements InvocationHandler { private Object realObject; //真实角色
public static Object newProxyInstance(Object realObject){ this.realObject = realObject; return Proxy.newProxyInstance(realObject.getClass().getClassLoader(), realObject.getClass().getInterfaces(),this); //this就是实现了 InvocationHandler 中invoke方法的本对象 } @Override //实现 InvocationHandler 接口的invoke方法,决定了该如何执行真实角色的方法 public Object invoke(Object proxy, Method method, Object[] args) { method.invoke(realObject, args); //规定了代理角色执行真实角色方法的逻辑,这里直接调用同名方法(通过反射的方法) } }
然后:
Object proxy = Handler.newProxyInstance(realObject);
只需传入一个需要被代理的真实角色,即可返回一个和此真实角色合适的代理角色。然后我们就可以通过proxy这个代理角色来执行真实角色的方法了。
动态代理——Cglib代理
Cglib是一组jar包,是另一种动态代理的策略,用于弥补JDK动态代理的不足——无法对没有实现某接口的类进行代理。那么,Cglib为什么就能对没有实现任何接口的类进行代理呢?因为它采取了不同的策略,没有通过接口找真实角色有哪些方法,而是通过继承真实角色在内存中生成一个子类,然后子类对这个真实角色进行一些功能的扩展。
因为Cglib的原理,有以下一些注意点:
(1)真实角色的类不能是final的,因为这样就无法被继承了。
(2)方法有final或static修饰也不行,因为无法被重写。
(3)方法如果是private的也无法被代理,因为这个方法子类不可见了。
Cglib底层是通过ASM框架修改字节码生成子类文件,当操作字节码不当生成大量新类时,有可能导致JVM方法区OOM。因此不要直接使用ASM,除非对字节码等非常熟悉。
Cglib动态代理在spring的aspect和interceptor(二者都是aop思想的一种实现)中得到应用。