Spring3.1.0实现原理分析(二十).Dao事务分析之非入侵式编码

       大家好,从这篇博客开始我们分析下Spring的事务模块。Spring事务的其中一个优点是非入侵式的编码,那么非入侵是怎么实现的呢?其实就是通过Spring Aop实现的。关于AOP我之前写过两篇博客,大家最好先看下,对于理解本文有帮助。

      下面是一个Spring事务的例子,在之前分析AOP的博客中我也举过一个例子,并且详细分析了每个Bean对象的创建过程。所以下面这个例子关于Bean的创建我只是简单把几个关键点写一下。       

一. 创建对象

<!-- 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
...
</bean>
<!-- 配置局部事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置一个业务逻辑Bean -->
<bean id="newsDao" class="testtx.tx.org.crazyit.app.dao.impl.NewsDaoImpl">
<property name="ds" ref="dataSource"/>
</bean>
<!-- 配置事务增强处理,指定事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut id="myPointcut" expression="execution(* testtx.tx.org.crazyit.app.dao.impl.*Impl.*(..))"/>
<!-- 定义通知器 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>

   在这个例子中一共有7个Bean对象需要创建,它们的ID如下(为了便于描述我对ID进行了简化处理),

   1. dataSource

   2. transactionManager

   3. newsDao

   4. txAdvice

   5. internalAutoProxyCreator       (用于创建代理对象的bean后处理器,该对象是Spring检测到存在aop配置自动添加的)

   6. myPointcut                             (切入点)

   7. DefaultBeanFactoryPointcutAdvisor#0  (通知器)

     首先在初始化阶段,Spring发现有AOP相关配置,于是会自动注册一个Bean后处理器“internalAutoProxyCreator”,类型是AspectJAwareAdvisorAutoProxyCreator

     然后创建dataSource,在实例化dataSource之前会进入AspectJAwareAdvisorAutoProxyCreator的postProcessBeforeInstantiation方法,在这个方法中创建所有通知器,本例中只有一个通知器“DefaultBeanFactoryPointcutAdvisor#0”进入填充对象属性值时,会给它的两个属性“adviceBeanName”和“pointcut”赋值,值分别是“txAdvice(字符串)”和“AspectJExpressionPointcut(就是myPointcut的实例,是个property bean)”。

     然后继续创建dataSource对象,在完成了创建dataSource对象的初始化步骤后,会调用Bean后处理器的postProcessAfterInitialization方法,而上述AspectJAwareAdvisorAutoProxyCreator就是一个Bean后处理器,于是在它的postProcessAfterInitialization方法中会判断是否有必要为dataSource创建代理对象,判断是由通知器的切入点对象完成的,切入点对象持有aspectj表达式,本例中是“* testtx.tx.org.crazyit.app.dao.impl.*Impl.*(..)”,显然无需为dataSource创建代理对象,至此dataSource创建完成。

     然后创建transactionManager对象,它的过程跟dataSource是相似的,无非通知器此时已创建,然后transactionManager也不在被代理的范围之内。再接着创建newsDao重点来了,newDao是匹配aspectj表达式的,需要为他创建代理对象,此时会触发创建通知对象"txAdvice",它的类型是TransactionInterceptor,就是一个Aop大联盟的方法拦截器,Spring的通知对象即是拦截器,这个观点我之前也表述过。在填充txtAdvice属性值阶段,会为它的两个属性“transactionManager”和“transactionAttributeSource”赋值,前者的值就是transactionAttributeSource,该对象已存在;后者的值是NameMatchTransactionAttributeSource,这个对象的作用是获取事务属性对象,这是一个单例Bean,需要创建,当进入填充该对象的属性值阶段,会为它属性nameMap赋值,它的值就是用户配置的事务属性,key是方法名称,value是事务属性对象,可见不同的方法对应不同的事务配置

{get*=PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly, *=PROPAGATION_REQUIRED,ISOLATION_DEFAULT}。

    我之前的博客说过,通知定义的顺序不一定是执行顺序,假设事务通知,前置通知,后置通知,最终通知,异常后通知都存在的情况,通知的执行顺序如下,

  • TransactionInterceptor   (事务通知)                    
  • MethodBeforeAdviceInterceptor  (前置通知)
  • AspectJAfterThrowingAdvice  (异常后通知)
  • AspectJAfterAdvice  (最终通知)
  • AfterReturningAdviceInterceptor  (后置通知)
   通过上述的过程,就完成了创建newsDao的代理对象,调用该类的所有方法都会被拦截,从而织入事务控制逻辑。

二. 简述Spring事务

       所谓Spring事务其实就是ORM框架把事务管理这部分工作委托给Spring来完成,自己不再处理事务相关操作,用张图来描述下,图中用红色字体表述的事务操作就是通过AOP织入的。

    

       Spring事务管理是由事务管理器来负责的,所有的事务管理器都实现了PlatformTransactionManager接口,该接口中定义三个方法:(1). 获取事务对象;(2).提交事务;(3):回滚事务。其中比较常用的一个事务管理器是DataSourceTransactionManager,这里简单描述下。这是一个基于数据库连接对象的事务管理器,它持有单个数据库连接,通过连接实现事务提交和回滚,它只支持局部事务。当Dao层的类执行数据库操作时,会被TransactionInterceptor拦截,拦截后所做的核心工作就是要创建一个jdbc连接对象,把连接对象设置成手动提交,并把它保存到ThreadLocal变量中,目的是要确保执行CRUD时使用的是同一个连接对象,连接的存储和获取具体是通过TransactionSynchronizationManager对象实现的。当CRUD执行结束后,调用连接对象的commit()或rollback()方法。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值