JDK动态代理以及CGLIB动态代理
1、Jdk动态代理以及jdk动态代理类的class的真正面目
jdk动态代理是利用反射机制生成的一个实现代理接口InvocationHandler和目标类接口的匿名类,InvocationHandler其中有一个invoke方法,在调用真正的目标方法method.invoke(target,args)之前或者之后做代理操作。
代码:
代理类
public class JdkTargetProxy implements InvocationHandler {
private Object target;
public JdkTargetProxy(Object target){
this.target = target;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> tClass){
return (T) Proxy.newProxyInstance(tClass.getClassLoader(),
new Class[]{tClass},
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "jdk动态代理before……");
Object result = method.invoke(target, args);
System.out.println(method.getName() + "jdk动态代理after……");
return result;
}
}
测试类
/**
* 接口
**/
public interface Dao {
String getTest();
}
/**
* 目标类
**/
@Service
public class DaoImpl implements Dao{
@Override
public String getTest() {
System.out.println("from DaoImpl1");
return "from DaoImpl1";
}
}
/**
* 测试函数
*/
public class TestMain {
public static void main(String[] args) {
// 生成代理类的同时将class文件输出
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
JdkTargetProxy targetProxy = new JdkTargetProxy(new DaoImpl());
Dao proxy = targetProxy.getProxy(Dao.class);
proxy.getTest();
}
}
结果:
getTestjdk动态代理before……
from DaoImpl1
getTestjdk动态代理after……
生成的代理类
在其中,重写了目标方法的实现
super.h即为 InvocationHandler的实现,即执行JdkTargetProxy的invoke方法。
2、cglib动态代理以及cglib动态代理类的class的真正面目
cglib封装了asm开源框架,对代理对象类的class文件进行字节码操作,生成代理类。生成的代理类是目标类的子类,重写了目标类的方法。
实现cglib动态代理需要依赖两个jar包,cglib的jar包以及asm的jar包,使用maven引入cglib-nodep即可。
引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.3.0</version>
</dependency>
cglib代理类:
public class CglibTargetProxy implements MethodInterceptor {
public <T> T getProxy(Class<T> tClass){
// 字节码增强类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(tClass);
// 设置回调类,调用哪个类的intercept的方法
enhancer.setCallback(this);
return (T)enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(method.getName() + "cglib代理逻辑before……");
Object result = methodProxy.invokeSuper(object, args);
System.out.println(method.getName() + "cglib切面逻辑after……");
return result;
}
}
测试方法:
public static void main(String[] args) {
// 设置生成代理类的同时输出文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:/code");
CglibTargetProxy targetProxy = new CglibTargetProxy();
DaoImpl proxy = targetProxy.getProxy(DaoImpl.class);
proxy.getTest();
}
结果:
CGLIB debugging enabled, writing to 'D:/code'
getTestcglib代理逻辑before……
from DaoImpl1
getTestcglib切面逻辑after……
代理类class的内容:
继承自目标类,使用字节码提升工具重写目标方法,实现getTest()时指向生成代理类是设置的callBack的类的intercept方法;
public final String getTest() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$getTest$0$Method, CGLIB$emptyArgs, CGLIB$getTest$0$Proxy) : super.getTest();
}
3、jdk动态代理和cglib动态代理的区别
- jdk动态代理是利用反射机制实现目标接口实现代理,cglib是通过字节码操作生成接口的子类实现代理。
- jdk动态代理只能实现实现了接口的目标对象,如果对象没有实现接口则无法使用jdk动态代理,因为java是单一继承的,但可以实现多个接口,jdk动态代理生成的代理类继承了一个Proxy,所以只能通过实现接口来实现。如果目标对象实现了接口也可以使用cglib。但是cglib不能对申明了final的类和方法进行代理。
- 在性能上,在jdk6以前,使用字节码技术实现代理的方式比反射的效率要高。但是jdk6、jdk7、jdk8对jdk动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB,当大陆调用的时候,jdk6、jdk7比CGLIB代理效率低,但是jdk8之后使用jdk动态代理的效率比cglib高。
二者优势
- 使用JDK动态代理,没有额外的依赖,随JDK平滑升级;JDK8之后性能优。
- CGLIB没有限定目标类必须实现接口,适用性广。