IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot之@Transactional详解 -> 正文阅读

[Java知识库]springboot之@Transactional详解

上节《springboot ApplicationContext之finishBeanFactoryInitialization(getBean())下》我们遗留了一个问题:
InfrastructureAdvisorAutoProxyCreator是什么时候加载到spring容器的,又是怎么产生代理的呢?
我们就来解答下,但是跟今天的主题@Transactional又有什么关系呢?跟我往下看!

springboot 中@Transactional起作用是启用了**@EnableTransactionManagement**注解,而这个注解的默认加载是在TransactionAutoConfiguration中,这个类定义在spring.factories中对应的key=org.springframework.boot.autoconfigure.EnableAutoConfiguration(关于这个注解如何使用,及作用,看相关的springboot starter)。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
		DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public TransactionManagerCustomizers platformTransactionManagerCustomizers(
			ObjectProvider<PlatformTransactionManagerCustomizer<?>> customizers) {
		return new TransactionManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(ReactiveTransactionManager.class)
	public TransactionalOperator transactionalOperator(ReactiveTransactionManager transactionManager) {
		return TransactionalOperator.create(transactionManager);
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
	public static class TransactionTemplateConfiguration {

		@Bean
		@ConditionalOnMissingBean(TransactionOperations.class)
		public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
			return new TransactionTemplate(transactionManager);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnBean(TransactionManager.class)
	@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
	public static class EnableTransactionManagementConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		public static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		public static class CglibAutoProxyConfiguration {

		}

	}

}

这个配置类,装了一个个内部类,我们看跟@Transactional相关的EnableTransactionManagementConfiguration 类,该类中定义了两个内部类JdkDynamicAutoProxyConfiguration 、CglibAutoProxyConfiguration 区别在于spring.aop.proxy-target-class的处理:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
	 * opposed to standard Java interface-based proxies ({@code false}). The default is
	 * {@code false}. <strong>Applicable only if {@link #mode()} is set to
	 * {@link AdviceMode#PROXY}</strong>.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with
	 * {@code @Transactional}. For example, other beans marked with Spring's
	 * {@code @Async} annotation will be upgraded to subclass proxying at the same
	 * time. This approach has no negative impact in practice unless one is explicitly
	 * expecting one type of proxy vs another, e.g. in tests.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate how transactional advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way; an
	 * {@link Transactional} annotation on such a method within a local call will be
	 * ignored since Spring's interceptor does not even kick in for such a runtime
	 * scenario. For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;

	/**
	 * Indicate the ordering of the execution of the transaction advisor
	 * when multiple advices are applied at a specific joinpoint.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;

}

看看这个引入类都引进了啥

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) {
			//当前模式
			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);
	}

}

两个重要的类:

  • AutoProxyRegistrar,又是个引入类,

     if (mode == AdviceMode.PROXY) {
     	//重点看下这里注入的类
     	AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
     	if ((Boolean) proxyTargetClass) {
     		AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
     		return;
     	}
     }
    

    最终注入InfrastructureAdvisorAutoProxyCreator,这就回答了开头是如何注入的问题了!good!

  • ProxyTransactionManagementConfiguration

这里要注意下:springboot 默认开启@EnableAspectJAutoProxy注解(详情看AopAutoConfiguration),会导致在容器中不是InfrastructureAdvisorAutoProxyCreator,而是AnnotationAwareAspectJAutoProxyCreator,本文是在加了spring.aop.auto=false 关闭状态!

这里会有个优先级,spring容器中beanname=org.springframework.aop.config.internalAutoProxyCreator 会只有一个类型!例如我们加了@EnableTransactionManagement注解,又同时有@EnableAspectJAutoProxy注解,这时候AnnotationAwareAspectJAutoProxyCreator就会覆盖InfrastructureAdvisorAutoProxyCreator注入到容器中。

public abstract class AopConfigUtils {
	public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
			"org.springframework.aop.config.internalAutoProxyCreator";
	static {
		// 三种类型
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
	}
	
	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			//优先级高的会覆盖之前注入bean
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}
}

当然我们今天的主题才开始,我们总结当前的引入类,spring 一串串的记不住,还是看个图吧
在这里插入图片描述

标注颜色的是跟事务相关的类,其它的异步、缓存跟当前的逻辑相同,看蓝色的三个引入类

  • BeanFactoryTransactionAttributeSourceAdvisor
  • AnnotationTransactionAttributeSource
  • TransactionInterceptor

它们三个的关系是,看下ProxyTransactionManagementConfiguration类

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
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;
	}

}


好了目前为止,springboot对@Transactional注解,前奏已经完成,准备好进入了。

当我们定义了一个类

@Service
public class TestC {

    @Autowired
    private TestMapper testMapper;

    public void say1(){
        TestC o = (TestC)AopContext.currentProxy();
        o.say2();
    }

    @Transactional
    public void say2(){
        Map<Object, Object> hashMap = new HashMap<>();
        hashMap .put("id",1);
        testMapper.say(hashMap);
    }

}

在spring加载TestC的时候,会被beanpostprocessor(InfrastructureAdvisorAutoProxyCreator)拦截产出代理类,具体什么时候调用《springboot ApplicationContext之finishBeanFactoryInitialization(getBean())下

现在来看看代理类是怎么产生的,拦截的入口就是wrapIfNecessary方法
在这里插入图片描述
分成两个步骤:

  1. 查看当前bean满足条件的advisor、advice
    首先会从当前容器中,查找类型是Advisor,当然当前满足条件的是之前注入到容器中的BeanFactoryTransactionAttributeSourceAdvisor,然后判断改advisor是否可以对当前类进行拦截就是图中的参canApply方法
    在这里插入图片描述
    两个判断:
    classFiltermatch
    methodMatcher.match,满足条件的是(重点来了),还是分两步:循环当前类的方法(如果类中加了事务注解但是没方法,不会拦截),对当前的方法进行判断。方法需要满足以下条件:

    1) 方法修饰符必须是public
    2)对应的方法上存在@Transactional
    3)如果2不满足,对应方法的类存在@Transactional
    
    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
       // Don't allow no-public methods as required.
       if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
       	return null;
       }
    
       // The method may be on an interface, but we need attributes from the target class.
       // If the target class is null, the method will be unchanged.
       Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    
       // First try is the method in the target class.
       TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
       if (txAttr != null) {
       	return txAttr;
       }
    
       // Second try is the transaction attribute on the target class.
       txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
       if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
       	return txAttr;
       }
    
       if (specificMethod != method) {
       	// Fallback is to look at the original method.
       	txAttr = findTransactionAttribute(method);
       	if (txAttr != null) {
       		return txAttr;
       	}
       	// Last fallback is the class of the original method.
       	txAttr = findTransactionAttribute(method.getDeclaringClass());
       	if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
       		return txAttr;
       	}
       }
    
       return null;
    }
    
  2. 存在满足条件的进行创建代理,代理(当前使用cglib)后的对象信息,主要加了拦截器DynamicAdvisedInterceptor
    在这里插入图片描述
    这时候Testc的代理对象已经产生,当调调用Testc.say2()方法,会被DynamicAdvisedInterceptor拦截器拦截
    在这里插入图片描述

判断是否匹配的方式,跟wrapIfNecessary基本一致,唯一区别是针对当前的方法(什么内部调用事务失效的原因),而不是当前类的所有方法。匹配后看下,调用的流程:开启事务–》执行方法–》提交事务。
在这里插入图片描述
刚才提到,内部调用失效的解决方式是要设置:暴露代理,覆盖默认注解@EnableAspectJAutoProxy(exposeProxy = true),就会执行AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry)

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * Register, escalate, and configure the AspectJ auto proxy creator based on the value
	 * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
	 * {@code @Configuration} class.
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

}
@EnableAspectJAutoProxy(exposeProxy = true)
@Service
public class TestC {

    @Autowired
    private TestMapper testMapper;

    public void say1(){
        TestC o = (TestC)AopContext.currentProxy();
        o.say2();
    }

    @Transactional
    public void say2(){
        Map<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("id",1);
        testMapper.say(objectObjectHashMap);
    }


}

想象下,当我们获取到代理对象(TestC)AopContext.currentProxy(),再执行调用方法say2(), 是不是又会被DynamicAdvisedInterceptor拦截,事务是不是又起作用了呢。

最后 最后扣了一张网上的图,前面几种我想应该是清楚了吧!
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 10:59:34  更:2022-09-13 11:04:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:01:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码