Spring 事物与底层原理

一. 事物的解释,及使用案例

解释: 假设同一方法中对数据库执行增删改操作,在操作数据库完毕的后续代码中发生了异常,在实际情况中按照业务逻辑由于抛出异常,此次操作是不应该对数据库有任何影响的,要对抛出异常前对数据库的修改回滚掉

实现事物设置的步骤

  1. 配置连接数据库的数据源
  2. 配置事物管理器
  3. 设置开启事物
  4. 设置需要开启事物的方法,做到方法报错数据回滚(也就是对数据库增删改操作的方法)
  5. 注意点,通常使用 mybatis 在配置mybatis时还需要配置扫描映射数据库表进行增删改查的 mapper 接口,与mapper.xml
  • 以整合 mybatis 框架举例传统xml方式配置
    mybatis 配置
<?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:context="http://www.springframework.org/schema/context"
	   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">
	<!-- 加载属性文件 -->
	<context:property-placeholder location="classpath:properties/*.properties"/>

	<!-- 配置数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<!-- 使用Druid无需配置driver,会自动的根据url得到driver -->
		<!-- 配置url ${jdbc.url}从属性文件获取 -->
		<property name="url" value="${jdbc.url}"/>
		<!-- 配置用户名 -->
		<property name="username" value="${jdbc.username}"/>
		<!-- 配置数据库密码 -->
		<property name="password" value=""/>
	</bean>

	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="classpath:mybatis/config.xml"/>
		<property name="dataSource" ref="dataSource"/>
		<!-- 配置自动扫描mybatis Mapper.java Mapper.xml -->
		<property name="mapperLocations" value="classpath:mapper/*.xml"/>
	</bean>

	<!--扫描对应mapper.xml的映射接口-->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 扫描包 -->
		<property name="basePackage" value="com.ssm.dao" />
		<!-- sqlSessionFactoryBeanName是批factory的名称 此处使用value,而不是ref -->
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
	<!--init-method : 指定 bean 的初始化方法
		destroy-method : 指定 bean 的销毁方法-->
	<bean id="hh" class="com.ssm.entity.HelpCategory" lazy-init="false" >
		<property name="name" value="aaaa"/>
		<property name="url" value="bbbb"/>
	</bean>
</beans>

开启事物,与事物管理器等

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	
	<!--配置开启事物-->
	<tx:annotation-driven/>

	<!-- 配置事务管理器,如果使用其它框架,例如Hb 还需要配置其它管理器 -->
   	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   		<!-- 数据源 -->
   		<property name="dataSource" ref="dataSource"></property>
   	</bean>

   
   <!-- 配置事务通知 -->
   <tx:advice id="txAdvice" transaction-manager="transactionManager">
   	<tx:attributes>
   		<!-- 
   		REQUIRED:支持当前事务,如果没有事务,就创建一个事务。建议使用。
   		MANDATORY:支持当前事务,如果没有事务,就抛出异常。
   		NESTED:嵌套事务,允许在当前事务中嵌套事务,如果没有事务,会创建一个。
   		NEVER:强制不使用事务,如果当前有事务,则抛出异常。
   		NOT_SUPPORTED:强制不使用事务,如果当前有事务,则将当前事务挂起。
   		REQUIRES_NEW:创建一个新的事务,如果当前已有事务,则挂起当前事务。
   		SUPPORTS:支持当前事务,如果没有事务,不做任何处理。
   		 -->
   		<tx:method name="insert*" propagation="REQUIRED"/>
   		<tx:method name="save*" propagation="REQUIRED"/>
   		<tx:method name="delete*" propagation="REQUIRED"/>
   		<tx:method name="update*" propagation="REQUIRED"/>
   		<tx:method name="select*" read-only="true"/>
   		<tx:method name="find*" read-only="true"/>
   		<tx:method name="*" propagation="REQUIRED"/>
   	</tx:attributes>
   </tx:advice>
   
   <!-- 配置aop,切面 -->
   <aop:config>
		<!-- 第一个*表示所有返回值  第二个*表示所有类 第三个*表示所有的方法   ..表示所有的传参类型 -->
		<aop:pointcut expression="execution(* com.ssm.service.*.*(..))" id="pointcut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
   </aop:config>

</beans>
  • 基于注解配置(此处没有配置扫描 mapper 接口与 mapper.xml 只是做了事物相关的示例)
//基于注解开启事物功能
@EnableTransactionManagement
@Configuration
@PropertySource(value = {"classpath:/properties/jdbc.properties"})
public class MyConfiguration {


    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String name;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 配置数据源
     * @return
     */
    @Bean
    public DruidDataSource configDruidDataSource(){

        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(url);
        druidDataSource.setName(name);
        druidDataSource.setPassword(password);
        return  druidDataSource;
    }

    /**
     * 配置事物管理器
     * @return
     */
    @Bean
    public PlatformTransactionManager configTransactionManager(){

        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(configDruidDataSource() );
        return  transactionManager;
    }
    
}
  • 需要使用事物的方法上使用 @Transaction 修饰
 	/**
     * 使用 @Transaction 对方法添加事物
     * @param helpCategory
     * @return
     */
    @Transactional
    public String saveReturnKey(HelpCategory helpCategory){
        return helpCategoryDao.saveReturnKey(helpCategory);
        //假设此处还有操作,在此处发生了异常,由于添加了事物,前面对数据库的操作就会回滚
    }
}

@Transactional 注解属性解释

在这里插入图片描述

事物的传播

@Transaction 默认传播机制为 Propagation.REQUIRED 支持当前事务,如果当前没有事务,则新建事务
如果当前存在事务,则加入当前事务,合并成一个事务

在这里插入图片描述
注意几种情况:

  1. 在同一个事物方法内部调用其它方法,外部方法报错,内部方法也会回滚,假设内部方法是一个用来专门记录的,不想回滚,使用使用 Propagation.NOT_SUPPORTED 以非事物方式执行,如果当前有事物,则挂起
  2. 内部事物不影响外部事物,外部事物影响内部事物,Propagation.NESTED嵌套事物 如果当前存在事务,则使用 SavePoint 技术保存当前事务状态进行,然后底层共用一个连接,内部出错的时候,自行回滚到 SavePoint这个状态,只要外部捕获到了异常,可以继续进行外部的事务提交,如果外部事务抛出了异常,整个大事务都会回滚。
  3. 外部事物,不影响内部事物,内部事物也不影响外部事物,Propagation.REQUIRES_NEW新建一个事务,如果当前有事务,是将当前事务挂起,自己新创建一个事务,新事务执行完毕后再唤醒当前事务,两个事务没有依赖关系
  4. NESTED 与 REQUIRES_NEW 的区别,两个相同的地方是内部事物回滚不会影响外部事物,由于NESTED 是通过SavePoint 保存外部事物,嵌套内部事物公用一个连接,在内部事物中可以读取外部事物未提交的数据,REQUIRES_NEW 是通过挂起外部事物新建一个互补影响的新事物方式,内部事物中读取不到外部事物的未提交数据,并且外部事物与内部事物共同操作一条数据时可能会造成死锁,或者脏读,尽量避免使用REQUIRES_NEW

事物的特性(ACDI)

原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性: 执行事务前后,数据保持一致;
隔离性: 并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;
持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响

事物的隔离级别

@Transaction 注解默认的隔离级别是 Isolation.DEFAULT 意思是默认使用数据库配置的隔离级别
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ 可重读,可解决掉脏读和不可重复读带来的问题,不能解决掉幻读

1. 了解事物中可能存在的问题

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。
  • 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务读取了数据,接着另一个并发事务插入了数据。在随后的查询中,第一个事务就会发现多了一些原本不存在的记录,就好像发生了幻觉一样

2. 隔离级别

READ-UNCOMMITTED 读取未提交: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
READ-COMMITTED 读取已提交: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
REPEATABLE-READ 可重读: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE 可串行化: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
在这里插入图片描述

二. 根据 @EnableTransactionManagement 开启事物注解,了解事物的实现原理

在这里插入图片描述
查看 @EnableTransactionManagement 注解源码,发现使用 @Import 注解向容器中注入了一组 ban(具体了解通过 @Import 注入), 查看Import 中传递的 TransactionManagementConfigurationSelector , 查看该类的源码,得出相容器中注入了

  • AutoProxyRegistrar
  • ProxyTransactionManagementConfiguration
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    public TransactionManagementConfigurationSelector() {
    }

    protected String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        //@EnableTransactionManagement中该属性值默认是 PROXY, 
        //将 AutoProxyRegistrar 与 ProxyTransactionManagementConfiguration 注入到了容器中
        case PROXY:
            return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration"};
        default:
            return null;
        }
    }
}

分析注入的 AutoProxyRegistrar

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    private final Log logger = LogFactory.getLog(this.getClass());

    public AutoProxyRegistrar() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
        Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
        Iterator var5 = annoTypes.iterator();

        while(var5.hasNext()) {
            String annoType = (String)var5.next();
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
            Object mode = candidate.get("mode");
            Object proxyTargetClass = candidate.get("proxyTargetClass");
            if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {
                candidateFound = true;
                if (mode == AdviceMode.PROXY) {
                	//默认情况下 mode 为"PROXY",并且proxyTargetClass 为 false执行该方法, 通过该方法向容器中注入了 InfrastructureAdvisorAutoProxyCreator 
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean)proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }

        if (!candidateFound) {
            String name = this.getClass().getSimpleName();
            this.logger.warn(String.format("%s was imported but no annotations were found having both 'mode' and 'proxyTargetClass' attributes of type AdviceMode and boolean respectively. This means that auto proxy creator registration and configuration may not have occurred as intended, and components may not be proxied as expected. Check to ensure that %s has been @Import'ed on the same class where these annotations are declared; otherwise remove the import of %s altogether.", name, name, name));
        }

    }
}

该类继承了 ImportBeanDefinitionRegistrar 接口,通过@Import 的手动注入了解到,通过向 @Import 注解中传递实现了ImportBeanDefinitionRegistrar 接口的类,可以自动执行该类中重写接口的 registerBeanDefinitions() 方法,通过该方法向容器中注入 bean,所以,AutoProxyRegistrar 这个类只是判断注入功能,在默认情况下 mode == AdviceMode.PROXY 调用 registerAutoProxyCreatorIfNecessary()方法将 InfrastructureAdvisorAutoProxyCreator 这个类注入到容器中

分析通过 AutoProxyRegistrar 向容器中注入的 InfrastructureAdvisorAutoProxyCreator

查看该类的继承关系

InfrastructureAdvisorAutoProxyCreator
--------->AbstractAdvisorAutoProxyCreator
--------------->AbstractAutoProxyCreator
---------------------->ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware 

间接继承 SmartInstantiationAwareBeanPostProcessor 接口说明该类是一个后置处理器,并且这个后置处理器毕 BeanPostProcessor 多了一个创建对象前要执行的方法
间接继承 BeanFactoryAware 接口说明该类在初始化时会获取 Spring 提供的一个 BeanFactory
(与前面AOP 中的相同,具体可以参照AOP底层源码分析)
将这个后置处理器创建注入到容器中后,后续再创建其bean注入到容器中时,会被这个后置处理器监控到,执行创建 bean 以前的方法,判读是否需要对这个 bean 进行增强,当这个 bean 创建成功后,这个后置处理器监控到,执行初始化 bean 以后的方法,创建这个 bean 的代理对象,注入到容器中

分析注入的 ProxyTransactionManagementConfiguration

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
    public ProxyTransactionManagementConfiguration() {
    }

	//向容器中注入一个事物增强器,这个事物增强器需要设置事物属性,事物拦截器
    @Bean(
        name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
    )
    @Role(2)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        //设置事物属性
        advisor.setTransactionAttributeSource(this.transactionAttributeSource());
        //设置事物拦截器
        advisor.setAdvice(this.transactionInterceptor());
        advisor.setOrder((Integer)this.enableTx.getNumber("order"));
        return advisor;
    }

	//向容器中注入事物属性,通过这个事物属性可以解析我们的@Transaction注解
    @Bean
    @Role(2)
    public TransactionAttributeSource transactionAttributeSource() {
    	//通过空参构造器创建事物属性对象,查看该空参构造器,会发现在构造器中调用 this(true) 带参构造器,
    	//在该构造器中会创建注解解析器,解析事物注解"@Transactional"
        return new AnnotationTransactionAttributeSource();
    }
	
	//向容器中注入事物拦截器
    @Bean
    @Role(2)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        //获取事物属性
        interceptor.setTransactionAttributeSource(this.transactionAttributeSource());
        if (this.txManager != null) {
        	//获取我们配置的事物管理器
            interceptor.setTransactionManager(this.txManager);
        }

        return interceptor;
    }
}

查看源码发现,该类使用 @Configuration 修饰,并且提供了三个使用 @Bean 修饰的方法,可以了解到,该类也是一个注入类,通过该类,想容器中注入了三个 bean, BeanFactoryTransactionAttributeSourceAdvisor 事物增强器, AnnotationTransactionAttributeSource事物属性, TransactionInterceptor 事物拦截器

AnnotationTransactionAttributeSource 事物属性

查看ProxyTransactionManagementConfiguration 中的 transactionAttributeSource() 方法,创建事物属性对象注入到容器中
创建事物属性对象时,会创建一个注解解析器, 默认创建 Spring 事物注解解析器,也可以创建 jpa 事物解析器,与EJB事物解析器,通过这个事物解析器可以解析 “@Transactional” 注解的所有属性信息等

TransactionInterceptor 事物拦截器

查看ProxyTransactionManagementConfiguration 中的 transactionInterceptor() 方法,创建事物拦截器 TransactionInterceptor,注入到容器中
创建事物拦截器,需要获取事物属性,获取自己配置到 Spring 中的事物管理器 DataSourceTransactionManager
查看 TransactionInterceptor 事物拦截器源码发现,该类实现了 MethodInterceptor 接口,在前面了解到,向容器中注入了一个InfrastructureAdvisorAutoProxyCreator 后置处理器,这个后置处理器中与一个初始化 bean完成后的方法,创建这个 bean 的代理对象,将要使用 @Transactional 修饰的每个方法封装为 MethodInterceptor ,代理对象要代理执行的就是这个重写的 MethodInterceptor 中的抽象方法,通过拿到的事物属性,进行分析,代理执行
查看该类中重写 MethodInterceptor 的 invoke() 方法,方法中会调用 invokeWithinTransaction()方法

public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
        //调用 invokeWithinTransaction () 方法
        return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
		//1.获取事物相关属性(也就是 @Transactional 中的属性)
        final TransactionAttribute txAttr = this.getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        //2.获取事物管理器(默认获取配置到 Spring 容器中的,持有数据源等,通过@Transaction 的 transactionManager 属性也可以指定获取那个事物管理器)
        final PlatformTransactionManager tm = this.determineTransactionManager(txAttr);
        final String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
        if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {
            try {
                Object result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, new TransactionCallback<Object>() {
                    public Object doInTransaction(TransactionStatus status) {
                        TransactionAspectSupport.TransactionInfo txInfo = TransactionAspectSupport.this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);

                        TransactionAspectSupport.ThrowableHolder var4;
                        try {
                        	//事物方法执行(也就是使用 @Transaction 修饰的方法执行)
                            Object var3 = invocation.proceedWithInvocation();
                            return var3;
                        } catch (Throwable var8) {
                            if (txAttr.rollbackOn(var8)) {
                                if (var8 instanceof RuntimeException) {
                                    throw (RuntimeException)var8;
                                }

                                throw new TransactionAspectSupport.ThrowableHolderException(var8);
                            }

                            var4 = new TransactionAspectSupport.ThrowableHolder(var8);
                        } finally {
                            TransactionAspectSupport.this.cleanupTransactionInfo(txInfo);
                        }

                        return var4;
                    }
                });
                if (result instanceof TransactionAspectSupport.ThrowableHolder) {
                    throw ((TransactionAspectSupport.ThrowableHolder)result).getThrowable();
                } else {
                    return result;
                }
            } catch (TransactionAspectSupport.ThrowableHolderException var14) {
                throw var14.getCause();
            }
        } else {
        	//获取事物
            TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;

            try {
            	//事物方法执行(也就是我们使用 @Transactinal 修饰的方法执行)
                retVal = invocation.proceedWithInvocation();
            } catch (Throwable var15) {
            	//如果方法执行发生异常,利用事物管理器回滚事物
                this.completeTransactionAfterThrowing(txInfo, var15);
                throw var15;
            } finally {
                this.cleanupTransactionInfo(txInfo);
            }
			//如果方法执行完毕,没有抛出异常,利用事物管理器commi 提交事物
            this.commitTransactionAfterReturning(txInfo);
            return retVal;
        }
    }

回滚方法

protected void completeTransactionAfterThrowing(TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
            }

            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                	//回滚事物
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                } catch (TransactionSystemException var7) {
                    this.logger.error("Application exception overridden by rollback exception", ex);
                    var7.initApplicationException(ex);
                    throw var7;
                } catch (RuntimeException var8) {
                    this.logger.error("Application exception overridden by rollback exception", ex);
                    throw var8;
                } catch (Error var9) {
                    this.logger.error("Application exception overridden by rollback error", ex);
                    throw var9;
                }
            } else {
                try {
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                } catch (TransactionSystemException var4) {
                    this.logger.error("Application exception overridden by commit exception", ex);
                    var4.initApplicationException(ex);
                    throw var4;
                } catch (RuntimeException var5) {
                    this.logger.error("Application exception overridden by commit exception", ex);
                    throw var5;
                } catch (Error var6) {
                    this.logger.error("Application exception overridden by commit error", ex);
                    throw var6;
                }
            }
        }

    }

BeanFactoryTransactionAttributeSourceAdvisor 事物增强器

查看ProxyTransactionManagementConfiguration 中的 transactionAdvisor() 方法,创建事物增强器 BeanFactoryTransactionAttributeSourceAdvisor ,注入到容器中,事物增强器需要设置事物拦截器,事物属性,在执行时通过事物增强器,去获取事物拦截器,通过事物拦截器分析事物属性,进行操作

三. 总结

学习要点

  1. 事物的四大特性 ACDI : 原子性,一致性, 隔离性,持久性
  2. 事物可能存在的问题: 脏读, 丢失修改, 可重复读, 幻读,
  3. 事物的隔离级别: 读取未提交, 读取已提交, 可重复读, 串行化,在
  4. mysql 的 MyISAM 不支持事物处理,InnoDB 支持事物处理默认 REPEATABLE-READ 可重读,@Transactional 中,通过 isolation 属性可以设置隔离级别,默认使用数据库隔离级别
  5. 事物的传播: 具体看上面吧, @Transactional 中通过 propagation 属性可以设置,默认REQUIRED 支持当前,事物,如果想要事物内部方法没有事物执行使用 NOT_SUPPORTED, 如果想要内部事物不影响外部事物,使用NESTED,如果想要外部事物不影响内部事物使用 REQUIRES_NEW 但是不推荐

事物的底层原理

  1. 事物的实现原理与 AOP 的实现原理相同,最终是通过动态代理来实现的
  2. 在Spring启动容器,创建初始化bean时首先会创建实现了BeanPostProcessor接口的后置处理器,通过后置处理器中的postProcessBeforeInitialization(),方法与postProcessAfterInitialization()方法去监控其它bean的初始化,在其它bean初始化前后调用执行这两个方法,实现某些功能,在设置 Spring 开启事物时, 会想容器中注入一个 实现了InstantiationAwareBeanPostProcessor接口的后置处理器,实现该接口的后置处理器比直接实现BeanPostProcess接口的多了一个postProcessBeforeInstantiation ()在创建bean以前执行的方法, 在后续的也就是可能存有被@Transaction修饰的方法的 bean 创建时,首先执行后置处理器的postProcessBeforeInitialization()方法,判断当前创建的 bean 是否允许代理增强,也就是该类中有没有关于事物的方法,获取被@Transactional 修饰的方法封装为增强器等,然后创建初始化这个 bean 初始化完毕后执行后置处理器中的postProcessAfterInitialization()方法,通过 jdk 动态代理,或 Cglib 动态代理,创建 ProxyFaction 代理类工厂,创建这个 bean 的代理类对象,注入到容器中
  3. 设置Spring 开启事物后,还会通过 ProxyTransactionManagementConfiguration 配置类向容器中注入 AnnotationTransactionAttributeSource 事物属性,TransactionInterceptor 事物拦截器与, BeanFactoryTransactionAttributeSourceAdvisor 事物增强器
    通过事物属性这个类中可以解析 @Transactional 注解的所有属性数据,TransactionInterceptor 事物拦截器继承MethodInterceptor接口,注入这个bean需要设置事物属性,获取配置的事物管理器,通过事物管理器拿到连接的数据库信息,在方法执行时,最终会将被 @Transactional 修饰的方法封装为MethodInterceptor,在执行时执行的也是重写该接口中的invoke() 方法,执行方法以前,首先会获取事物,当方法执行完毕,手动提交事物,若方法执行失败,手动回滚事物
  4. 也可以通过 AOP 创建自己的事物管理框架,自定义一个事物注解,通过这个自定义的注解修饰需要事物的方法,当方法执行时通过前置通知去拦截判断方法上是否有这个自定义关于事物的注解,如果有通过 DataSourceTransactionManager 开启手动事物, 当方法执行完毕通过返回通知使用 DataSourceTransactionManager 手动提交事物, 若方法发生异常,通过DataSourceTransactionManager 回滚
发布了48 篇原创文章 · 获赞 0 · 访问量 580

猜你喜欢

转载自blog.csdn.net/qq_29799655/article/details/105361641