Spring+AspectJ集成

最近遇到个问题,服务端框架被重新构建后,发现spring里配置的@Transaction失效了,报了hibernate session未绑定的错误:
具体流程是:

1.注册函数:

Spring+AspectJ集成

2.反射调用这个注册的方法

Spring+AspectJ集成

这个invoke的 _this就是上面注册时候的this
其中_onLogin方法是有@transaction的,但是从debug的结果可以看出来,这个方法并没有被AOP代理,所以自然没有处理transaction。
原因就在于invoke的_this,并不是spring的代理类,也就是说 是脱离spring管理的类,所以aop不起作用自然正常

改造方法有两种:
注册函数的时候 this,使用spring的代理类

Spring+AspectJ集成

或者
反射调用的时候,使用spring的代理类

Spring+AspectJ集成

这样其实这个wearver其实是被spring代理的,从debug模式下可以看到是被GCLIB代理的,被spring管理了。这样Transaction就又可以使用了。

如果脱离spring管理的类,想使用aop或者是老项目怎么办?Aspectj可以解决这个问题,具体的原理等2017-3-8解释了,看下我们这个项目如何改造
其中这方面的中文资料很少,最后完成还是阅读了spring的官方文档才解决。
其中主要阅读2章
AOP和transactionmanager

其中使用的是 LTW(load Time Weaving),这个原理大概就是在classload的时候植入代码
1.application里面需要这么配置:

Spring+AspectJ集成

其中 aspectj-weaving="on"

Spring+AspectJ集成

2.pom.xml需要增加 aspectj支持

Spring+AspectJ集成

这里踩了坑,这么配置了报一个错误,大概意思是aspectj是版本是6.0,但是spring aop aspject这个借口是7.0版本。这里把aspectj的版本提升到1.7,但是不好用,找了很多资料未果,才发现整个项目构建是用了maven+普通lib的管理方式,普通引用的lib里有aspectj 1.5的包 (可以推测是java优先使用引用的lib,而不是maven的包
3.我们需要更改classload,所以需要修改启动参数(-javaagent:path/to/spring-instrument.jar
其中老版本是spring-agent.jar 

Spring+AspectJ集成

根据文档全部配置完毕之后,启动服务,发现还是失效
出问题的代码

Spring+AspectJ集成

这里可以看到是spring加载完 立马使用dao这里失败了,但是从debug中看出其实这里是aspectj的已经起作用了,没起作用的是transaction(推测是是transaction失效,而不是aspectj失效),spring这里的文档有提到自己annnotationTranscationAspect的自己注入transcationManager

Spring+AspectJ集成

在代码中手动注入

Spring+AspectJ集成

正常工作。说明猜测是对的,这里加载顺序有问题,把这段代码移动到spring另外一个加载完的代码里。

Spring+AspectJ集成

把dao的代码移动到ApplicationListener里有效(猜测:加载顺序ApplicationContentAware,aspectj aop,ApplicationListener(ContextRefreshedEvent事件))
至此,程序正常,除了报一个waring(还不知道原因)
[[email protected]] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
这种配置aop transcation的代码不支持aspectj

Spring+AspectJ集成

结论:
至此可以看到Aspectj还是很强大的,不受spring aop托管的类也可以随意加@transaction
对于一些业务代码可以尝试用aop开发,比如说日志监控和某些活动业务可以尝试aop
另外:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"> 的功能是 支持 @Transcation

Spring+AspectJ集成

这段其实是对 biz ,game这个包下的方法 做了aop,aop的方法是增加Transcation控制

关于ApplicationContentAware和ApplicationListener(ContextRefreshedEvent事件)的加载顺序,debug参看了下spring加载的源码,发现是前者在前,后者在后,源码解析:

Spring+AspectJ集成

关于ApplicationContentAware和aspectj aop谁先执行,从debug源码也可以看到,先执行applicationContentAware里的代码,在去给AnnotationTranscationAspect里面的transcationManager赋值,所以导致在applicationContextWare里使用dao失败。符合之前的推测。
Spring+AspectJ集成

执行顺序是
1.ApplicationContentWare 的接口
2.给tx 赋值 transcation  (即 tx:annotation-driven transaction-manager="transactionManager" mode ="aspectj")
3.抛出ContextRefreshedEvent事件

奇怪的是把 transcation 放弃 aspectj之后,正常。跟踪debug后发现执行顺序变化为
1.给tx 赋值 transcation  (即 tx:annotation-driven transaction-manager="transactionManager")
2.ApplicationContentWare 的接口
3.抛出ContextRefreshedEvent事件
貌似对于aspectj 模式 spring是特殊处理的,怀疑是个bug?