上文交代了spring事务的一些基本的概念和比较重要的类。然后通过问题的方式先粗略的交代一些spring的事务一些方面。本文将从spring tx在spring系统中如何生效这个角度来思考spring事务这件事情。
说道事务,首先是联想到了数据库。数据库会根据我们设置的事务属性去做事务这件事情。那么,我们如何将事务配置到spring体系中的呢?
1、注解形式
xml中启动@Transactional注解扫描
<tx:annotation-driven transaction-manager="transactionManager" />
@Transactional(propagation=Propagation.REQUIRED)
public void save(User user){
xxxx
}
2、代理工厂形式
<bean id="proxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 为事务代理工厂Bean注入事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 要在哪个Bean上面创建事务代理对象 -->
<property name="target" ref="productDao" />
<!-- 指定事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
3、aop形式
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut"
expression="XXXX" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="pointcut" />
</aop:config>
不管是aop实现的也好,还是我们自己生成代理类采用事务拦截器实现事务也好,其实思想都是一致的。下面我们着重说说aop事务代理是如何实现的。上面的第一种和第三种都是使用了aop去实现。我们将从以下事务和aop分别都做了什么,然后再结合两者看看事务是如何实现的。
拿@Transaction来举例。注解形式包含两个方面
1、事务开启标签配置
2、方法上的事务标签
事务开启做了什么?
从标签解析器中我们不难发现事务开启标签做了以下这件事情 --- 将标签解析成adivosr的形式注入到spring容器中。
那么当我们在方法头上写了事务标签的时候如何感知事务的呢?
1、注解解析
在我们将标签解析成adivosr的过程中,我们也将一个解析类SpringTransactionAnnotationParser注入到了注解标签中。
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = new LinkedHashSet<>(2);
this.annotationParsers.add(new SpringTransactionAnnotationParser());
if (jta12Present) {
this.annotationParsers.add(new JtaTransactionAnnotationParser());
}
if (ejb3Present) {
this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
}
}
该注解解析器能够将@Transaction标签解析成类实例TransactionAttribute
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
if (ae.getAnnotations().length > 0) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
return attr;
}
}
}
return null;
}
拦截器会找到这个方法去获得相应的事务属性。
2、什么时候将有注解的类包装成了代理 有了事务属性,也有了切点和拦截器。接下来就是aop要做的事情了。
spring在解析xml的时候会找到类BeanNameAutoProxyCreator,该类的父类实现了接口BeanPostProcessor,那么在每一个类初始化的时候都要走该方法。
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
该方法首先找到容器中的advisor类型的实例,然后根据切点筛选该bean符合要求的advisor。如果找到的advisor不为空,则创建对象的aop代理返回。
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
//在ConfigurableListableBeanFactory类型的容器中,将被代理类的bean属性AutoProxyUtils.originalTargetClass设为为被代理类类。
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
//代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//advisor数组
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//遍历advisor数组,将其放置到代理工厂中
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
//设置被代理源
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
//代理是否被冻结属性设置
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//创建该bean的代理。
return proxyFactory.getProxy(getProxyClassLoader());
}
该类还有一个地方创建了代理
这个地方是工厂在创建对象,获得指定bean的早起访问,为了解决循环引用的问题。
至此,那些配置到BeanNameAutoProxyCreator类中的service类将被代理成了apring的aop代理了。那么就差最后一步,在这些代理类被访问的时候,aop是该如何做才走了事务逻辑呢?
3、aop代理解析过程
由于service一般都有实现了接口类,所以代理是走的jdk代理。接下来的逻辑我都只看jdk代理。
JdkDynamicAopProxy类实现了InvocationHandler,所以当代理类被访问的时候,会首先进入方法invoke中。
事务的拦截器不是InterceptorAndDynamicMethodMatcher类型的,那么会直接执行事务拦截器逻辑。
至此aop就完成了事务逻辑的处理。
总结下整个过程:
1、配置文件将aop的advice、pointcut注入到spring容器中。使用类BeanNameAutoProxyCreator 将要进行事务逻辑的类生产aop代理。
2、在访问aop代理类的时候会走拦截器链,当走到事务拦截器时会生产事务属性,根据事务属性生成事务。
3、拦截器链继续执行直到结束,事务拦截器会根据返回的结果类型,如果报错则根据事务属性配置回滚事务,如果顺利完成则根据事务属性将事务从当前线程中删除。
4、最后提交事务,操作数据库的connection.commit方法将我们执行的sql语句提交到数据库中,完成所有操作。
参考文献
https://my.oschina.net/pingpangkuangmo/blog/416038
欢迎加入qq群: 122567875 欢迎关注微信公众号:hyssop的后花园