JDK动态代理
JDK的动态代理只允许代理接口
重要的类和接口Proxy和InvocationHandler
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)方法创建目标对象的代理对象
获取到目标对象的代理对象之后,执行目标对象的方法实际上是通过调用代理对象的invoke方法实现的
我们可以在invoke方法中增加额外的通用的逻辑来对目标对象进行增强
/**
* @Description 代理对象
*/
public class ProxyHandler implements InvocationHandler{
private Object target;
public Object getProxyHandler(Object target){
this.target = target;
Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(target, args);
System.out.println("After");
return invoke;
}
}
获取到代理对象之后,像调用目标对象一样调用代理对象的方法,直观上和普通的调用没有任何区别,但是实际上调用的是代理对象的invoke方法,在invoke方法里面可以做的事情有很多,可以增强甚至重写某些方法,通过method.getName()来对指定方法进行处理
public class ProxyHandlerTest extends TestInit {
@Autowired
ISalesOrderFlowDomainService salesOrderFlowDomainService;
@Test
public void test(){
ProxyHandler proxyHandler = new ProxyHandler();
ISalesOrderFlowDomainService iSalesOrderFlowDomainService = (ISalesOrderFlowDomainService) proxyHandler.getProxyHandler(salesOrderFlowDomainService);
SalesOrderFlowDTO salesOrderFlow = iSalesOrderFlowDomainService.getSalesOrderFlow("20210702000001", OrderFlowType.SIGNED);
System.out.println(salesOrderFlow);
}
}
使用Cglib实现动态代理
Cglib不是jdk自带的jar包,需要下载并加入到项目中
CGLIB代理相关的类:
- net.sf.cglib.proxy.Enhancer 主要的增强类。
- net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
- net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。
CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数
实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor{
//主要的增强类。
private Enhancer enhancer = new Enhancer();
@Override
/**
*
* @param o 是被代理对象
* @param method 调用方法的Method对象
* @param args 方法参数
* @param methodProxy
* @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before " + methodProxy.getSuperName());
System.out.println(method.getName());
Object o1 = methodProxy.invokeSuper(o, args);
//Object o2 = method.invoke(o, args); 使用这种方式会发生死循环,因为方法会被拦截
System.out.println("after " + methodProxy.getSuperName());
return o1;
}
public Object newProxyInstance(Class<?> c) {
//设置产生的代理对象的父类。
enhancer.setSuperclass(c);
//设置CallBack接口的实例
enhancer.setCallback(this);
//使用默认无参数的构造函数创建目标对象
return enhancer.create();
}
}
被代理对象和测试类
public class CglibDemo {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Do o = (Do)cglibProxy.newProxyInstance(Do.class);
System.out.println(o.doSomething(5));
}
}
class Do{
public int doSomething(int num){
System.out.println("方法执行中。。。。。。");
return num;
}
}
Jdk和Cglib 的区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现接口,必须采用CGLIB库.
spring会自动在JDK动态代理和CGLIB之间转换如何强制使用CGLIB实现AOP?
- 添加CGLIB库,SPRING_HOME/cglib/*.jar
- 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
JDK动态代理和CGLIB字节码生成的区别? - JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
【参考文章】
【1】https://www.cnblogs.com/shijiaqi1066/p/3429691.html