Spring事物源码

@Transactional

spring 事务注解

1.简单开启事务管理

@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的

<tx:annotation-driven />

2.事务注解详解

默认遇到throw new RuntimeException("...");会回滚
需要捕获的throw new Exception("...");不会回滚

指定回滚

@Transactional(rollbackFor=Exception.class) 
    public void methodName() {
       // 不会回滚
       throw new Exception("...");
    } 

指定不回滚

@Transactional(noRollbackFor=Exception.class)
    public ItimDaoImpl getItemDaoImpl() {
        // 会回滚
        throw new RuntimeException("注释");
    } 
添加事物
  • @Transactional(propagation=Propagation.REQUIRED)
    如果有事务,那么加入事务,没有的话新建一个(不写的情况下)

  • @Transactional(propagation=Propagation.NOT_SUPPORTED)
    容器不为这个方法开启事务

  • @Transactional(propagation=Propagation.REQUIRES_NEW)
    不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

  • @Transactional(propagation=Propagation.MANDATORY)
    必须在一个已有的事务中执行,否则抛出异常

  • @Transactional(propagation=Propagation.NEVER)
    必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

  • @Transactional(propagation=Propagation.SUPPORTS)
    如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

@Transactional(propagation=Propagation.NESTED)

   public void methodName(){
      // 本类的修改方法 1
      update();
      // 调用其他类的修改方法
      otherBean.update();
      // 本类的修改方法 2
      update();
   }

other失败了不会影响 本类的修改提交成功,本类update的失败,other也失败

readOnly=true只读,不能更新,删除

@Transactional (propagation = Propagation.REQUIRED,readOnly=true)

设置超时时间

@Transactional (propagation = Propagation.REQUIRED,timeout=30)

设置数据库隔离级别

@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)

3.指定事务管理器

spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。

  • 事务管理器
    不管JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果添加 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果添加spring-boot-starter-data-jpa依赖,框架会默认注入 JpaTransactionManager 实例。

你可以在启动类中添加如下方法,Debug测试,就能知道自动注入的是 PlatformTransactionManager 接口的哪个实现类。

  • 打印项目事务管理器
    @EnableTransactionManagement 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {

    @Bean
    public Object testBean(PlatformTransactionManager platformTransactionManager){
        System.out.println(">>" + platformTransactionManager.getClass().getName());
        return new Object();
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }
}

这些SpringBoot为我们自动做了,这些对我们并不透明,如果你项目做的比较大,添加的持久化依赖比较多,我们还是会选择人为的指定使用哪个事务管理器。
代码如下:

  • 指定事务管理器
    其中 dataSource 框架会自动为我们注入
@EnableTransactionManagement
@SpringBootApplication
public class ProfiledemoApplication {
    @Bean
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public Object testBean(PlatformTransactionManager platformTransactionManager) {
        System.out.println(">>" + platformTransactionManager.getClass().getName());
        return new Object();
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }
}

在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。

然后在Service中,被 @Transactional 注解的方法,将支持事务。如果注解在类上,则整个类的所有方法都默认支持事务。

对于同一个工程中存在多个事务管理器要怎么处理,请看下面的实例,具体说明请看代码中的注释。

  • 使用指定的事务管理器
    // 开启注解事务管理,等同于xml配置文件中的 <tx:annotation-driven />
@EnableTransactionManagement 
@SpringBootApplication
public class ProfiledemoApplication implements TransactionManagementConfigurer {

    @Resource(name="txManager2")
    private PlatformTransactionManager txManager2;

    // 创建事务管理器1
    @Bean(name = "txManager1")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // 创建事务管理器2
    @Bean(name = "txManager2")
    public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }

    // 实现接口 TransactionManagementConfigurer 方法
    // 其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager2;
    }

    public static void main(String[] args) {
        SpringApplication.run(ProfiledemoApplication.class, args);
    }

}
@Component
public class DevSendMessage implements SendMessage {

    // 使用value具体指定使用哪个事务管理器
    @Transactional(value="txManager1")
    @Override
    public void send() {
        System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
        send2();
    }

    // 在存在多个事务管理器的情况下,如果使用value具体指定
    // 则默认使用方法 annotationDrivenTransactionManager() 返回的事务管理器
    @Transactional
    public void send2() {
        System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
    }

}

源码讲解

Spring 注解 事物开发

一、声明式事务:
环境搭建: 1、导入相关依赖 数据源、数据库驱动、Spring-jdbc模块

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.13.RELEASE</version>
</dependency>
​
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据

3、给方法上标注 @Transactional 表示当前方法是一个事务方法;

4、 @EnableTransactionManagement 开启基于注解的事务管理功能;

5、配置事务管理器来控制事务transactionManager();

代码:

@Configuration
@EnableTransactionManagement
@ComponentScan("com.demo.tx")
public class TxConfig {

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource ();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://192.168.0.1/demo");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }
​
    @Bean
    public JdbcTemplate jdbcTemplate(){
        return  new JdbcTemplate(dataSource());
    }
​
    @Bean
    public PlatformTransactionManager transactionManager(){
       return new DataSourceTransactionManager(dataSource());
    }
​
}

二、事物原理部分

  • @EnableTransactionManagement
image.png
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
​
    boolean proxyTargetClass() default false;
​
    AdviceMode mode() default AdviceMode.PROXY;
​
    int order() default Ordered.LOWEST_PRECEDENCE;
}
  • EnableTransactionManagement有三个属性,分别是proxyTargetClass、AdviceMode、order;它import了TransactionManagementConfigurationSelector
  • proxyTargetClass设置为true表示使用基于子类实现的代理(CGLIB),设置为false表示使用基于接口实现的代理,默认为false
  • AdviceMode表示是使用哪种transactional advice,有PROXY及ASPECTJ两种,默认是AdviceMode.PROXY

Spring AOP使用JDK动态代理或CGLIB为给定目标对象创建代理。(默认使用JDK动态代理)。

如果被代理的目标对象已经实现了接口,则使用JDK动态代理。如果目标对象未实现任何接口,则会创建CGLIB代理。

如果要强制使用CGLIB代理,final 方法无法被覆盖不建议使用。要强制使用CGLIB代理,请将元素<aop:config>的属性proxy-target-class值设置为true,如下所示:

<aop:config proxy-target-class="true">
</aop:config>

代理原理区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

  • TransactionManagementConfigurationSelector
    TransactionManagementConfigurationSelector继承了抽象类AdviceModeImportSelector,它覆盖了selectImports,该方法根据adviceMode返回要创建的类名;如果是PROXY模式,则返回AutoProxyRegistrar.class.getName(),ProxyTransactionManagementConfiguration.class.getName();如果是ASPECTJ模式,则返回TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME或者是TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME
public class TransactionManagementConfigurationSelector 
extends AdviceModeImportSelector<EnableTransactionManagement> {
    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(), 
                  ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {TransactionManagementConfigUtils.
                  TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}
  • AutoProxyRegistrar
    给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件
AutoProxyRegistrar.registerBeanDefinitions() -->
    
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);   -->
    registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
  return registerAutoProxyCreatorIfNecessary(registry, null);
}
​
BeanDefinition registerAutoProxyCreatorIfNecessary(
    BeanDefinitionRegistry registry, Object source) {
  return registerOrEscalateApcAsRequired(
      InfrastructureAdvisorAutoProxyCreator.class, registry, source); 
 //注册了一个InfrastructureAdvisorAutoProxyCreator组件
}

InfrastructureAdvisorAutoProxyCreator:?原理和AOP部分的AnnotationAwareAspectJAutoProxyCreator一样,利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;

  • ProxyTransactionManagementConfiguration
    给容器中注册事务增强器transactionAdvisor,事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解,事务拦截器TransactionInterceptor保存了事务属性信息,
    事务管理器是一个 MethodInterceptor;
@Configuration
public class ProxyTransactionManagementConfiguration 
  extends AbstractTransactionManagementConfiguration {
​
    事物增强器
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor 
          = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        return advisor;
    }
​
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }
}

3.1.2)、事务拦截器:
MethodInterceptor在目标方法执行的时候;执行拦截器链;

事务拦截器:

​ 1)、先获取事务相关的属性

​ 2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger最终会从容器中按照类型获取一个PlatformTransactionManager;

​ 3)、执行目标方法
如果异常,获取到事务管理器,利用事务管理回滚操作;
如果正常,利用事务管理器,提交事务

3.1.2.1 MethodInterceptor在目标方法执行的时候;
TransactionInterceptor.invoke() 方法

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {

Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}

3.1.2.2 TransactionAspectSupport.invokeWithinTransaction()方法

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {

// 1)、先获取事务相关的属性
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// 2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//获取要执行的事物方法
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建一个事物
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
//事物方法的执行
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 有异常回滚事物
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//得到事物管理器,提交事物
commitTransactionAfterReturning(txInfo);
return retVal;
}

else {
final ThrowableHolder throwableHolder = new ThrowableHolder();

// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});

// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
3.1.2.2.1)、获取PlatformTransactionManager
如果事先没有添加指定任何transactionmanger最终会从容器中按照类型获取一个PlatformTransactionManager

protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
return getTransactionManager();
}
//判断是否@Transactional(transactionManager = "")指定了transactionManager
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.transactionManagerBeanName);
}
else {
PlatformTransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
//在IOC容器中获取PlatformTransactionManager
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容