1、预备知识

aop概念请参考【http://www.iteye.com/topic/1122401】和【http://jinnianshilongnian.iteye.com/blog/1418596

spring的事务管理,请参考【http://jinnianshilongnian.iteye.com/blog/1441271

转摘出处:https://jinnianshilongnian.iteye.com/blog/1487235

使用AOP 代理后的方法调用执行流程,如图所示

也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。

2、测试代码准备

  1. public interface AService {
  2. public void a();
  3. public void b();
  4. }
  5. @Service()
  6. public class AServiceImpl1 implements AService{
  7. @Transactional(propagation = Propagation.REQUIRED)
  8. public void a() {
  9. this.b();
  10. }
  11. @Transactional(propagation = Propagation.REQUIRES_NEW)
  12. public void b() {
  13. }
  14. }

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方法即可走事务切面,即可以进行事务增强,如下所示:

  1. public void a() {
  2. aopProxy.b();//即调用AOP代理对象的b方法即可执行事务切面进行事务增强
  3. }

判断一个Bean是否是AOP代理对象可以使用如下三种方法:

AopUtils.isAopProxy(bean)        : 是否是代理对象;

AopUtils.isCglibProxy(bean)       : 是否是CGLIB方式的代理对象;

AopUtils.isJdkDynamicProxy(bean) : 是否是JDK动态代理方式的代理对象;

3.1、通过ThreadLocal暴露Aop代理对象

1、开启暴露Aop代理到ThreadLocal支持(如下配置方式从spring3开始支持)

  1. <aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
  1. <aop:config expose-proxy="true"><!—xml风格支持-->

SpringBoot配置:

---------------------

@EnableAspectJAutoProxy

1)使用xml时的配置:

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

属性说明:

①boolean proxyTargetClass() default false;

描述:启用cglib代理,proxyTargetClass默认为false。

②boolean exposeProxy() default false;

描述:如果在一个方法中,调用内部方法,需要在调用内部方法时也能够进行代理,比如内部调用时,使用

(IService)AopContext.currentProxy().sayHello(),需要将exposeProx设置为true,默认为false。

2)使用注解@EnableAspectJAutoProxy配置

@EnableAspectJAutoProxy(proxyTargetClass=true, exposeProxy=true)
@SpringBootApplication
public class BootStrap {
public static void main(String[] args) {
SpringApplication.run(BootStrap.class, args);
}
}

注:也可以不使用@EnableAspectAutoProxy注解开启AspectJ自动代理技术,可以在spring boot的默认配置appllication.properties中进行配置,配置如下:

# 增加@EnableAspectJAutoProxy
spring.aop.auto=true
# 开启CGLIB代理
spring.aop.proxy-target-class=true
---------------------

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、通过初始化方法在目标对象中注入代理对象

  1. @Service
  2. public class AServiceImpl3 implements AService{
  3. @Autowired  //①  注入上下文
  4. private ApplicationContext context;
  5. private AService proxySelf; //②  表示代理对象,不是目标对象
  6. @PostConstruct  //③ 初始化方法
  7. private void setSelf() {
  8. //从上下文获取代理对象(如果通过proxtSelf=this是不对的,this是目标对象)
  9. //此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean
  10. proxySelf = context.getBean(AService.class);
  11. }
  12. @Transactional(propagation = Propagation.REQUIRED)
  13. public void a() {
  14. proxySelf.b(); //④ 调用代理对象的方法 这样可以执行事务切面
  15. }
  16. @Transactional(propagation = Propagation.REQUIRES_NEW)
  17. public void b() {
  18. }
  19. }

此处日志就不分析,和3.1类似。此种方式不是很灵活,所有需要自我调用的实现类必须重复实现代码。

3.3、通过BeanPostProcessor 在目标对象中注入代理对象

此种解决方案可以参考http://fyting.iteye.com/blog/109236

BeanPostProcessor 的介绍和使用敬请等待我的下一篇分析帖。

一、定义BeanPostProcessor 需要使用的标识接口

  1. public interface BeanSelfAware {
  2. void setSelf(Object proxyBean);
  3. }

即我们自定义的BeanPostProcessor (InjectBeanSelfProcessor)如果发现我们的Bean是实现了该标识接口就调用setSelf注入代理对象。

二、Bean实现

  1. @Service
  2. public class AServiceImpl4 implements AService, BeanSelfAware {//此处省略接口定义
  3. private AService proxySelf;
  4. public void setSelf(Object proxyBean) { //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
  5. this.proxySelf = (AService) proxyBean;
  6. }
  7. @Transactional(propagation = Propagation.REQUIRED)
  8. public void a() {
  9. proxySelf.b();//调用代理对象的方法 这样可以执行事务切面
  10. }
  11. @Transactional(propagation = Propagation.REQUIRES_NEW)
  12. public void b() {
  13. }
  14. }

实现BeanSelfAware标识接口的setSelf将代理对象注入,并且通过“proxySelf.b()”这样可以实施b方法的事务定义。

三、InjectBeanSelfProcessor实现

  1. @Component
  2. public class InjectBeanSelfProcessor implements BeanPostProcessor {
  3. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  4. return bean;
  5. }
  6. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  7. if(bean instanceof BeanSelfAware) {//如果Bean实现了BeanSelfAware标识接口,就将代理对象注入
  8. ((BeanSelfAware) bean).setSelf(bean); //即使是prototype Bean也可以使用此种方式
  9. }
  10. return bean;
  11. }
  12. }

postProcessAfterInitialization根据目标对象是否实现BeanSelfAware标识接口,通过setSelf(bean)将代理对象(bean)注入到目标对象中,从而可以完成目标对象内部的自我调用。

关于BeanPostProcessor的执行流程等请一定参考我的这篇帖子,否则无法继续往下执行。

四、InjectBeanSelfProcessor的问题

(1、场景:通过InjectBeanSelfProcessor进行注入代理对象且循环依赖场景下会产生前者无法通过setSelf设置代理对象的问题。 循环依赖是应该避免的,但是实际工作中不可避免会有人使用这种注入,毕竟没有强制性。

(2、用例

(2.1定义BeanPostProcessor 需要使用的标识接口

和3.1中一样此处不再重复。

 

2.2Bean实现

  1. @Service
  2. public class AServiceImpl implements AService, BeanSelfAware {//此处省略Aservice接口定义
  3. @Autowired
  4. private BService bService;   //①  通过@Autowired方式注入BService
  5. private AService self;       //②  注入自己的AOP代理对象
  6. public void setSelf(Object proxyBean) {
  7. this.self = (AService) proxyBean;  //③ 通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
  8. System.out.println("AService=="+ AopUtils.isAopProxy(this.self)); //如果输出true标识AOP代理对象注入成功
  9. }
  10. @Transactional(propagation = Propagation.REQUIRED)
  11. public void a() {
  12. self.b();
  13. }
  14. @Transactional(propagation = Propagation.REQUIRES_NEW)
  15. public void b() {
  16. }
  17. }

 

  1. @Service
  2. public class BServiceImpl implements BService, BeanSelfAware {//此处省略Aservice接口定义
  3. @Autowired
  4. private AService aService;  //①  通过@Autowired方式注入AService
  5. private BService self;      //②  注入自己的AOP代理对象
  6. public void setSelf(Object proxyBean) {  //③ 通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象
  7. this.self = (BService) proxyBean;
  8. System.out.println("BService=" + AopUtils.isAopProxy(this.self)); //如果输出true标识AOP代理对象注入成功
  9. }
  10. @Transactional(propagation = Propagation.REQUIRED)
  11. public void a() {
  12. self.b();
  13. }
  14. @Transactional(propagation = Propagation.REQUIRES_NEW)
  15. public void b() {
  16. }
  17. }

此处A依赖B,B依赖A,即构成循环依赖,此处不探讨循环依赖的设计问题(实际工作应该避免循环依赖),只探讨为什么循环依赖会出现注入代理对象失败的问题。

 

循环依赖请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415278】。

依赖的初始化和销毁顺序请参考我的博文【http://jinnianshilongnian.iteye.com/blog/1415461】。

(2.3、InjectBeanSelfProcessor实现

和之前3.3中一样 此处不再重复。

 

(2.4、测试用例

  1. @RunWith(value = SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(value = {"classpath:spring-config.xml"})
  3. public class SelfInjectTest {
  4. @Autowired
  5. AService aService;
  6. @Autowired
  7. BService bService;
  8. @Test
  9. public void test() {
  10. }
  11. }

执行如上测试用例会输出:

BService=true

AService==false

即BService通过InjectBeanSelfProcessor注入代理对象成功,而AService却失败了(实际是注入了目标对象),如下是debug得到的信息:

(2. 5、这是为什么呢,怎么在循环依赖会出现这种情况?

敬请期待我的下一篇分析帖。

3.4、改进版的InjectBeanSelfProcessor的解决方案

  1. @Component
  2. public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {
  3. private ApplicationContext context;
  4. //① 注入ApplicationContext
  5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6. this.context = applicationContext;
  7. }
  8. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  9. if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过
  10. return bean;
  11. }
  12. if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入
  13. ((BeanSelfAware) bean).setSelf(bean);
  14. } else {
  15. //④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
  16. //此种方式不适合解决prototype Bean的代理对象注入
  17. ((BeanSelfAware)bean).setSelf(context.getBean(beanName));
  18. }
  19. return bean;
  20. }
  21. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  22. return bean;
  23. }
  24. }

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代理对象注入问题,即无法解决目标对象的自我调用问题。

没有完美的解决方案,只有最适用的解决方案。

测试代码请参考附件,jar包与http://www.iteye.com/topic/1120924使用的是一样的

Spring事务处理时自我调用的更多相关文章

  1. Spring事务处理时自我调用的解决方案 嵌套AOP

    开涛的解决方案1 http://jinnianshilongnian.iteye.com/blog/1487235 AopContext.currentProxy() 原理 http://books. ...

  2. 【spring源码学习】spring的aop目标对象中进行自我调用,且需要实施相应的事务定义的解决方案

    转载:http://www.iteye.com/topic/1122740 1.预备知识 aop概念请参考[http://www.iteye.com/topic/1122401]和[http://ji ...

  3. Spring事务处理

    事务(Transaction)是并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位. 数据库向用户提供保存当前程序状态的方法,叫事务提交(commit): ...

  4. java 之DelayQueue,TaskDelayed,handlerFactory,dataChange消息配置.收发等.java spring事务处理TransactionTemplate

    java 之DelayQueue,TaskDelayed,handlerFactory,dataChange消息配置.收发等.java spring事务处理TransactionTemplate等. ...

  5. SpringMVC系列(十五)Spring MVC与Spring整合时实例被创建两次的解决方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的关系

    一.Spring MVC与Spring整合时实例被创建两次的解决方案 1.问题产生的原因 Spring MVC的配置文件和Spring的配置文件里面都使用了扫描注解<context:compon ...

  6. 在 Visual Studio 中调试时映射调用堆栈上的方法

    本文转自:https://msdn.microsoft.com/zh-cn/library/dn194476.aspx 1.创建代码图,以便在调试时对调用堆栈进行可视化跟踪. 你可以在图中进行标注以跟 ...

  7. 编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时会产生Additional information: 阅读器关闭时尝试调用 Read 无效问题,解决方法与解释

    在自学杨中科老师的视频教学时,拓展编写SqlHelper使用,在将ExecuteReader方法封装进而读取数据库中的数据时 会产生Additional information: 阅读器关闭时尝试调用 ...

  8. struts2与spring集成时action的class属性值意义

    struts2单独使用时action由struts2自己负责创建:与spring集成时,action实例由spring负责创建(依赖注入).这导致在两种情况下struts.xml配置文件的略微差异. ...

  9. Object之魔术函数__toString() 直接输出对象引用时自动调用

    __toString()是快速获取对象的字符串信息的便捷方式 在直接输出对象引用时自动调用的方法. __toString()的作用 当我们调试程序时,需要知道是否得出正确的数据.比如打印一个对象时,看 ...

随机推荐

  1. 从 Tapable 中得到的启发

    Tapable Why Tapable 前端开发中 Webpack 本质上是基于事件流的运行机制,它的工作流程是将特定的任务分发到指定的事件钩子中去完成.而实现这一切的核心就是 tapable,Web ...

  2. The main method caused an error: java.util.concurrent.ExecutionException: org.apache.flink.runtime.client.JobSubmissionException: Failed to submit JobGraph.

    在使用flink run命令提交任务可能会遇到如下错误: The program finished with the following exception: org.apache.flink.cli ...

  3. js事件入门(6)

    7.事件冒泡机制 7.1.什么是事件冒泡 当一个元素接收到一个事件以后,会将事件传播给它的父级元素,它的负级元素会一层一层往上传播,直到最顶层window,这种事件传播机制叫作事件冒泡. <!D ...

  4. MFC:CImage显示OpenCV:Mat矩阵图像

    *************************************/ //1.读入Mat矩阵(cvMat一样),Mat img=imread("*.*");//cvLoad ...

  5. Spreading the Wealth

    题目 A Communist regime is trying to redistribute wealth in a village. They have have decided to sit e ...

  6. 阿里云centos7安装jdk8

    1.准备Linux版本的jdk8直接上Oracle公司的官网下载就好了    http://www.oracle.com/technetwork/java/javase/downloads/jdk8- ...

  7. 轻松搞定安全框架(Shiro)

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  8. 使用TransferLearning实现环视图像的角点检测——Tensorflow+MobileNetv2_SSD

    环境说明 依赖环境安装eIQ官方指南: name: eiq_auto channels: - conda-forge - defaults dependencies: - numpy=1.18.1=p ...

  9. linux简单介绍

    Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统. linux基本思想有两点,1.一切都是文件:2.每个软件有确定 ...

  10. CodeFoeces 1215 D Ticket Game(数学,思维)

    CodeFoeces 1215 D Ticket Game 题目大意 M和B轮流玩游戏(每一轮M先手 现在给出一个长度为偶数的串,包含字符'?'和数字 现在两人依次在'?'上填数字\(0\)~\(9\ ...