代理模式
代理模式的意图是通过一个接口或者占位符来控制对该对象的访问
代理对象通常拥有一个和真实对象相同的接口,通过控制访问将请求合理的转发给底层真实的对象
动态代理(需要代理的类实现了接口)
通过反射类Proxy以及InvocationHandler回调接口实现的
动态代理是通过代理对象包装实际对象,通过代理对象来拦截对实际对象的请求,然后用代理再转发给实际对象,并且允许你在拦截调用之前或者之后增加自己的代码
创建动态代理
(1)获得实际对象实现的全部接口.obj是实际对象
Class[] classes=obj.getClass().getInterfaces();
(2)获得实际对象所属类的类加载器
ClassLoader loader=obj.getClass().getClassLoader();
(3)创建代理对象自身
这个对象所属类必须实现java.lang.reflect包的InvocationHandler接口。这个接口中定义了一个invoke方法
如下:通过实现InvocationHandler接口,重写invoke方法,result=method.invoke(obj, args)这一行是通过反射把目标调用转发给实际对象,在转发前后增加代码,实现一个简单的日志功能,这样代理类就创建好了
package xidian.lili.edu.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ImpatientProxy implements InvocationHandler{
private Object obj;
public ImpatientProxy(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
Object result;
long t1=System.currentTimeMillis();
result=method.invoke(obj, args);
long t2=System.currentTimeMillis();
System.out.println("It takes "+(t2-t1)+" millis to invoke "+method.getName());
return result;
}
}
那么要使用代理类ImpatientProxy的对象,就要用到java.lang.reflect包的Proxy类,这个类需要一组接口,一个类加载器和ImpatientProxy实例对象,所以我们可以在代理类中增加如下方法,这个newInstance的静态方法为我们创建一个静态代理对象,这个方法返回的对象会实现被包装对象的所有接口,可以将其转化为任意类型的对象
package xidian.lili.edu.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ImpatientProxy implements InvocationHandler{
private Object obj;
public ImpatientProxy(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
long t1=System.currentTimeMillis();
result=method.invoke(obj, args);
long t2=System.currentTimeMillis();
System.out.println("It takes "+(t2-t1)+" millis to invoke "+method.getName());
return result;
}
public static Object newInstance(Object obj){
ClassLoader loader=obj.getClass().getClassLoader();
Class[] classes=obj.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, classes, new ImpatientProxy(obj));
}
}
一旦你写了一个动态代理类,如上,只要对象实现了你想拦截的方法所属的接口,就可以使用代理类去包装该对象。
package xidian.lili.edu.proxy;
import java.util.HashSet;
import java.util.Set;
public class ShowDynamicProxy {
public static void main(String[] args) {
Set s=new HashSet();
//用动态代理包装s对象
s=(Set) ImpatientProxy.newInstance(s);
s.add("hello world");
s.isEmpty();
}
}
在方法的执行前后对调用进行拦截,并创建自己的思想属于面向切片编程(AOP)
没有实现接口(cglib)
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
GLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类