<!-- 声明式事务 -->
<bean name="txmanager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
	<!-- 事务开启必须使用session -->
	<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
 
<!-- 事务增强类 
transaction-manager:这种机制要放到上面哪一个bean中,
transaction-manager如果不写有一个默认值transactionManager,此时必须保证声明式事务中的bean的名字是transactionManager
-->
<tx:advice id="txAdvice" transaction-manager="txmanager">
	<tx:attributes>
		<!-- REQUIRED: 第一种事务声明方式,需要事务
			SUPPORTS:第四种声明方式,不需要事务-->
		<tx:method name="save*" propagation="REQUIRED"/>
		<tx:method name="update*" propagation="REQUIRED"/>
		<tx:method name="delete*" propagation="REQUIRED"/>
		<tx:method name="get*" propagation="SUPPORTS"/>
		<tx:method name="find*" propagation="SUPPORTS"/>
		<!-- 匹配以上之外的所有方法,类似于switch的default -->
		<tx:method name="*" propagation="SUPPORTS"/>
	</tx:attributes>
</tx:advice>
 
<!-- 使用AOP切面,开始把事务添加到切面中 -->
<aop:config>
	<!-- 表达式:返回类型模式是*,它代表了匹配任意的返回类型
	应用到service下所有实现类的所有类的所有方法所有参数 -->
	<aop:pointcut expression="execution(* com.bdqn.ssh.service.impl.*.* (..))" id="mypoint"/>
	<!-- 织入 把管理类[txmanager]生成的代理[txAdvice],织入到所定义的切面[mypoint]中 -->
	<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"/>
</aop:config>

一、前言

本节我们来谈谈 <tx:advice/>、<aop:config> 标签如何创建事务切面的。

二、 <tx:advice/>、<aop:config>标签如何创建事务切面的

首先 <tx:advice/> 标签作用是创建一个TransactionInterceptor,作为事务切面的通知方法 在Spring中(可以参考:https://gitbook.cn/gitchat/activity/5a8fdf6bf2e5dc2ca621a937) Advisor这个概念是从 Spring 1.2的 AOP 支持中提出的,一个 Advisor 相当于一个小型的切面,不同的是它只有一个通知(Advice),Advisor中还包含一个pointcut(切点),切点定义了对那些方法进行拦截,而通知是具体对拦截到的方法进行增强的逻辑。

具体对 <tx:advice/> 标签进行解析的是TxAdviceBeanDefinitionParser,其时序图如下:

spring 切面获取返回结果 spring切面实现原理_时序图

首先TxAdviceBeanDefinitionParser有getBeanClass方法代码代码:


 

1.    protected Class<?> getBeanClass(Element element) {
2.        return TransactionInterceptor.class;
3.    }

这说明该标签解析后生成的是TransactionInterceptor对象的bean定义。

  •  
    其中时序图中步骤(2)是设置配置demo的XML配置文件里面创建的事务管理器到TransactionInterceptor对象,
  • spring 切面获取返回结果 spring切面实现原理_时序图_02

  •  
  •  
    时序图(4)~(10)则解析 <tx:advice/>标签中事务属性值设置到TransactionInterceptor对象里面属性里面。
     

注:也就是 <tx:advice/>标签的作用是生成一个TransactionInterceptor拦击器对象,并设置该对象的一些事务属性,然后该对象将作为事务切面的通知方法。

然后 <aop:config>标签作用是创建一个DefaultBeanFactoryPointcutAdvisor(其实现了Advisor接口)对象作为作一个Advisor,前面说了一个Advisor就是一个小型的切面,所以其中定义了切点和通知。该标签是ConfigBeanDefinitionParser类进行解析的,其时序图如下:

spring 切面获取返回结果 spring切面实现原理_spring 切面获取返回结果_03

  •  
    时序图中步骤(2)创建了一个DefaultBeanFactoryPointcutAdvisor对象的bean定义,步骤(3)(4)则是设置上面创建的通知对象(TransactionInterceptor)到该Advisor
     
  •  
    时序图中步骤(8)则是解析标签中的切点表达式,然后设置到DefaultBeanFactoryPointcutAdvisor对象的bean定义。
     
  •  
    时序图步骤(4)注册了一个AspectJAwareAdvisorAutoProxyCreator到Spring容器,作用就是对满足pointcut表达式的类的方法进行代理,并且使用advice进行拦截处理,而advice就是事务拦截器。
     

由于AspectJAwareAdvisorAutoProxyCreator类实现了BeanPostProcessor接口,所以具有postProcessAfterInitialization方法,而对符合切点的方法进行代理就是在该方法内的wrapIfNecessary方法:


 

1. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
2.        ...
3.        // 8.1
4.        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
5.        if (specificInterceptors != DO_NOT_PROXY) {
6.            this.advisedBeans.put(cacheKey, Boolean.TRUE);
7.            //8.2
8.            Object proxy = createProxy(
9.                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
10.            this.proxyTypes.put(cacheKey, proxy.getClass());
11.            return proxy;
12.        }
13.  
14.        this.advisedBeans.put(cacheKey, Boolean.FALSE);
15.        return bean;
16.    }

其中8.1查找所有可以对当前bean进行增强的切面,其中有一个条件就是看那些bean实现了Advisor接口,而 <aop:config>标签作用是创建一个DefaultBeanFactoryPointcutAdvisor,并且其实现了Advisor接口,所以这里会使用DefaultBeanFactoryPointcutAdvisor切面,然后会看当前bean的方法是否满足切面的切点表达式,具体是AopUtils的canApply方法进行判断:

spring 切面获取返回结果 spring切面实现原理_Boo_04

如果满足则执行8.2对方法进行代理,这里会对TestTransactionProgagationUserImpl、TestTransactionProgagationCourseImpl、UserManagerBoImpl类的所有方法进行事务代理。

注:Spring框架中一个 Advisor 相当于一个小型的切面, <tx:advice/>定义了这个切面的通知方法,而 <aop:config>具体定义了一个Advisor切面,并且内部定义了一个切点,并且引入了 <tx:advice/>定义的通知方法