zoukankan      html  css  js  c++  java
  • spring源码分析——事务的实现原理

     

      在对数据库进行操作时,有时候会把多个操作放到一个事务里,保证原子性,那么这个事务是怎么实现的呢?

    下面我们先通过一个demo看一下事务的使用:

    一:事务的使用

    数据库jdbc配置:

    ##数据源配置
    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/study
    jdbc.username=root
    jdbc.password=root
    

    spring-context.xml配置:

    	<!--加载properties配置文件-->
    	<context:property-placeholder location="classpath*:*.properties" />
    
    	<!-- 配置数据源 -->
    	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
    		  destroy-method="close">
    		<property name="driverClassName" value="${jdbc.driverClass}" />
    		<property name="url" value="${jdbc.url}" />
    		<property name="username" value="${jdbc.username}" />
    		<property name="password" value="${jdbc.password}" />
    	</bean>
    
    	<!-- 指定数据源和配置文件路径 -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="dataSource" />
    		<!-- 自动扫描mapping.xml文件 -->
    		<property name="mapperLocations" value="classpath:UserMapper.xml"></property>
    	</bean>
    
    	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    		<property name="basePackage" value="com.hello.mapper"></property>
    	</bean>
    
    	<!-- 配置事务管理器 -->
    	<bean id="transactionManager"
    		  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"/>
    	</bean>
    
    	<tx:annotation-driven/>
    

      

    写一个类,方法上加上@Transactional注解

    @Service
    public class TestTransactionalService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Transactional
        public int add(){
            User user = new User();
            user.setName("Lucy");
            user.setAge("30");
            int effectCount = userMapper.insert(user);
    		System.out.println("影响条数: "+effectCount);
    		int i = 10/0;
    		return effectCount;
        }
    
    }
    

      

    测试代码:

     运行结果: 从结果可以看到影响条数为1,但是数据库没有数据,可以看出事务回滚生效了

     二:事务的原理分析

    1:注册埋点,注册BeanDefinition对象,实例化BeanPostProcessor对象,并注册到beanPostProcessors中

    配置开启事务管理:

    通过命名空间找到对象的handler类:

     

    注册自定义标签tx的属性解析器,AnnotationDrivenBeanDefinitionParser

     AutoProxyCreator已经以BeanDefinition的形式注册到了BeanDefinitionMaps中。

    注册其他类:

     

    2:实例化并注册到beanPostProcessors中

    是在refresh方法的registryBeanPostProcessor中实现的,这里不再赘述

    3:寻找增强,创建代理

    用增强去匹配当前bean信息:

     

     

     

    创建代理:

     

    4:拦截方法,对方法进行增强

     TestTransactionalService这个对象已经被代理

    进入DynamicAdvisedInterceptor类的intercept方法:

    只有一个增强:

    TransactionInterceptor 的 invoke方法

     

    抛出异常,进入catch代码块:

    回滚事务:

     

     DataSourceTransactionManager类的doRollback回滚方法:

    可以看出正在执行回滚操作是在DataSourceTransactionManager类中进行的

    下面把导致异常的代码去掉,看一下提交的情况:

    提交事务:

     

     

     

    调到了DataSourceTransactionManager的doCommit事务:

    到这里事务管理的分析就结束了,实现原理就是对数据库的操作方法进行增强,如果执行成功就commit,执行失败就rollback。

    a: 看一下异常回滚里面小细节:rollbackFor ,什么异常才回滚?

     如果@Transactional注解上不配置rollbackFor 默认是RuntimException 或者 Error

     

    如果自定义rollbackFor类型:

    b:如果@Transactional注释的方法不是public修饰的,也不会被代理,找不到@Transactional注解的信息

     

    c:如果是内部调用,会不会生效,从其他类调用add方法:

    调用add方法,进入intercept

       

    在add方法中调用addData,实际是调用target的方法,不是代理方法,所以不会有事务的存在

     

     

  • 相关阅读:
    LIKE语句也可以这样写
    a链接触发javascript函数导致innerHTML里的图片无法加载
    引用类型真屌
    网站建设心得
    SPAN
    Go! 环境配置和入门
    linux内核编译
    面试题
    KCMT开源控件之方便简洁的分页控件
    c#中out、ref和params的用法与区别
  • 原文地址:https://www.cnblogs.com/warrior4236/p/13257450.html
Copyright © 2011-2022 走看看