代理模式应用-AOP事务(转)

https://jinnianshilongnian.iteye.com/blog/1487235
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、测试代码准备

Java代码

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

Java代码

  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开始支持)

Java代码

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

Java代码

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

Java代码

  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 需要使用的标识接口

Java代码

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

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

二、Bean实现

Java代码

  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实现

Java代码

  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实现

Java代码

  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. }

 

Java代码

  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、测试用例

Java代码

  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的解决方案

Java代码

  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使用的是一样的

原文地址:https://www.cnblogs.com/guanbin-529/p/11221169.html

时间: 2024-10-21 16:39:07

代理模式应用-AOP事务(转)的相关文章

【Java技术点滴】——代理模式及其对事务封装

背景 项目中我们会遇到这样的情况:在几个方法中加入相同的代码,这些代码是与业务无关的,并且以后有可能由于考虑不周或需求变动再或者是其他原因,我们需要对他们进行逐一进行改动.举个具体的例子,比如程序中的日志控制.事务控制等,这些功能是与业务无关的,但却需要将它们与我们的逻辑混在一起,达到一些特殊的需求. 这样的情况往往代码都是相同的,可以抽离出来,为了复用,我们可以将这些相同的代码单独封装成的方法,以供其他需要的地方调用,这样对于以后的修改就做到了只修改一处的效果,达到了程序的复用,但另外一个问题

【Spring入门系列】代理模式和AOP

代理模式概述 代理是一种设计模式,提供了对目标对象另外的访问方式,通过代理对象访问目标对象.这样可以在目标对象功能实现的基础上,增加额外的功能,从而达到扩展目标对象功能的效果.简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式. 静态代理 这种代理方式需要代理对象和目标对象实现一样的接口. 优点:可以在不修改目标对象的前提下扩展目标对象的功能. 缺点:1.冗余,由于代理对象要实现与目标对象一致的接口,会产生过多的代理类.2.不易维护,一旦接口增加方法,

代理模式与AOP

代理模式  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务.简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途.在后面我会解释这种间接

代理模式——用AOP测试业务层方法的执行时间

代理模式 对代理模式的理解,通过http://www.runoob.com/design-pattern/proxy-pattern.html 对AOP的代理模式,参考https://www.cnblogs.com/xujiming/p/5737531.html 目标:测试service层每一个方法的执行时间: 代理模式的本质:将被代理对象注入给代理对象,当调用代理对象时,实际上,是在调用被注入的被代理对象 理解: 代理对象 被代理对象 代理类 被代理对象 商家 厂商 房屋中介 房东 静态代理:

反射实现AOP动态代理模式(Spring AOP实现原理)

其实AOP的意思就是面向切面编程. OO注重的是我们解决问题的方法(封装成Method),而AOP注重的是许多解决问题的方法中的共同点,是对OO思想的一种补充! 还是拿人家经常举的一个例子讲解一下吧: 比如说,我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,或部分监控.也许我们就会在要一些方法前去加上一条日志记录. 我们写个例子看看我们最简单的解决方案 我们先写一个接口IHello.java代码如下: package sinosoft.dj.aop.st

动态代理模式和AOP探究

java强大的反射机制给动态代理带来了可能.能够自由穿梭在类与方法之间.简直神通广大. 动态代理的一个小例子,顺便看看神奇的AOP是如何实现的.代码如下: 首先声明的是一个接口Dog类 1 package com.chenjun.test; 2 3 public interface Dog 4 { 5 public void info(); 6 public void run(); 7 } 然后一个“猎狗类”实现了该接口 1 package com.chenjun.test; 2 3 publi

代理模式-利用JDK原生动态实现AOP

一.目录 二.概述与模型 1.概述 含义:控制对对象的访问. 作用:详细控制某个(某类)某对象的方法,在调用之前做前置处理,调用之后做后置处理,从而实现将统一流程代码放在代理类中处理. 举例:我们在学习JDBC的时候,在批量处理的时候遇到过事务.流程:设置提交方式为手动提交-开启事务-批量处理-关闭事务-设置提交方式为默认.从这里我们清晰可以看见,每次进行批处理的时候,唯有增删改操作变化,其他的操作都是一样的.那么,我们可不可以把一样的操作提取出来交给一个类处理,批量操作提取出来交给一个类实现?

框架 day37 Spring3,AOP,代理模式(动态/CGLIB/工厂bean),传统AOP,AspectJ框架(基于xml/注解),切入点表达式,jdbcTemplate

1     AOP 1.1   什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. * AOP采取横向抽取机制,取代了传统纵向继承体系

AOP之代理模式(一)

AOP,为Aspect OrientedProgramming的缩写,意为:面向切面,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 说了一堆很官方的话,大家可能不是特别明白,不过这些算是我们实践的理论寄出去,还是很有必要知道的,但是现在不必要很懂,接下来从最简单的代码开始,一步步的慢慢深入了解,到底什么是AOP,什么是代理模式.学习,就是这样一个理论与