CGLIB动态代理是一种在运行时增强字节码的技术,它通过将代理对象设置为被目标对象的子类来增强目标方法。
1.CGLIB动态代理在Spring AOP中的应用
下面我们实现一个Spring AOP的小例子
首先定义一个目标类,其中包含了我们要增强的方法
@Component public class Chinese implements Person { public String sayHello(String name) { System.out.println("正在执行sayHello方法"); return name + " Hello, Spring Aop"; } public void eat(String food) { System.out.println("我正在吃: " + food); } }接着定义一个后置增强处理切面
@Aspect public class AfterReturningAdviceTest { @AfterReturning(returning = "rvt", pointcut = "execution(* com.cmb.china.springaop.*.*(..))") public void log(Object rvt) { System.out.println("获取目标方法返回值: " + rvt); System.out.println("模拟记录日志功能"); } }在定义一个环绕增强处理切面
@Aspect public class AroundAdviceTest { @Around("execution(* com.cmb.china.springaop.*.*(..))") public Object processTx(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("执行目标方法之前,模拟开始事务..."); Object rvt = joinPoint.proceed(new String[]{"被改变的参数"}); System.out.println("执行目标方法之后,模拟结束事务"); return rvt + " 新增的内容"; } }最后在Spring配置文件中开启AspectJ支持
<context:component-scan base-package="com.cmb.china"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> </context:component-scan> <aop:aspectj-autoproxy/>进行测试
public class BeanTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person chinese = context.getBean("chinese", Person.class); System.out.println(chinese.sayHello("张三")); chinese.eat("西瓜"); System.out.println(chinese.getClass()); } }运行结果
执行目标方法之前,模拟开始事务...
正在执行sayHello方法
执行目标方法之后,模拟结束事务
获取目标方法返回值: 被改变的参数 Hello, Spring Aop 新增的内容
模拟记录日志功能
被改变的参数 Hello, Spring Aop 新增的内容
执行目标方法之前,模拟开始事务...
我正在吃: 被改变的参数
执行目标方法之后,模拟结束事务
获取目标方法返回值: null 新增的内容
模拟记录日志功能
class com.cmb.china.springaop.Chinese$$EnhancerBySpringCGLIB$$464c626a
从结果中可以看出,切面已经织入了目标方法,在执行目标方法的前后执行了我们在切面中定义的增强方法。@AfterReturning注解是在目标方法执行之后织入增强方法,@Around注解是在目标方法执行前后织入增强方法。从chinese.getClass()的结果com.cmb.china.springaop.Chinese&$$EnhancerBySpringCGLIB$$464c626a可知,动态代理类是由CGLIB通过Enhancer生成的。
2.CGLIB动态代理类的生成过程
下面我们再来写一个例子直观感受一下CGLIB生成代理类的过程
先为CGLIB提供一个拦截器实现类
public class AroundAdvice implements MethodInterceptor { public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("执行目标方法之前,模拟事务开始"); Object rvt = methodProxy.invokeSuper(target, new String[]{"被改变的参数"}); System.out.println("执行目标方法之后,模拟结束事务..."); return rvt + " 新增的内容"; } }AroundAdvice实现了MethodInterceptor接口,实现了intercept方法,其中的target是目标对象,method是目标方法,args是目标方法的实际入参,methodPorxy是目标方法的代理方法。
下面实现一个为Chinese对象生成代理类的工厂
public class ChineseProxyFactory { public static Chinese getAuthInstance() { Enhancer enhancer = new Enhancer(); // 通过运行时修改字节码设置代理类的父类 enhancer.setSuperclass(Chinese.class); enhancer.setCallback(new AroundAdvice()); return (Chinese) enhancer.create(); } }测试
public class Test { public static void main(String[] args) { Chinese chinese = ChineseProxyFactory.getAuthInstance(); System.out.println(chinese.sayHello("孙悟空")); chinese.eat("西瓜"); System.out.println(chinese.getClass()); } }运行结果
执行目标方法之前,模拟事务开始
正在执行sayHello方法
执行目标方法之前,模拟事务开始
执行目标方法之前,模拟事务开始
执行目标方法之后,模拟结束事务...
执行目标方法之后,模拟结束事务...
被改变的参数 Hello, Spring Aop 新增的内容
执行目标方法之前,模拟事务开始
我正在吃: 被改变的参数
执行目标方法之前,模拟事务开始
执行目标方法之前,模拟事务开始
执行目标方法之后,模拟结束事务...
执行目标方法之后,模拟结束事务...
class com.cmb.china.springaop.Chinese$$EnhancerByCGLIB$$83b98ab0
正在执行sayHello方法
执行目标方法之前,模拟事务开始
执行目标方法之前,模拟事务开始
执行目标方法之后,模拟结束事务...
执行目标方法之后,模拟结束事务...
被改变的参数 Hello, Spring Aop 新增的内容
执行目标方法之前,模拟事务开始
我正在吃: 被改变的参数
执行目标方法之前,模拟事务开始
执行目标方法之前,模拟事务开始
执行目标方法之后,模拟结束事务...
执行目标方法之后,模拟结束事务...
class com.cmb.china.springaop.Chinese$$EnhancerByCGLIB$$83b98ab0