CGLIB动态代理应用和原理

来源:互联网 发布:u盘启动安装linux系统 编辑:程序博客网 时间:2024/06/05 12:33

CGLIB动态代理是一种在运行时增强字节码的技术,它通过将代理对象设置为被目标对象的子类来增强目标方法。

1.CGLIB动态代理在Spring AOP中的应用

下面我们实现一个Spring AOP的小例子

首先定义一个目标类,其中包含了我们要增强的方法

@Componentpublic 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);    }}
接着定义一个后置增强处理切面

@Aspectpublic class AfterReturningAdviceTest {    @AfterReturning(returning = "rvt", pointcut = "execution(* com.cmb.china.springaop.*.*(..))")    public void log(Object rvt) {        System.out.println("获取目标方法返回值: " + rvt);        System.out.println("模拟记录日志功能");    }}
在定义一个环绕增强处理切面

@Aspectpublic 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