众所周知,Spring的声明式事务是利用AOP手段实现的,所谓“深入一点,你会更快乐”,本文试图给出相关代码分析。

AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring由Advice接口扩展了5中类型的增强(接口),AOP联盟自身提供了IntroductionInterceptor->MethodInterceptor->Interceptor->Advice,而MethodInterceptor就代表环绕增强,表示在目标方法执行前后实施增强。要进行事务操作,正是要在目标方法前后加入相应的代码,因此,Spring为我们提供了TransactionInterceptor类。

TransactionInterceptor的invoke方法调用了父类TransactionAspectSupport的invokeWithinTransactionf方法,

Java代码  
  1. if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
  2. // Standard transaction demarcation with getTransaction and commit/rollback calls.
  3. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
  4. Object retVal = null;
  5. try {
  6. // This is an around advice: Invoke the next interceptor in the chain.
  7. // This will normally result in a target object being invoked.
  8. retVal = invocation.proceedWithInvocation();
  9. }
  10. catch (Throwable ex) {
  11. // target invocation exception
  12. completeTransactionAfterThrowing(txInfo, ex);
  13. throw ex;
  14. }
  15. finally {
  16. cleanupTransactionInfo(txInfo);
  17. }
  18. commitTransactionAfterReturning(txInfo);
  19. return retVal;
  20. }

瞬间,我们看到了我们期望看到的代码,其中completeTransactionAfterThrowing里面做的是rollback的相关操作。

Spring 提供了多种不同的方案实现对 bean 的 aop proxy, 包括 ProxyFactoryBean, 便利的 TransactionProxyFactoryBean 以及 AutoProxyCreator 等,

这里重点说一下最常用的 ProxyFactoryBean, TransactionProxyFactoryBean, BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator 的联系和区别

1. ProxyFactoryBean : 使用率最高的 proxy 方式, 它通过配置 interceptorNames 属性决定加入哪些 advisor (method interceptor 将会被自动包装成 advisor),

注意是 "interceptorNames" 而不是 "interceptors",

原因是 ProxyFactoryBean 可能返回非 singleton 的 proxy 实例, 而 advisior 可能也是非 singleton 的,

因此不能通过 interceptor reference 来注入

2. TransactionProxyFactoryBean : 特定用于 transaction proxy, 注意其 super class 是 AbstractSingletonProxyFactoryBean, 也就是说,

TransactionProxyFactoryBean 永远无法返回非 singleton 的 proxy 实例 !

如果你需要非 singleton 的 proxy 实例, 请考虑使用 ProxyFactoryBean.

3. BeanNameAutoProxyCreator : 故名思义, 根据 bean name 进行 auto proxy, bean name 的 match 规则参见 org.springframework.util.PatternMatchUtils

4. DefaultAdvisorAutoProxyCreator : 更强大的 auto proxy creator, 强大之处在于它会 cahce 容器中所有注册的 advisor, 然后搜索容器中所有的 bean ,

如果某个 bean 满足 advisor 中的 Pointcut, 那么将会被自动代理, 与 BeanNameAutoProxyCreator 相比, 省去了配置 beanNames 的工作,

5. AnnotationAwareAspectJAutoProxyCreator -> @Aspect  <aop:aspectj-autoproxy/>

-> @Transactinal <tx:annotation-driven transaction-manager="txManager"/>

AbstractAutoProxyCreator实现了BeanPostProcessor,Spring默认会自动创建代理。

Java代码  
  1. // AbstractAutowireCapableBeanFactory
  2. public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
  3. throws BeansException {
  4. Object result = existingBean;
  5. for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
  6. result = beanProcessor.postProcessBeforeInitialization(result, beanName);
  7. if (result == null) {
  8. return result;
  9. }
  10. }
  11. return result;
  12. }
  13. public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
  14. throws BeansException {
  15. Object result = existingBean;
  16. for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
  17. result = beanProcessor.postProcessAfterInitialization(result, beanName);
  18. if (result == null) {
  19. return result;
  20. }
  21. }
  22. return result;
  23. }

 

 

我们来看下AbstractAutoProxyCreator里的重点代码

Java代码  
  1. // AbstractAutoProxyCreator
  2. public Object postProcessBeforeInitialization(Object bean, String beanName) {
  3. return bean;
  4. }
  5. /**
  6. * Create a proxy with the configured interceptors if the bean is
  7. * identified as one to proxy by the subclass.
  8. * @see #getAdvicesAndAdvisorsForBean
  9. */
  10. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  11. if (bean != null) {
  12. Object cacheKey = getCacheKey(bean.getClass(), beanName);
  13. if (!this.earlyProxyReferences.contains(cacheKey)) {
  14. return wrapIfNecessary(bean, beanName, cacheKey);
  15. }
  16. }
  17. return bean;
  18. }
Java代码  
  1. /**
  2. * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
  3. * @param bean the raw bean instance
  4. * @param beanName the name of the bean
  5. * @param cacheKey the cache key for metadata access
  6. * @return a proxy wrapping the bean, or the raw bean instance as-is
  7. */
  8. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  9. if (this.targetSourcedBeans.contains(beanName)) {
  10. return bean;
  11. }
  12. if (this.nonAdvisedBeans.contains(cacheKey)) {
  13. return bean;
  14. }
  15. if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
  16. this.nonAdvisedBeans.add(cacheKey);
  17. return bean;
  18. }
  19. // Create proxy if we have advice.
  20. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  21. if (specificInterceptors != DO_NOT_PROXY) {
  22. // 有AnnotationAwareAspectJAutoProxyCreator 这个processor时
  23. this.advisedBeans.add(cacheKey);
  24. Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  25. this.proxyTypes.put(cacheKey, proxy.getClass());
  26. return proxy;
  27. }
  28. this.nonAdvisedBeans.add(cacheKey);
  29. return bean;
  30. }

Java代码  
  1. protected Object createProxy(
  2. Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
  3. ProxyFactory proxyFactory = new ProxyFactory();
  4. // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
  5. proxyFactory.copyFrom(this);
  6. if (!shouldProxyTargetClass(beanClass, beanName)) {
  7. // Must allow for introductions; can't just set interfaces to
  8. // the target's interfaces only.
  9. Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
  10. for (Class<?> targetInterface : targetInterfaces) {
  11. proxyFactory.addInterface(targetInterface);
  12. }
  13. }
  14. Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  15. for (Advisor advisor : advisors) {
  16. proxyFactory.addAdvisor(advisor);
  17. }
  18. proxyFactory.setTargetSource(targetSource);
  19. customizeProxyFactory(proxyFactory);
  20. proxyFactory.setFrozen(this.freezeProxy);
  21. if (advisorsPreFiltered()) {
  22. proxyFactory.setPreFiltered(true);
  23. }
  24. return proxyFactory.getProxy(this.proxyClassLoader);
  25. }

至于事务切面和其他切面形成切面chain时的调用关系,请参考http://wely.iteye.com/blog/2313924的解释。

  本文并未介绍事务属性、事务状态、事务管理器以及事务自身更底层的一些内容,这些内容留待我们研究了mysql的事务后再详细介绍。


原文链接:[http://wely.iteye.com/blog/2317428]

Spring AOP实现声明式事务代码分析相关推荐

  1. 实现自己的BeanFactory、AOP以及声明式事务

    实现自己的BeanFactory                                                                   在使用spring时,我们很少用& ...

  2. Spring源码——声明式事务流程

    前言 最近回顾了一下Spring源码,准备用思维导图的方式简单的将整个源码内容的流程展示出来,思维导图.图片等文件更新在https://github.com/MrSorrow/spring-frame ...

  3. spring 注解开启声明式事务

    spring开启声明式事务: 导入依赖: pom.xml <dependencies><!-- https://mvnrepository.com/artifact/org.spri ...

  4. spring+mybatis之声明式事务管理初识(小实例)

    前几篇的文章都只是初步学习spring和mybatis框架,所写的实例也都非常简单,所进行的数据访问控制也都很简单,没有加入事务管理.这篇文章将初步接触事务管理. 1.事务管理 理解事务管理之前,先通 ...

  5. spring——使用注解声明式事务整合jdbc——GRUD

    准备阶段: pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...

  6. spring——使用xml声明式事务整合jdbc——GRUD

    准备阶段: pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...

  7. Spring+mybatis整合声明式事务异常之org.springframework.transaction.TransactionTimedOutException: Transaction ..

    1.异常信息 org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline ...

  8. Spring如何用“声明式事务”保护亿万数据安全?【万字解决并发冲突】

    Hello,你好呀,我是灰小猿,一个超会写bug的程序猿

  9. 全面分析 Spring 的编程式事务管理及声明式事务管理(转)

    摘要 Spring 的事务管理是 Spring 框架中一个比较重要的知识点,该知识点本身并不复杂,只是由于其比较灵活,导致初学者很难把握.本教程从基础知识开始,详细分析了 Spring 事务管理的使用 ...

最新文章

  1. 企业网络翻译官——DNS
  2. 概述VB.NET正则表达式简化程序代码
  3. oracle禁止自动启动命令,自动启动和关闭Oracle 脚本
  4. 18个不可不知的有用潜规则
  5. 高性能浏览器网络(High Performance Browser Networking) 第二章
  6. 关于bootstrap和webinf下访问其它文件的方法 2021-04-17
  7. cba篮球暂停次数和时间_CBA一场比赛每支球队可以叫多少次暂停
  8. 正则提取 html 里input 标记的value 值
  9. mysql上线脚本规范_MySQL 的 21 个规范、优化最佳实践!
  10. 【Excel】数据透视表—标签合并居中
  11. 深度学习#1.有监督学习和无监督学习
  12. 微信小程序 绑定手机号获取验证码
  13. PHP全站pjax影响收录,zblogPHP增加pjax功能,大写的一个“帅”字 - 胡言乱语
  14. TCP/IP 之 蓟辽督师
  15. 解决php的“It is not safe to rely on the system’s timezone settings”问题
  16. 什么是网关?使用网关有什么好处
  17. 动态壁纸:酷炫手机壁纸
  18. [转]数据分析与处理之二:Leveldb 实现原理
  19. Java 利用EasyPoi做Excel模板的导入导出操作
  20. select取地区及下级区域_R:民政部官网行政区域代码的爬取

热门文章

  1. C#中的问号运算符简介
  2. javascript闭包新认识
  3. Flash:LoadVars数据提交与表单处理
  4. 使用Jenkins的任务自动跑脚本后发现,服务没有起来
  5. 反病毒软件技术简析与探索(2009年5月18日)
  6. Python sys 使用说明
  7. C#常见算法题目(面试准备)
  8. Script To Monitor RDBMS Session UGA and PGA Current And Maximum Usage Over Time
  9. 问题二十六:C++全局变量的使用实例
  10. 面试题02.07.链表相交