如果在配置文件中配置了:
<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了:
service是一个proxy
调用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();
}
}