【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案
转载:http://www.iteye.com/topic/1122740
1、预备知识
aop概念请参考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596】
spring的事务管理,请参考【http://jinnianshilongnian.iteye.com/blog/1441271】
使用AOP 代理后的方法调用执行流程,如图所示
也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
2、测试代码准备
public interface AService {
public void a();
public void b();
} @Service()
public class AServiceImpl1 implements AService{
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
this.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
3、问题
目标对象内部的自我调用将无法实施切面中的增强,如图所示
此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强,因此b方法的事务定义“@Transactional(propagation = Propagation.REQUIRES_NEW)”将不会实施,即结果是b和a方法的事务定义是一样的,可以从以下日志看出:
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'a' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager' org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl1.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' -----创建a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session …… for Hibernate transaction ---打开Session …… org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl1.a] org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl1.a] ----完成a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session ……---提交a方法事务 或 org.springframework.orm.hibernate4.HibernateTransactionManager Rolling back Hibernate transaction on Session ……---如果有异常将回滚a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization …… org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --关闭Session
我们可以看到事务切面只对a方法进行了事务增强,没有对b方法进行增强。
3、解决方案
此处a方法中调用b方法时,只要通过AOP代理调用b方法即可走事务切面,即可以进行事务增强,如下所示:
public void a() {
aopProxy.b();//即调用AOP代理对象的b方法即可执行事务切面进行事务增强
}
判断一个Bean是否是AOP代理对象可以使用如下三种方法:
AopUtils.isAopProxy(bean) : 是否是代理对象;
AopUtils.isCglibProxy(bean) : 是否是CGLIB方式的代理对象;
AopUtils.isJdkDynamicProxy(bean) : 是否是JDK动态代理方式的代理对象;
3.1、通过ThreadLocal暴露Aop代理对象
1、开启暴露Aop代理到ThreadLocal支持(如下配置方式从spring3开始支持)
<aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
或者
<aop:config expose-proxy="true"><!—xml风格支持-->
2、修改我们的业务实现类
this.b();-----------修改为--------->((AService) AopContext.currentProxy()).b();
3、执行测试用例,日志如下
org.springframework.beans.factory.support.DefaultListableBeanFactory Returning cached instance of singleton bean 'txManager' org.springframework.orm.hibernate4.HibernateTransactionManager Creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.a]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '' -----创建a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session ……for Hibernate transaction --打开a Session org.springframework.orm.hibernate4.HibernateTransactionManager Preparing JDBC Connection of Hibernate Session …… org.springframework.orm.hibernate4.HibernateTransactionManager Exposing Hibernate transaction as JDBC transaction …… …… org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.a] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource Adding transactional method 'b' with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; '' …… org.springframework.orm.hibernate4.HibernateTransactionManager Suspending current transaction, creating new transaction with name [com.sishuok.service.impl.AServiceImpl2.b] -----创建b方法事务(并暂停a方法事务) …… org.springframework.orm.hibernate4.HibernateTransactionManager Opened new Session for Hibernate transaction ---打开b Session …… org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Getting transaction for [com.sishuok.service.impl.AServiceImpl2.b] org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.b] ----完成b方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session …… ---提交b方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization …… org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --关闭 b Session -----到此b方法事务完毕 org.springframework.orm.hibernate4.HibernateTransactionManager Resuming suspended transaction after completion of inner transaction ---恢复a方法事务 …… org.springframework.transaction.support.TransactionSynchronizationManager Initializing transaction synchronization org.springframework.transaction.interceptor.TransactionInterceptor Completing transaction for [com.sishuok.service.impl.AServiceImpl2.a] ----完成a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering beforeCompletion synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Initiating transaction commit org.springframework.orm.hibernate4.HibernateTransactionManager Committing Hibernate transaction on Session ……---提交a方法事务 org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCommit synchronization org.springframework.orm.hibernate4.HibernateTransactionManager Triggering afterCompletion synchronization org.springframework.transaction.support.TransactionSynchronizationManager Clearing transaction synchronization …… org.springframework.orm.hibernate4.HibernateTransactionManager Closing Hibernate Session …… after transaction --关闭 a Session
此处我们可以看到b方法的事务起作用了。
以上方式是解决目标对象内部方法自我调用并实施事务的最简单的解决方案。
4、实现原理分析
4.1、在进入代理对象之后通过AopContext.serCurrentProxy(proxy)暴露当前代理对象到ThreadLocal,并保存上次ThreadLocal绑定的代理对象为oldProxy;
4.2、接下来我们可以通过 AopContext.currentProxy() 获取当前代理对象;
4.3、在退出代理对象之前要重新将ThreadLocal绑定的代理对象设置为上一次的代理对象,即AopContext.serCurrentProxy(oldProxy)。
有些人不喜欢这种方式,说通过ThreadLocal暴露有性能问题,其实这个不需要考虑,因为事务相关的(Session和Connection)内部也是通过SessionHolder和ConnectionHolder暴露到ThreadLocal实现的。
不过自我调用这种场景确实只有很少情况遇到,因此不用这种方式我们也可以通过如下方式实现。
3.2、通过初始化方法在目标对象中注入代理对象
@Service
public class AServiceImpl3 implements AService{
@Autowired //① 注入上下文
private ApplicationContext context; private AService proxySelf; //② 表示代理对象,不是目标对象
@PostConstruct //③ 初始化方法
private void setSelf() {
//从上下文获取代理对象(如果通过proxtSelf=this是不对的,this是目标对象)
//此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean
proxySelf = context.getBean(AService.class);
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
proxySelf.b(); //④ 调用代理对象的方法 这样可以执行事务切面
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
此处日志就不分析,和3.1类似。此种方式不是很灵活,所有需要自我调用的实现类必须重复实现代码。
3.3、通过BeanPostProcessor 在目标对象中注入代理对象
此种解决方案可以参考http://fyting.iteye.com/blog/109236。
BeanPostProcessor 的介绍和使用敬请等待我的下一篇分析帖。
一、定义BeanPostProcessor 需要使用的标识接口
public interface BeanSelfAware {
void setSelf(Object proxyBean);
}
即我们自定义的BeanPostProcessor (InjectBeanSelfProcessor)如果发现我们的Bean是实现了该标识接口就调用setSelf注入代理对象。
二、Bean实现
@Service
public class AServiceImpl4 implements AService, BeanSelfAware {//此处省略接口定义
private AService proxySelf;
public void setSelf(Object proxyBean) { //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
this.proxySelf = (AService) proxyBean;
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
proxySelf.b();//调用代理对象的方法 这样可以执行事务切面
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
实现BeanSelfAware标识接口的setSelf将代理对象注入,并且通过“proxySelf.b()”这样可以实施b方法的事务定义。
三、InjectBeanSelfProcessor实现
@Component
public class InjectBeanSelfProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof BeanSelfAware) {//如果Bean实现了BeanSelfAware标识接口,就将代理对象注入
((BeanSelfAware) bean).setSelf(bean); //即使是prototype Bean也可以使用此种方式
}
return bean;
}
}
postProcessAfterInitialization根据目标对象是否实现BeanSelfAware标识接口,通过setSelf(bean)将代理对象(bean)注入到目标对象中,从而可以完成目标对象内部的自我调用。
关于BeanPostProcessor的执行流程等请一定参考我的这篇帖子,否则无法继续往下执行。
四、InjectBeanSelfProcessor的问题
(1、场景:通过InjectBeanSelfProcessor进行注入代理对象且循环依赖场景下会产生前者无法通过setSelf设置代理对象的问题。 循环依赖是应该避免的,但是实际工作中不可避免会有人使用这种注入,毕竟没有强制性。
(2、用例
(2.1、定义BeanPostProcessor 需要使用的标识接口
和3.1中一样此处不再重复。
(2.2、Bean实现
@Service
public class AServiceImpl implements AService, BeanSelfAware {//此处省略Aservice接口定义
@Autowired
private BService bService; //① 通过@Autowired方式注入BService
private AService self; //② 注入自己的AOP代理对象
public void setSelf(Object proxyBean) {
this.self = (AService) proxyBean; //③ 通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
System.out.println("AService=="+ AopUtils.isAopProxy(this.self)); //如果输出true标识AOP代理对象注入成功
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
self.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
@Service
public class BServiceImpl implements BService, BeanSelfAware {//此处省略Aservice接口定义
@Autowired
private AService aService; //① 通过@Autowired方式注入AService
private BService self; //② 注入自己的AOP代理对象
public void setSelf(Object proxyBean) { //③ 通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
this.self = (BService) proxyBean;
System.out.println("BService=" + AopUtils.isAopProxy(this.self)); //如果输出true标识AOP代理对象注入成功
}
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
self.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
此处A依赖B,B依赖A,即构成循环依赖,此处不探讨循环依赖的设计问题(实际工作应该避免循环依赖),只探讨为什么循环依赖会出现注入代理对象失败的问题。
循环依赖请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415278】。
依赖的初始化和销毁顺序请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415461】。
(2.3、InjectBeanSelfProcessor实现
和之前3.3中一样 此处不再重复。
(2.4、测试用例
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:spring-config.xml"})
public class SelfInjectTest {
@Autowired
AService aService;
@Autowired
BService bService;
@Test
public void test() {
}
}
执行如上测试用例会输出:
BService=true
AService==false
即BService通过InjectBeanSelfProcessor注入代理对象成功,而AService却失败了(实际是注入了目标对象),如下是debug得到的信息:
(2. 5、这是为什么呢,怎么在循环依赖会出现这种情况?
敬请期待我的下一篇分析帖。
3.4、改进版的InjectBeanSelfProcessor的解决方案
@Component
public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext context;
//① 注入ApplicationContext
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过
return bean;
}
if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入
((BeanSelfAware) bean).setSelf(bean);
} else {
//④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
//此种方式不适合解决prototype Bean的代理对象注入
((BeanSelfAware)bean).setSelf(context.getBean(beanName));
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
5、总结
纵观其上:
【3.1 通过ThreadLocal暴露Aop代理对象】适合解决所有场景(不管是singleton Bean还是prototype Bean)的AOP代理获取问题(即能解决目标对象的自我调用问题);
【3.2 通过初始化方法在目标对象中注入代理对象】 和【3.4 改进版的InjectBeanSelfProcessor的解决方案】能解决普通(无循环依赖)的AOP代理对象注入问题,而且也能解决【3.3】中提到的循环依赖(应该是singleton之间的循环依赖)造成的目标对象无法注入AOP代理对象问题,但该解决方案不适合解决循环依赖中包含prototype Bean的自我调用问题;
【3.3 通过BeanPostProcessor 在目标对象中注入代理对象】:只能解决 普通(无循环依赖)的 的Bean注入AOP代理,无法解决循环依赖的AOP代理对象注入问题,即无法解决目标对象的自我调用问题。
A----B
B----A
只有在A和B都不为原型是允许的,即如果A和B都是prototype则会报错(无法进行原型Bean的循环依赖)。
A(单例)---B(单例) 或 A(原型)---B(单例) 这是可以的,但 A(原型)---B(原型)或 A(原型)---B(单例Lazy)【且context.getBean("A")】时 这是不允许的。
一、A(原型)---B(原型) A(原型)---B(单例Lazy)【且context.getBean("A")】 会报:
Error
creating bean with name 'BServiceImpl': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.sishuok.issue.AService
com.sishuok.issue.impl.BServiceImpl.aService; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'AServiceImpl': Injection of autowired dependencies
failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.sishuok.issue.BService
com.sishuok.issue.impl.AServiceImpl.bService; nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'BServiceImpl': Requested bean is
currently in creation: [color=red]Is there an unresolvable circular
reference[/color]?
二、A(原型)---B(单例) 和 A(单例)---B(单例)
这种方式 使用我的 【3.3 通过BeanPostProcessor 在目标对象中注入代理对象】 是没有问题的。
因此【
3.4 改进版的InjectBeanSelfProcessor的解决方案 】 可以作为最后的解决方案。
【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案的更多相关文章
- spring源码学习之路---AOP初探(六)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 最近工作很忙,但当初打算学习 ...
- spring源码学习——spring整体架构和设计理念
Spring是在Rod Johnson的<Expert One-On-One J2EE Development and Design >的基础上衍生而来的.主要目的是通过使用基本的java ...
- Spring 源码学习(4) —— 动态AOP使用示例
在实际工作中, 此bean可能是满足业务需要的核心逻辑, 例如test()方法中可能会封装着某个核心业务, 如果在test()方法前后加入日志来跟踪调试, 直接修改源码并不符合面向对象的设计模式, 而 ...
- Spring源码学习
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
- Spring 源码学习——Aop
Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...
- spring源码学习之AOP(一)
继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...
- Spring 源码学习笔记10——Spring AOP
Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...
- spring源码学习之路---深入AOP(终)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...
- Spring源码学习(7)——AOP
我们知道,使用面对对象编程的时候有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志.安全检测等,所以就有了一个对面对对象编程的补充,即面对切面编程(AOP),AOP所关注的方向 ...
随机推荐
- PHP stream相关协议及上下文选项和参数归纳
支持的协议和封装协议 PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen(). copy(). file_exists() 和 filesize() 的文件系统函数. 除了这些封装协 ...
- zabbix自动化运维学习笔记(服务器安装)
最近博主开始接触自动化运维.首先就是zabbix这个开源的监控系统 一开始博主只是在自己的虚拟机上尝试安装.最后终于开始在公司的服务器上正式安装,教程博主也是通过度娘找的 这是原文:链接 安装环境:C ...
- python中如何剔除字符串
问题: 过滤用户输入中前后多余的空白字符 ‘ ++++abc123--- ‘ 过滤某windows下编辑文本中的’\r’: ‘hello world \r\n’ 去掉文本中unicode组 ...
- 4.彻底理解synchronized
1. synchronized简介 在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable { private s ...
- (2) iOS开发之UI处理-UILabel篇
我们经常要根据内容去动态计算控件的高度,比如一个UILabel控件,常常要显示多行内容,并且计算出总高度,如果每个UILabel要多行显示,都要写这么一段代码是非常痛苦的,看代码如下: 我想大 ...
- SpringBoot Mybatis PageHelper插件报错
SpringBoot2.0.0 MyBatis1.3.2 PageHelper1.1.2插件,但是在启动运行时,抛错:org.springframework.beans.factory.BeanCre ...
- ES查询index对应的mapping信息
private void getMappingByIndex(String indices) throws IOException { GetMappingsRequest getMappingsRe ...
- 《Drools7.0.0.Final规则引擎教程》第4章 4.4 约束(Pattern的一部分)
4.4.3 约束(Pattern的一部分) 前面我们已经介绍了条件约束在Pattern中位置了,那么什么是条件约束呢?简单来说就是一个返回true或者false的表达式,比如下面的5小于6,就是一个约 ...
- PHPStorm 使用正则批量查询替换并自动转换大小写的方法
PHPStorm 的项目查询替换功能那是非常非常强大的, 速度也很快, 配合正则更加灵活强大. 一般的正则查询替换没什么太多好说的, 这里主要说说比较少用的 大小写自动转换的问题, 也是比较少用但很有 ...
- Leetcode 894. All Possible Full Binary Trees
递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # ...