JDK动态代理和CGLib动态代理的异同?
JDK动态代理是通过jdk的反射机制来动态增强被代理类,被代理类必须是接口的实现类;其主要接口和类是InvokerHandler和proxy;
具体步骤是先通过Proxy.newInstance方法生成代理对象,然后通过实现InvocationHandler的invoke方法来对被代理类中的方法进行增强;
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK代理加工类
*/
public class DynamicProxy implements InvocationHandler {
// 需要先通过getInstance指定目标类,然后再对目标类的方法进行增强
private Object targetObj;
/**
* 对代理方法进行增强:例子中只是对方法添加了日志,实际应用中可以根据需要增加日志管理,事务管理,登陆鉴权等等
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始执行。。。");
Object invoke = method.invoke(targetObj,args);
System.out.println("执行结束。。。");
return invoke;
}
/**
* 创建目标类的实例
* JDK代理必须是基于接口的实现类
* @param targetObj
* @return
*/
public Object getInstance(Object targetObj){
this.targetObj = targetObj;
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),targetObj.getClass().getInterfaces(),this );
}
}
/**
* 代理测试类
*/
public class TestDynamicProxy {
@Test
public void testJDKDynamicProxy() throws IOException {
// 保存代理类的字节码文件(idea不生效),需要设置VM参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
/**
* jdk代理要求所代理的对象必须是通过接口实现的
*/
List<String> list =(List) new DynamicProxy().getInstance(new ArrayList<String>());
list.add("java");
//System.out.println(list.toString());
}
}
它只需要在JDK环境就可以运行;
通过缓存代理类$Proxy0(生成办法上网找一下)可以知道代理对象是继承Proxy,而java是单继承的,想要把代理对象和真实类关联起来,只有通过实现被代理类的接口,所以从设计角度来看,JDK动态代理的被代理类必须是基于接口的实现类。
CGLIB是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB的实现是代理类通过继承被代理类(父类),在代理类(子类)中采用方法拦截的技术拦截所有父类方法的调用并顺势植入增强逻辑;
其主要类是Enhancer,具体步骤是通过设置Enhancer的callback方法对被代理类(父类)的所有方法进行拦截从而达到增强的目的;
/**
* cglib被代理接口
*/
public interface UserInterface {
public void test();
}
package service.impl;
import service.UserInterface;
/**
* cglib被代理类
*/
public class Userservice implements UserInterface {
@Override
public void test(){
System.out.println("调用test方法");
}
}
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import service.UserInterface;
import service.impl.Userservice;
import java.lang.reflect.Method;
/**
* cglib测试类
*/
public class TestCglib {
@Test
public void testCg(){
final Userservice target = new Userservice();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Userservice.class);
enhancer.setCallback(new MethodInterceptor() {
// 对方法进行拦截处理
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if(method.getName().equals("test")){
System.out.println("before。。");
method.invoke(target,objects);
System.out.println("after。。");
return null;
}
// 返回对应方法的返回对象
return method.invoke(target,objects);
}
});
Userservice userservice = (Userservice) enhancer.create();
userservice.test();
}
//运行结果
//before。。
//调用test方法
//after。。
@Test
public void testCgInterface(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserInterface.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理接口");
return null;
}
});
UserInterface userInterface = (UserInterface) enhancer.create();
userInterface.test();
}
//运行结果:代理接口
}
它需要第三方库(例如spring-core包)的支撑;
通过缓存代理类Userservice$$EnhancerByCGLIB$$98e0d2b2(名字和被代理类有关)可以知道代理对象是继承父类Userservice,实现了CGLib的接口Factory;通过实测CGLib可以支持接口和接口实现的类,也支持没有实现接口的类;
Spring 中何时使用JDK或CGLIB?
1如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2如果目标对象实现了接口,也可以通过spring配置参数实现强制使用CGLIB实现AOP
<!--xml配置-->
<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>
//注解方式
@EnableAspectJAutoProxy(proxyTargetClass=true, exposeProxy=true)
3如果目标对象没有实现接口,必须采用 CGLIB,这时Spring会自动在JDK动态代理和CGLIB之间转换