1.代理的概念
什么是代理呢,显而易见,就如同生活中的代理一般,给代理人 做某件事的权限或者能力,然后其他人需要你去做这件事的时候,就不直接让你去做了,而让代理去做。这种模式有什么好处呢?你想想,代理相当于可以做你能做的事了,然后这个代理还能做到你不能做的事。比如你卖一个东西,你只管发货,代理可以帮你宣传,帮你售后,这个能力是不是就增强了。有人可能就有疑问,那我直接把所有能力绑在一起不就行了。额,我只能说很多的业务其实不可能一下全部考虑好,更好的应该是按自己的需求来。
正文
2.CGLIB动态代理
使用CGlib动态代理是实现AOP的一种方式。Spring生成对象时,如果当前类A被切面切到,比如A类的test方法加上了@Transactional注解,就会有一个事务的切面,那么除了生成一个单例外,还会生成代理对象CGLIB$A$XX 这种代理对象,这个 代理对象会继承类A然后实现MethodInterceptor接口。然后我们通过调用注入的类A的test方法,就会发现,其实我们是调用CGLIB$A$XX代理对象的 intercept方法,这个方法就是一个切面逻辑。代码实现可以自己尝试,如下。使用到了enhancer字节码增强器。下面主要讲解intercept方法的参数。主要是MethodProxy。
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// CGLib动态代理 类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"./code");
UserService bean = applicationContext.getBean(UserService.class);
// 字节码增强器
// Enhancer enhancer=new Enhancer();
// // 设置代理类
// enhancer.setSuperclass(UserService.class);
// // 设置切面逻辑
// enhancer.setCallback(new UserServiceCGLIBProxy());
// UserInterface userService= (UserInterface)enhancer.create();
// userService.getJDKuser();
// CGlib代理接口
Enhancer en=new Enhancer();
// 设置代理类
en.setSuperclass(UserService.class);
// 设置切面逻辑
UserService testuser=new UserService();
en.setCallback(new MethodInterceptor() {
/**
*
* @param o 代理对象
* @param method 当前执行的方法
* @param objects 方法的参数
* @param methodProxy methodProxy会代理两个方法,一个是增强后的intercept方法,一个是没有增强的方法,o是代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// invoke执行的是普通的CGlibinvokeMethod方法
methodProxy.invoke(testuser,objects); // 相当于被代理对象直接调用CGlibinvokeMethod方法
// invokeSuper执行的是 CGLIB$CGlibinvokeMethod方法
Object result = methodProxy.invokeSuper(o, objects);// 相当于代理对象调用CGLIB$CGlibinvokeMethod方法
System.out.println("接口切面逻辑");
return result;
}
});
UserService proxy= (UserService)en.create();
proxy.CGlibinvokeMethod();
}
2.1MethodProxy
MethodProxy中主要有两个方法,一个invoke,一个invokeSuper。
invoke执行的是被代理对象的当前方法。invokeSuper执行的时候代理对象的切面逻辑。
因此,invoke的参数必须是被代理对象,不能是XxCGLIB$XX这种代理对象。如果invoke调用代理对象的,代理对象执行方法时会进入intercept,然后又会调用invoke代理对象,这样就会形成无线 递归,最后栈溢出StackOverflowError。
invokeSuper的参数必须是代理对象,因为它会执行CGLIB$test方法,如果参数为被代理对象,那么是找不到这个方法的,也会报错。如果对方法有疑问,可以把代理对象的class字节码文件反编译后看看。代理对象会继承被代理对象,然后会为每个方法创建一个CGLIB$XX方法。由于是继承,所以被代理对象的属性最好不要是final修饰
invoke源码,1代表的是被代理对象,2代表代理对象,底层的源码是使用了FASTCLASS,通过Switch (方法下标),入参是 方法的索引下标,从而快速定位执行方法,而不是通过反射来执行方法。因此这时候CGLIB性能优于jdk反射
这个大概说一下,有兴趣可以自己了解
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw ex;
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
3.JDK动态代理
通过反射的方式来实现增强。代理类实现InvocationHandler接口,但是不需要创建代理类对象
/**
* 作者: 四叶
* 创建时间: 2022/11/4
* 内容:
*/
package com.cglibstudy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserServiceJDKProxy implements InvocationHandler {
private Object targetObj;
public UserServiceJDKProxy(Object target){
this.targetObj=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// JDK动态代理
System.out.println("JDK动态代理方法开始执行");
Object ressult = method.invoke(targetObj, args);
System.out.println("JDK动态代理方法执行完毕");
return ressult;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// JDK动态代理
UserService userService1=new UserService();
Class<? extends UserService> aClass = userService1.getClass();
UserServiceJDKProxy jdkProxy=new UserServiceJDKProxy(userService1);
UserInterface service= (UserInterface)Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), jdkProxy);
service.getJDKuser();
}
总结与比较:
1,CGLIB需要依赖于三方CGLIB库生成代理类,JDK不需要,直接通过反射即可。
2,CGLIB会创建一个继承于被代理对象的代理类,然后实现MethodInterceptor,重写intercept方法,因此被代理类不可用final修饰; JDK实现InvocationHandler 接口。
3,JDK采用委托机制,只能代理实现接口的类。而CGLIB由于是继承被代理对象,可以对普通的类来实现代理。
4.如果当前类是实现了接口,Spring默认使用JDK实现AOP,如果没有实现,会使用CGLIB实现AOP。