首先写下查找的对动态代理的定义:
动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。
我的理解:
动态代理旨在java运行时(涉及到的知识:编译时、运行时、构建时)动态加载目标类,在实现目标类方法的基础上,使用自己的方法(jdk用反射,cjlib则利用ASM机制继承目标类)将自己需要的增强代码插入其中,以达到不修改原代码,只新添自己的代码而增强原方法功能的目的,从查到的方法可以知道,增强的代理类已经不是目标类,是新的类,新的字节码,目标类未发生变动.
首先写下涉及到的类:
public interface Person {
public void speak();
}
public class Joker{//是否继承接口根据动态代理实现方式不同而不同,jdk的需要,cjlib不需要
// @Override
public void speak() {
System.out.println("JokerSpeaking");
}
}
然后是调用增强后方法的代码
public static void main(String []args) throws Exception {
Joker j = (Joker)getProxy(new Joker());
j.speak();
}
最后就是重头戏,两种动态代理的实现方法:
用jdk实现的动态代理:
private static Object getJDKProxy(final Object target) throws Exception {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),//获取类加载器
target.getClass().getInterfaces(),//获取目标类所实现的接口,这也是jsk动态代理目标类需要实现接口的原因
(proxy1, method, args) -> {
System.out.println(method.getName() + "proxyEnhance");
Object result = method.invoke(target, args);//调用原方法
System.out.println(method.getName() + "proxyEnhanceEnd");
return result;
}
);
return proxy;
}
用cjlib实现的动态代理:
private static Object getCjlibProxy(final Object target) throws Exception {
Enhancer e = new Enhancer();
e.setSuperclass(target.getClass());
e.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("前");
Object invoke = method.invoke(target, objects);
System.out.println("后");
return invoke;
});
return e.create();
}
以前几次学动态代理,其核心代码虽然不变,但是看上去的形式却千差万别,这次仔细观察后发现,仅以上涉及到的代码就可以实现增强目标方法的目的,以前看上去差很多问题是出在代理类上:无论是jdk的InvocationHandler还是cjlib的MethodInterceptor(jdk中因为lamda表达式的关系省略了InvocationHandler的出现)都是可以自己继承并进行增强的,最后只需将自己实现的类替换其父类的位置仍然可以实现功能.
以下是大佬的两种代理方式的对比说明,链接:https://www.cnblogs.com/whirly/p/10154887.html
JDK 动态代理:
为了解决静态代理中,生成大量的代理类造成的冗余;
JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现,
jdk的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象
jdk动态代理之所以只能代理接口是因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口
优点:解决了静态代理中冗余的代理实现类问题。
缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
CGLIB 代理:
由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK方式解决不了;
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。
实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。
但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。
同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。
优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。
缺点:技术实现相对难理解些。