AOP CGLIB 是采用 继承方式 进行代理的

本文介绍了Spring AOP中CGLIB代理的工作原理,当使用@Aspect注解并配置合适切点时,CGLIB会通过生成子类的方式创建代理。在调用final方法时,CGLIB代理会直接调用自己的实现,导致在父类中的属性改变不会反映到原始对象。为解决此问题,可以移除final修饰。文章还简述了CGLIB的使用步骤,并指出Spring 3.2之后已内置CGLIB库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果在配置文件中配置了:

<aop:aspectj-autoproxy proxy-target-class="true" />

而且在代码中设置了 @Aspect

 @AfterReturning(value="execution(* com.tfdd.service..*.*(..))", returning="serviceResult")
    public void doAfterReturn(ServiceResult serviceResult){

        if(serviceResult!=null && !serviceResult.isSuccess()){
            TransactionStatus currentTransactionStatus = TransactionAspectSupport.currentTransactionStatus();
            if(currentTransactionStatus!=null){
                currentTransactionStatus.setRollbackOnly();
            }
        }
    }

那么,所有在 com.tfdd.service 及其子类下面的类,都将会是一个代理

比如 下面这个类在 com.tfdd.service 下,:

    public abstract class MSService extends Thread{
        ....    

     public final boolean startMSService() {
        if (isLive) {
            return false;
        }
        isLive = true;
        this.start();
        return true;
    }    


    @Override
    public void run() {
        while (isLive) {
            // do something
        }
    }

        ...
    }

在 注入它的地方,它将是一个proxy:


    @Autowired
    private MSService service;

    @RequestMapping("/start")
    @ResponseBody
    public String activityStart(Integer sum) {
        if (service.isLive()) {
            return "抢车位已经开始了!";
        }
        if (sum == null || sum <= 0) {
            return "参数有误!";
        }
        // 初始化秒杀服务:标志位初始化和消息接受处理方法初始化
        service.initMSService(sum);
        // 开启执行线程
        service.startMSService();

        return "抢车位开始!!!";
    }

在调用 service.startMSService()方法的时候,将会看到一个很神奇的事情:
1. 即 startMSService()的时候,isLive 已经被设置为true了
2. 但过一会儿启动线程,调用run()方法的是偶,isLive却还是false

这是因为:调用startMSService()方法的时候, service是一个proxy,但是在调用run方法的时候, 它是一个Impl了:

  1. service是一个proxy
    这里写图片描述

  2. 调用run方法的时候,它是一个Impl,不是proxy了
    这里写图片描述

通过研究之后,发现 CGLIB 采用的是 生成子类的方式进行代理的,对于final的方法,是继承不了的,但它会有一个相应的方法,当通过代理调用改方法的时候,是会直接调用代理类中的方法的
具体的,在 CglibAopProxy::doValidateClass() 中提到了

这里写图片描述

所以,上述方法中,修改的都是代理类中的 property,但是对于 proxy 代理的 target类(这里是MSServiceImpl )却是没有任何改变的

HOW to resolve:
将final去除掉

 public final boolean startMSService() {
    if (isLive) {
        return false;
    }
    isLive = true;
    this.start();
    return true;
}

变为:

 public boolean startMSService() {
    if (isLive) {
        return false;
    }
    isLive = true;
    this.start();
    return true;
}  

Refer: http://gupeng-ie.iteye.com/blog/1856608
其他知识点:
CGLIB(Code Generation Library)是一个开源项目。

JDK是针对接口的,有局限性,必须由接口。对于业务类不使用接口的,无法使用jdk的动态代理。

CGLib是一个开源的类库,采用非常底层的字节码技术,可以为一个类创建子类,解决无接口代理问题。

jdk 和 CGLib 生成代理的区别?
  jdk动态代理--目标对象面向接口的代理
  CGLib代理--目标对象类的子类

Spring在最新的3.2版本,已经内部集成了CGLib开发包。
在spring-core中。
也就是说:spring3.2之前,使用cglib需要我们手动导入jar–cglib.2.2.3.zip
spring3.2之后,我们使用cglib,只需要导入spring的核心jar包,就可以使用。

*CGLib生成代理的步骤:

package junit;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import cn.gp.proxy.proxypattern.ProductDao;

public class CGLibProxyTest {
@Test
public void testCGLib(){
final ProductDao productDao = new ProductDao();

//第一步,创建Enhancer对象
Enhancer enhancer = new Enhancer();

//第二步,设置根据哪个类生成子类。
enhancer.setSuperclass(productDao.getClass());

//第三步,绑定回调函数callback()--代理类在实现方法时,调用callback
enhancer.setCallback(new MethodInterceptor() {

/**
* proxy:生成的代理对象本身
* method:要执行的方法(反射对象)
* args:要执行的方法的参数
* methodProxy:代理对象的方法,不使用
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {

System.out.println(“被代理对象的任何方法被调用,都会在此被拦截。。。。”);

//调用 被代理对象 的方法(放行)
return method.invoke(productDao, args);
}
});

//第四步,创建代理对象
ProductDao productDaoProxy = (ProductDao) enhancer.create();

productDaoProxy.addProduct();
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值