文档章节

spring事务之二 --spring事务的aop实现

hyssop
 hyssop
发布于 2016/09/21 18:21
字数 1389
阅读 231
收藏 0

上文交代了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的后花园

© 著作权归作者所有

上一篇: baidu面试
下一篇: spring 事务(1)
hyssop
粉丝 19
博文 102
码字总数 111521
作品 0
昌平
程序员
私信 提问
【每日提高之声明式事物】spring声明式事务 同一类内方法调用事务失效

【问题】 Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring AOP啊,事务管理真轻松啊,真轻松;事务管理代码没有了,脑不酸了,手不痛了,一口气全配上了事务;轻量级,测...

卯金刀GG
2018/08/01
0
0
Spring 声明式注解事务实现机制

Spring中注解事务实现机制 在使用@Transactional 注解管理事务时步骤很简单。但是如果对@Transactional理解不够透彻,很容易出现事务不起作用的情况。所以,在对@Transactional的实现机制要有...

狂奔的熊二
2018/09/21
0
0
hibernate persist update 方法没有正常工作(不保存数据,不更新数据)

工程结构 问题描述 在工程中通过spring aop的方式配置事务,使用hibernate做持久化。在代码实现中使用hibernate persit()方法插入数据到数据库,使用hibernate update()方法更新数据。问题是...

xiaoheike
2016/04/15
0
0
哪些方法不能够实施Spring AOP事务

哪些方法不能够实施Spring AOP事务 由于Spring事务管理是基于接口代理或动态字节码技术。通过AOP实施事务增强。 基于接口动态代理的AOP事务增强,接口方法必须都是public的。 实现类的方法也...

hyssop
2014/12/06
0
0
Spring AOP 日志拦截器的事务管理

如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信 息,这些操作通过调用一个服务类的方法来执行...

哲别0
2018/05/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【阴阳师】真蛇10层记录

蛇切黑体系 追月神 散件一速,速度越高越好(220+) 镰鼬 招财二速,速度211以上; 山兔 火灵三速,速度180-200均可; 丑女 心眼四速,速度170左右即可; 大蛇 蚌精暴击套。速度高于阴阳师即...

Areya
16分钟前
3
0
js动态设置元素高度

this.$refs.xxx.style.height= this.contentHeight; 元素需要绑定

Carbenson
53分钟前
2
0
今天的学习

今天学到了ci框架中的查询语句的where条件语句: 1、$this->db->select('')->from('')->where('id = ??')->get()->result_array();2、$this->db->select('')->from('')->where('id', '??'......

墨冥
今天
2
0
MySQL在高并发下的订单撮合、系统使用、共享锁与排他锁保证数据一致性

前序 距离上次择文发表,两月余久。2018年也即将要结束了,目前的工作依然是与区块链应用相关的,也很荣幸在9月初受邀签约出版暂名为《区块链以太坊DApp实战开发》一书,预计在明年年初出版。...

我最喜欢三大框架
今天
2
0
深入理解Flutter多线程

该文章属于<简书 — 刘小壮>原创,转载请注明: <简书 — 刘小壮> https://www.jianshu.com/p/54da18ed1a9e Flutter默认是单线程任务处理的,如果不开启新的线程,任务默认在主线程中处理。 ...

刘小壮
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部