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

xiaoxiao2021-02-28  91

本章内容:

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的基于动态代理方式实现事务处理的流程就讲完了。
转载请注明原文地址: https://www.6miu.com/read-46287.html

最新回复(0)