动态代理设计模式——我的理解
动态代理是指在运行时,动态生成代理类。即,代理类的字节码将在运行时生成并载入当前的ClassLoader。
JDK的动态代理
我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器(实现 InvocationHandler 接口,通过统一的invoke方法,对原函数的进行处理)就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。
java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心
InvocationHandler接口
- 每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用
Proxy类
- Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法
注意:Jdk的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用jdk代理
- Jdk的动态代理模式
- 获取被代理的对象的所有接口列表
- 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX;
- 根据需要实现的接口信息,在代码中动态创建该Proxy类的字节码;
- 将对应的字节码转换为对于的class对象;
- 创建InvocationHandler实例handler,用来处理Proxy所有方法的调用;
- Proxy的class对象以创建的handler对象为参数,实例化一个proxy对象;
- 实例源码
- 动态代理类
// 1. 生成 动态代理对象
// 2. 指定 代理对象运行目标对象方法时需要完成的 具体任务
// 注:需实现InvocationHandler接口 = 调用处理器 接口
// 所以称为 调用处理器类
public class DynamicProxy implements InvocationHandler {
// 声明被代理的对象
// 作用:绑定关系,即关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke()
private Object ProxyObject;
/**
* 根据被代理对象,生成代理对象Proxy
*/
public Object newProxyInstance(Object ProxyObject){
// 被代理的对象
this.ProxyObject = ProxyObject;
// Proxy类 = 动态代理类的主类
// Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
// 参数说明:
// ClassLoader loader:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// Class<?>[] interfaces:指定目标对象的实现接口,即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
// InvocationHandler:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象
return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),ProxyObject.getClass().getInterfaces(),this);
}
// 复写InvocationHandler接口的invoke()
// 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 参数说明:
// Object proxy:动态代理对象(即哪个动态代理对象调用了method()
// Method method:目标对象被调用的方法
// Object[] args:指定被调用方法的参数
Object result = null;
// 通过Java反射机制调用目标对象方法,得到返回值
result = method.invoke(ProxyObject, args);
// 返回结果
return result;
}
}
CGLIB的动态代理
CGLIB是针对类来实现的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
- CGLIB创建某个类A的动态代理类的模式
- 查找A上的所有非final的public类型的方法定义
- 将这些方法的定义转换成字节码
- 将组成的字节码转换成相应的代理的class对象
- 实现MethodInterceptor接口,用来处理对代理类上所有方法的请求(这个接口和Jdk动态代理InvocationHandler的功能和角色是一样的)
- 实例和源码
- 被代理对象RealSubject
public class RealSubjectCglib{
// 操作
public String operate(){
return "RealSubjectCglib";
}
}
- 代理类
public class CglibProxy implements MethodInterceptor{
// 被代理对象
private Object target;
public Object getInstance(Object target){
this.target = target;
//Cglib中的加强器,用来创建动态代理
Enhancer enhancer = new Enhancer();
//设置要创建动态代理的类
enhancer.setSuperclass(this.target.getClass());
//设置回调,这里相当于是对于代理类上所有方法的调用,都会调用Callback,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(this);
Object obj = enhancer.create();
return obj;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable{
Object object = proxy.invokeSuper(obj, args);
return object;
}
}
- 测试
public Test{
public static void main(String[] args){
// 创建代理对象
CglibProxy proxy = new CglibProxy();
RealSubjectCglib cglib = (RealSubjectCglib)proxy.getInstance(new RealSubjectCglib());
// 代理操作
cglib.operate();
}
}
- 注意:
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。