【简记】Java Web 内幕——Spring中的事务操作和底层源码

本章内容:

  • Spring中事务使用
  • 事务的底层操作

事务控制

环境准备(Struts框架)
用户访问—》Action --》 Service—》Dao

一个业务的成功: 调用的service是执行成功的,意味着service中调用的所有的dao是执行成功的。 事务应该在Service层统一控制。

编程式事务控制

自己手动控制事务,就叫做编程式事务控制。
Jdbc代码:
	Conn.setAutoCommite(false);  // 设置手动控制事务
Hibernate代码:
	Session.beginTransaction();    // 开启一个事务
【细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制】
(比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚.)

声明式事务控制

Spring提供了对事务的管理, 这个就叫声明式事务管理。
Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。
Spring声明式事务管理,核心实现就是基于Aop。
【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】
(因为aop拦截的是方法。)

Spring声明式事务管理器类:
	Jdbc技术:DataSourceTransactionManager
	Hibernate技术:HibernateTransactionManager

声明式事务管理

步骤:
1) 引入spring-aop相关的4个jar文件
2) 引入aop名称空间 【XML配置方式需要引入】
3) 引入tx名称空间 【事务方式必须引入】

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    	 http://www.springframework.org/schema/beans/spring-beans.xsd
     	 http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/tx
     	 http://www.springframework.org/schema/tx/spring-tx.xsd">

	
	<!-- 1. 数据源对象: C3P0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
		<property name="initialPoolSize" value="3"></property>
		<property name="maxPoolSize" value="10"></property>
		<property name="maxStatements" value="100"></property>
		<property name="acquireIncrement" value="2"></property>
	</bean>
	
	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 3. dao实例 -->
	<bean id="deptDao" class="cn.itcast.a_tx.DeptDao">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
 
	<!-- 4. service实例 -->
	<bean id="deptService" class="cn.itcast.a_tx.DeptService">
		<property name="deptDao" ref="deptDao"></property>
	</bean>
	
	<!-- #############5. Spring声明式事务管理配置############### -->
	<!-- 5.1 配置事务管理器类 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 5.2 配置事务增强(如果管理事务?) -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="*" read-only="false"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
	<aop:config>
		<aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
	</aop:config>
	
</beans>     

read-only = true;
只读事务的功能,它在整个事务中保证一致性读:在整个事务中的数据在事务开始时就决定,即使有其他会话在事务周期内修改并提交数据,也不会影响事务。可以认为在只读事务周期内,其他事务的对数据的改变就像不存在一样。


注解方式实现

使用注解实现Spring的声明式事务管理,更加简单!
步骤:
1) 必须引入Aop相关的jar文件
2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类
3)在需要添加事务控制的地方,写上: @Transactional

@Transactional注解:
1)应用事务的注解
2)定义到方法上: 当前方法应用spring的声明式事务
3)定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
4)定义到父类上: 当执行父类的方法时候应用事务。

	<!-- 1. 数据源对象: C3P0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
		<property name="user" value="root"></property>
		<property name="password" value="root"></property>
		<property name="initialPoolSize" value="3"></property>
		<property name="maxPoolSize" value="10"></property>
		<property name="maxStatements" value="100"></property>
		<property name="acquireIncrement" value="2"></property>
	</bean>
	
	<!-- 2. JdbcTemplate工具类实例 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 事务管理器类 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="cn.itcast.b_anno"></context:component-scan>
	
	<!-- 注解方式实现事务: 指定注解方式实现事务,说明是jdbc层面的事务 -->
	<tx:annotation-driven transaction-manager="txManager"/>
</beans> 


事务属性
@Transactional(
			readOnly = false,  // 读写事务
			timeout = -1,       // 事务的超时时间不限制
			noRollbackFor = ArithmeticException.class,  // 遇到数学异常不回滚
			isolation = Isolation.DEFAULT,              // 事务的隔离级别,数据库的默认
			propagation = Propagation.REQUIRED			// 事务的传播行为
	)

隔离级别:
这里写图片描述

事务传播行为:
Propagation.REQUIRED
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务, 就会加入当前的事务;

Propagation.REQUIRED_NEW
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。
即两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务。

举例:

   Class Log{
		Propagation.REQUIRED  
		insertLog();  
   }

	Propagation.REQUIRED
	Void  saveDept(){
		insertLog();    // 加入当前事务
		saveDept();
        .. 异常, 日志会回滚
	}
	Class Log{
		Propagation.REQUIRED_NEW  
		insertLog();  
}

	Propagation.REQUIRED
	Void  saveDept(){
		insertLog();    // 始终开启事务
		saveDept();
		.. 异常, 日志不会回滚
	}

事务的源码

TransactionProxyFactoryBean类是代理bean工厂,简化事务处理。

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean  
        implements BeanFactoryAware   

继承AbstractSingletonProxyFactoryBean, 该类提供了创建事务bean代理对象的方法。

public void afterPropertiesSet() {  
        if (this.target == null) {  
            throw new IllegalArgumentException("Property 'target' is required");  
        }  
        if (this.target instanceof String) {  
            throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");  
        }  
        if (this.proxyClassLoader == null) {  
            this.proxyClassLoader = ClassUtils.getDefaultClassLoader();  
        }  
  
        ProxyFactory proxyFactory = new ProxyFactory();  
  
        if (this.preInterceptors != null) {  
            for (Object interceptor : this.preInterceptors) {  
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));  
            }  
        }  
  
        // Add the main interceptor (typically an Advisor).  
        proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));  
  
        if (this.postInterceptors != null) {  
            for (Object interceptor : this.postInterceptors) {  
                proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));  
            }  
        }  
  
        proxyFactory.copyFrom(this);  
  
        TargetSource targetSource = createTargetSource(this.target);  
        proxyFactory.setTargetSource(targetSource);  
  
        if (this.proxyInterfaces != null) {  
            proxyFactory.setInterfaces(this.proxyInterfaces);  
        }  
        else if (!isProxyTargetClass()) {  
            // Rely on AOP infrastructure to tell us what interfaces to proxy.  
            proxyFactory.setInterfaces(  
                    ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));  
        }  
  
        this.proxy = proxyFactory.getProxy(this.proxyClassLoader);/**此处创建了代理对象**/  
    }  

而在该方法中调用了createMainInterceptor方法,该方法很重要,获取到事务的拦截对象,进入到该方法中:

protected Object createMainInterceptor() {  
        this.transactionInterceptor.afterPropertiesSet();  
        if (this.pointcut != null) {  
            return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);  
        }  
        else {  
            // Rely on default pointcut.  
            return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);  
        }  
    }  

这样,当我们调用业务bean的处理方法时,实际上是调用代理对象Interceptor的Invoker方法:

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor,Serializable {  
   
    public Object invoke(final MethodInvocationinvocation) throws Throwable {  
                                                                                                                                                  .......  
              //就在这一句开启事务  
              TransactionInfotxInfo = createTransactionIfNecessary(tm, txAttr,joinpointIdentification);  
              ......  
              retVal= invocation.proceed();   //执行下一个拦截器 或 执行代理目标的方法  
              ......                                                                                                                                        
              commitTransactionAfterReturning(txInfo);  <span style="font-family: Arial, Helvetica, sans-serif;">//提交事务           </span>  
                                                                     
              return retVal;  
              .......  
    }   
}  

该方法中实现了真正的在业务bean方法中加入事务处理,现在我们来分析下这段源码。
我们注意到这段代码:TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification),该代码就决定了是否启用事务处理。深入到该方法内部,
该方法调用了status = tm.getTransaction(txAttr),这里就是获取事务的关键部分。进入该方法的实现:

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {  
 ......  
   public final TransactionStatus getTransaction(TransactionDefinitiondefinition) throws TransactionException {  
              </span>  
              //这里是取得当前已绑定到TransactionSynchronizationManager的上下文的事务,主要为事务传播行为而设  
              Object transaction =doGetTransaction();  
              ........  
              //如果事务已存在,根据事务传播行为来处理,  
              if(isExistingTransaction(transaction)) {  
                     // Existingtransaction found -> check propagation behavior to find out how to behave.  
                     return handleExistingTransaction(definition, transaction, debugEnabled);  
              }  
              ......  
              //这里最关键,开启一个新的事务,并放进TransactionSynchronizationManager的上下文中,并绑定到当前线程)  
              doBegin(transaction,definition);  
              return status;  
              .......  
       }  
......  
}  

AbstractPlatformTransactionManager抽象类是spring事务处理中的一个核心类,该类实现接口PlatformTransactionManager,他们之间的关系如下图示
这里写图片描述

如上图所示,spring实现了几种方式的事务处理,其中有JTA,DataSource,Hibernate,JDO,JPA,CCI。现在我们进入HibernateTransactionManager的doGenTransaction的方法内部,查看他是如何处理的,其他的几个都是类似的。  
public class HibernateTransactionManager extends AbstractPlatformTransactionManager  
        implements ResourceTransactionManager, InitializingBean {  
  
    protected Object doGetTransaction() {  
        HibernateTransactionObject txObject = new HibernateTransactionObject();  
        ......  
  
        //在TransactionSynchronizationManager的上下文中查找当前的Session(实质上也是与线程绑定的)  
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());  
  
        ......  
        return txObject;  
    }  
  
}  

该方法尝试获取当前已绑定到TransactionSynchronizationManager的上下文的事务,主要为事务传播行为而设定。然后进入doBegin()方法,该方法尝试开启一个新的事务,并放进TransactionSynchronizationManager的上下文(ThreadLocal的Map)中,绑定到当前线程中(线程的ThreadLocal变量中)。

public class HibernateTransactionManager extends AbstractPlatformTransactionManager  
        implements ResourceTransactionManager, InitializingBean {  
......  
     protected void doBegin(Object transaction, TransactionDefinition definition) {  
                HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;  
        .........  
        try {  
            if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {  
                //这里才真正的打开Session  
                Session newSession = SessionFactoryUtils.openSession(getSessionFactory());  
                .........  
            }  
            session = txObject.getSessionHolder().getSession();  
                        .........  
            Transaction hibTx;  
            // Register transaction timeout.  
            .........  
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {  
                .........  
            }  
            else {  
                //这里才真正地开启事务  
                hibTx = session.beginTransaction();  
            }  
            .........  
                        //如果这新开启的Session,则将SessionHolder(Session和Transaction)放到TransactionSynchronizationManager的上下文中(绑定到当前线程)  
            // Bind the session holder to the thread.  
            if (txObject.isNewSessionHolder()) {                                                                                                
                                //以键值对<SessionFactory,SessionHolder>形式绑定到TransactionSynchronizationManager的上下文中  
                TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());  
            }         
            .......  
        }  
        .........  
     }  
......  
}  

主要是理解TransactionSynchronizationManager, 要懂得Spring的TransactionManager开启事务后是以键值对<SessionFactory,SessionHolder>形式 存放到 TransactionSynchronizationManager的上下文(ThreadLocal的Map)中(以ThreadLocal的方式与当前线程绑定)。

在获取Session,开启事务后,又回到TransactionInterceptor 的Invoker方法中,进行实际业务的处理:retVal= invocation.proceed(),处理完之后,则开始提交事务或者回滚事务。  

成功后提交事务:commitTransactionAfterReturning(txInfo),进入该方法内部就可发现是调用了PlatformTransactionManager.commin方法进行提交。  

失败后回滚事务:completeTransactionAfterThrowing(txInfo, ex)也是类似。  

至此Spring的基于动态代理方式实现事务处理的流程就讲完了。  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值