SpringBoot下@Transactional原理

一、@Transactional的使用

  • 加在方法上:方法内的所有操作处于一个事务中
  • 加在类上
    • 该类的所有public修饰的方法都具有共享事务属性
    • 如果方法和类上都有事务注解,方法上的事务注解优先

二、@Transactional原理

Springboot目前最为流行,它的约定大于配置的特性深受大家喜欢,注解驱动开发已成为主流。面向元数据遍历已经成为越来越多开发者的偏好,因此原理从Springboot的EnableTransactionManagement注解说起

1、@EnableTransactionManagement

表示开启事务管理

@Target(ElementType.TYPE)
@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;
}

通过Import导入了TransactionManagementConfigurationSelector类,其中默认的AdviceMode为AdviceMode.PROXY,即默认使用JDK的动态代理生成代理类。

2、TransactionManagementConfigurationSelector

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
   

	/**
	 * Returns {@link ProxyTransactionManagementConfiguration} or
	 * {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
	 * and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
	 * respectively.
	 */
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
   
		switch (adviceMode) {
   
            //根据上面注解的默认配置,adviceMode默认为PROXY
			case PROXY:
				return new String[] {
   AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {
   determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
   
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}
}

TransactionManagementConfigurationSelector继承自AdviceModeImportSelector类,实现selectImports方法可以注入对应的bean,根据EnableTransactionManagement注解的默认配置,adviceMode默认为PROXY。所以这里会注入AutoProxyRegistrar和ProxyTransactionManagementConfiguration这两个类

  • AutoProxyRegistrar

    public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
         
    
    	private final Log logger = LogFactory.getLog(getClass());
    
    	/**
    	 * Register, escalate, and configure the standard auto proxy creator (APC) against the
    	 * given registry. Works by finding the nearest annotation declared on the importing
    	 * {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
    	 * attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
    	 * {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
    	 * subclass (CGLIB) proxying.
    	 * <p>Several {@code @Enable*} annotations expose both {@code mode} and
    	 * {@code proxyTargetClass} attributes. It is important to note that most of these
    	 * capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
    	 * single APC}. For this reason, this implementation doesn't "care" exactly which
    	 * annotation it finds -- as long as it exposes the right {@code mode} and
    	 * {@code proxyTargetClass} attributes, the APC can be registered and configured all
    	 * the same.
    	 */
    	@Override
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         
    		boolean candidateFound = false;
            // 这里面需要特别注意的是:这里是拿到所有的注解类型~~~而不是只拿@EnableAspectJAutoProxy这个类型的
    		// 原因:因为mode、proxyTargetClass等属性会直接影响到代理得方式,而拥有这些属性的注解至少有:
    		// @EnableTransactionManagement、@EnableAsync、@EnableCaching等~~~~
    		// 甚至还有启用AOP的注解:@EnableAspectJAutoProxy它也能设置`proxyTargetClass`这个属性的值,因此也会产生关联影响~
    		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    		for (String annType : annTypes) {
         
    			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
    			if (candidate == null) {
         
    				continue;
    			}
                //获取mode和proxyTargetClass的属性
    			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) {
         
                        //向容器注入的是一个InfrastructureAdvisorAutoProxyCreator,它主要是读取Advisor类,并对符合的bean进行二次代理
                        //如果出现多次的话,这里不是覆盖的形式,而是以第一次的为主
    					//当然它内部有做等级的提升之类的
    					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                        //看要不要强制使用CGLIB的方式(这个属性若出现多次,是会是覆盖的形式)
    					if ((Boolean) proxyTargetClass) {
         
    						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    						return;
    					}
    				}
    			}
    		}
            
            //如果一个都没有找到则打印info日志
    		//可能是自己注入这个类,而不是使用注解去注入
    		if (!candidateFound && logger.isInfoEnabled()) {
         
    			String name = getClass().getSimpleName();
    			logger.info(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));
    		}
    	}
    
    }
    

    主要作用就是往Spring容器注入了一个自动代理创建器:org.springframework.aop.config.internalAutoProxyCreator,并且看看是采用CGLIB还是JDK代理

  • ProxyTransactionManagementConfiguration

    @Configuration(proxyBeanMethods = false)
    public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
         
    
    	@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
    			TransactionAttributeSource transactionAttributeSource,
    			TransactionInterceptor transactionInterceptor) {
         
    		BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    		advisor.setTransactionAttributeSource(transactionAttributeSource);
    		advisor.setAdvice(transactionInterceptor);
    		if (this.enableTx != null) {
         
    			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(
    			TransactionAttributeSource transactionAttributeSource) {
         
    		TransactionInterceptor interceptor = new TransactionInterceptor();
    		interceptor.setTransactionAttributeSource(transactionAttributeSource);
    		if (this.txManager != null) {
         
    			interceptor.setTransactionManager(this.txManager);
    		}
    		return interceptor;
    	}
    }
    

    这里是往容器中注入事务相关的bean

    • BeanFactoryTransactionAttributeSourceAdvisor:事务的核心,下面分析
    • TransactionAttributeSource:事务属性源
    • TransactionInterceptor:事务拦截器,它是个MethodInterceptor。(我们可以自定义个beanName一模一样的TransactionInterceptor来覆盖默认的事务拦截器)

    再看父类AbstractTransactionManagementConfiguration

    @Configuration
    public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
         
    
    	@Nullable
    	protected AnnotationAttributes enableTx;
    
    	/**
    	 * Default transaction manager, as configured through a {@link TransactionManagementConfigurer}.
    	 */
    	@Nullable
    	protected TransactionManager txManager;
    
    
    	@Override
    	public void setImportMetadata(AnnotationMetadata importMetadata) {
         
    		this.enableTx = AnnotationAttributes.fromMap(
    				importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
    		if (this.enableTx == null) {
         
    			throw new IllegalArgumentException(
    					"@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
    		}
    	}
    
        //这里可以通过配置文件注入一个默认的事务管理器
    	@Autowired(required = false)
    	void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
         
    		if (CollectionUtils.isEmpty(configurers)) {
         
    			return;
    		}
            //最多只允许配置一个
    		if (configurers.size() > 1) {
         
    			throw new IllegalStateException("Only one TransactionManagementConfigurer may exist");
    		}
    		TransactionManagementConfigurer configurer = configurers.iterator().next();
    		this.txManager = configurer.annotationDrivenTransactionManager();
    	}
    
        //注册一个监听器工厂,用以支持@TransactionalEventListener注解标注的方法,来监听事务相关的事件
    	@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public static TransactionalEventListenerFac
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot中,@Transactional注解可以用于实现对事务的管理。在使用SpringBoot与PostgreSQL的项目中,可以使用@Transactional注解来实现事务的回滚操作。当@Transactional注解被应用到方法上时,它会在方法执行前创建或者加入一个事务,并在方法执行完后根据执行情况提交或者回滚事务。这样可以确保在方法执行过程中出现异常时,事务能够正确地回滚。 在使用@Transactional注解时,需要注意以下几点: 1. 该注解只能应用于public修饰的方法上。 2. 默认情况下,Spring会对unchecked异常进行事务回滚,即对RuntimeException及其子类进行回滚操作。如果想对checked异常也进行回滚,可以在注解上指定rollbackFor属性,例如@Transactional(rollbackFor = Exception.class)。 3. 在service层进行try-catch操作时,如果异常被捕获了并且没有抛出,那么事务将不会回滚。如果想要在service层抛出异常并确保事务回滚,可以使用两种方法之一: a. 在catch块中手动设置事务回滚,例如TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。 b. 在方法上抛出异常,让Spring自动捕获并回滚事务。 总之,通过在SpringBoot项目的方法上使用@Transactional注解,可以便捷地实现对事务的管理,并确保在方法执行过程中出现异常时能够正确地回滚事务。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SpringBoot事务注解@Transactional](https://blog.csdn.net/qq_42772400/article/details/106136367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [SpringBoot @Transactional事务回滚实现](https://blog.csdn.net/weixin_44407691/article/details/113094850)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值