动态代理的原理
代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
实现原理 ?? ?jdk动态代理 ?? ??? ?主要通过Proxy.newProxyInstance()和InvocationHandler这两个类和方法实现 ?? ??? ?实现过程 ?? ??? ??? ?创建代理类proxy实现Invocation接口,重写invoke()方法 ?? ??? ??? ??? ?调用被代理类方法时默认调用此方法 ?? ??? ??? ?将被代理类作为构造函数的参数传入代理类proxy ?? ??? ??? ?调用Proxy.newProxyInsatnce(classloader,interfaces,handler)方法生成代理类 ?? ??? ??? ??? ?生成的代理类 ?? ??? ??? ??? ??? ?$Proxy0 extends Proxy implements Person ?? ??? ??? ??? ??? ?类型为$Proxy0 ?? ??? ??? ??? ??? ?因为已经继承了Proxy,所以java动态代理只能对接口进行代理 ?? ??? ??? ??? ?代理对象会实现用户提供的这组接口,因此可以将这个代理对象强制类型转化为这组接口中的任意一个 ?? ??? ??? ??? ?通过反射生成对象 ?? ??? ??? ?总结: 代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。 ?? ?cglib ?? ??? ?生成对象类型为Enhancer ?? ??? ?实现原理类似于 jdk 动态代理,只是他在运行期间生成的代理对象是针 对目标类扩展的子类 ?? ?静态代理 ?? ??? ?缺点 ?? ??? ??? ?如果要代理一个接口的多个实现的话需要定义不同的代理类 ?? ??? ??? ?代理类 和 被代理类 必须实现同样的接口,万一接口有变动,代理、被代理类都得修改 ?? ??? ?在编译的时候就直接生成代理类 ?? ?JDK动态代理和cglib的对比 ?? ??? ?CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高 ?? ??? ??? ?1.6和1.7的时候,CGLib更快 ?? ??? ??? ?1.8的时候,jdk更快 ?? ??? ?CGLib在创建对象的时候所花费的时间却比JDK动态代理多 ?? ??? ?singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反之,则适合用JDK动态代理 ?? ??? ?JDK动态代理是面向接口的,CGLib动态代理是通过字节码底层继承代理类来实现(如果被代理类被final关键字所修饰,那么会失败) ?? ??? ?JDK生成的代理类类型是Proxy(因为继承的是Proxy),CGLIB生成的代理类类型是Enhancer类型 ?? ??? ?如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制); 如果要被代理的对象不是实现类,那么Spring会强制使用CGLib来实现动态代理。
public class LoggingProxy {
//被代理的对象
private Object target;
public LoggingProxy(Object target){
this.target=target;
}
public Object getProxy(){
//获取被代理对象的类加载器
ClassLoader classLoader=target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
Object proxy= Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
/**
* 参数的说明
* invoke方法说明就是要执行的扩展的攻难
* proxy:传入的被代理的对象
* method:调用的方法
* args:调用方法时候传入的参数
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println("====Logging: the method" +name+"begin with " + Arrays.toString(args));
//执行目标的方法即将方法的调用转到被代理的对象上
Object result = method.invoke(target, args);
System.out.println("====Logging: the method"+name+"returns"+result);
return result;
}
});
return proxy;
}
}
|