Spring事务管理---下

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务管理---下相关的知识,希望对你有一定的参考价值。

Spring事务管理---下


本系列文章:

Spring事务管理—中

Spring事务管理—上

Spring事务王国概览


前文,我们已经完成了对注解元数据驱动的声明式事务的模拟实现,并且详细分析了模拟实现的流程和原理,下面我将带领各位来看看Spring真正的源码实现,让大家真正掌握Spring事务的精髓。


注解元数据驱动的声明式事务

在不使用SpringBoot自动配置的情况下,开启注解元数据驱动的声明式事务有两种方式:

  • 在配置文件中加上下面这句话
 <tx:annotation-driven transaction-manager="transactionManager" mode="proxy" proxy-target-class="true"/>
  • 或者在配置类上加上下面这个注解
@EnableTransactionManagement

tx:annotation-driven源码追踪

想要进行测试,可以引入下面这段xml配置:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
">
 <tx:annotation-driven mode="proxy" proxy-target-class="true"/>
</beans>

tx:annotation-driven标签中的mode属性和proxy-target-class属性,如果看过我上篇模拟注解驱动声明式事务的文章应该都清楚,我们自定义的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyTransactionManagementConfigurationSelector.class)
public @interface MyEnableTransactionManagement 

    /**
     * 是否默认采用cglib进行代理
     */
    boolean proxyTargetClass() default false;

    /**
     * 代理模式--是jdk,cglib代理还是aspectj代理
     */
    AdviceMode mode() default AdviceMode.PROXY;

设置了这两个属性,目的在于能够去创建自动代理创建器,这里一样。

tx:annotation-driven不属于Spring默认命名空间,属于自定义命名空间,那么肯定存在相关的tx命名空间空间解析器,也就是下面要研究的TxNamespaceHandler :

public class TxNamespaceHandler extends NamespaceHandlerSupport 

	static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";

	static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";


	static String getTransactionManagerName(Element element) 
		return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
				element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
	


	@Override
	public void init() 
		registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
		//annotation-driven解析器--->AnnotationDrivenBeanDefinitionParser
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
	



AnnotationDrivenBeanDefinitionParser负责解析tx命名空间下的annotation-driven标签

BeanDefinitionParserDelegate的parseCustomElement负责解析非默认命名空间的标签:

	@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) 
	//获取当前命令空间对应的URI,这里是http://www.springframework.org/schema/tx
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) 
			return null;
		
		//根据上面这个URI去定位到对应的自定义命名空间解析器,怎么定位的呢?
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		//如果没找到相关的解析器,那么就不管,但是会记录一个警告日志
		if (handler == null) 
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		
		//调用自定义命名空间解析器的parse方法
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	

那是怎么根据一个URI定位到自定义命名空间解析器的呢?

resolve方法会通过解析URI定位到对应的自定义命名空间解析器

	public NamespaceHandler resolve(String namespaceUri) 
	//getHandlerMappings负责去查找所有可以发现的自定义命名空间解析器和namespaceUri的映射关系
	//第一次执行该方法时,返回的是URI和对应自定义命名空间解析器的全类名,此时解析器还没有被实例化
		Map<String, Object> handlerMappings = getHandlerMappings();
		//找到当前namespaceUri对应的自定义命令空间解析器---没找到就返回null,找到了就尝试去实例化
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) 
			return null;
		
        //如果是当前自定义解析器是第二次被查询,那么已经实例化了,直接返回		
		else if (handlerOrClassName instanceof NamespaceHandler) 
			return (NamespaceHandler) handlerOrClassName;
		
		//如果当前解析器第一次被使用到,那么会进行实例化
		else 
			String className = (String) handlerOrClassName;
			try 
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) 
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				//调用实例化后的解析器的init方法
				namespaceHandler.init();
				//放入handlerMappings集合,这样下次来的时候,就直接返回实例化好的解析器即可
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			
			catch (ClassNotFoundException ex) 
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
			
			catch (LinkageError err) 
				throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
			
		
	

还是那个问题: 如果获取到当前系统中可用的自定义解析器和对应URI的映射关系的呢?

这个就需要来看看getHandlerMappings的实现了:

	private Map<String, Object> getHandlerMappings() 
		Map<String, Object> handlerMappings = this.handlerMappings;
		//如果handlerMappings 不为空,说明不是第一次来了,那么直接返回
		//因为寻找当前系统中可用的自定义解析器和对应URI的映射关系,这个过程只需要执行一次即可
		if (handlerMappings == null) 
			synchronized (this) 
				handlerMappings = this.handlerMappings;
				if (handlerMappings == null) 
					if (logger.isTraceEnabled()) 
						logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
					
					try 
					//handlerMappingsLocation就是META-INF/spring.handlers
						Properties mappings =
						//这里会去类路径下找所有的META-INF/spring.handlers文件,该文件中记录了解析器和URI的映射关系
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isTraceEnabled()) 
							logger.trace("Loaded NamespaceHandler mappings: " + mappings);
						
						handlerMappings = new ConcurrentHashMap<>(mappings.size());
						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
						this.handlerMappings = handlerMappings;
					
					catch (IOException ex) 
						throw new IllegalStateException(
								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
					
				
			
		
		return handlerMappings;
	



这其实就是SPI思想,熟悉serviceLoader底层实现的小伙伴应该非常清楚,Drver驱动查找底层就是靠serviceLoader的服务发现机制(SPI)完成的

建议大家可以了解一下,可以看一下下面这篇文章

ServiceLoader和DriverManager的前世今生


查找到自定义命令空间解析器后,下一步就是就是调用解析器AnnotationDrivenBeanDefinitionParser的parse方法来解析自定义标签了

	public BeanDefinition parse(Element element, ParserContext parserContext) 
	   //注册一个TransactionalEventListenerFactory--用来生产事务事件监听器的工厂---会在事务提交,回滚等时间点,回调监听器相关接口--这里不多讲
		registerTransactionalEventListenerFactory(parserContext);
		String mode = element.getAttribute("mode");
		//判断代理模式
		//如果Mode为aspectj,那么走aspectj的代理
		if ("aspectj".equals(mode)) 
			// mode="aspectj"
			registerTransactionAspect(element, parserContext);
			if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) 
				registerJtaTransactionAspect(element, parserContext);
			
		
		//其他情况都选择jdk或者cglib代理
		else 
			// mode="proxy"
			//下面就是准备自动代理创建器相关的东西了
			//AopAutoProxyConfigurer是AnnotationDrivenBeanDefinitionParser一个内部类
			AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
		
		return null;
	

AopAutoProxyConfigurer.configureAutoProxyCreator负责创建并配置自动代理创建器

AopAutoProxyConfigurer是AnnotationDrivenBeanDefinitionParser一个内部类,并且该类只有一个configureAutoProxyCreator方法

该方法主要就是来注册自动代理创建器,然后准备TransactionAttributeSource ,TransactionInterceptor和TransactionAttributeSourceAdvisor

		public static void configureAutoProxyCreator(Element element, ParserContext parserContext) 
		    //注册自动代理创建器,并解析标签中相关属性: proxyClass等,设置到自动代理创建器中
			AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
           //默认放入容器中的事务增强器的beanName为org.springframework.transaction.config.internalTransactionAdvisor
			String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
			//如果我们手动往容器中放入了与上面同名的增强器bean,那么这里就跳过,用户指定的优先级更高
			if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) 
				Object eleSource = parserContext.extractSource(element);

				// Create the TransactionAttributeSource definition.
				//TransactionAttributeSource的bean定义准备
				//实际放入的是AnnotationTransactionAttributeSource--他会负责解析@Transactional注解,然后形成方法和TransactionAttribute的映射关系,进行保存
				RootBeanDefinition sourceDef = new RootBeanDefinition(
						"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
				sourceDef.setSource(eleSource);
				//设置角色----ROLE_INFRASTRUCTURE---InfrastructureAdvisorAutoProxyCreator靠beanDefinition来过滤增强器
			//这里不是增强器,仅仅表明当前注入的bean是基础设置bean	 
				sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				//registerWithGeneratedName会注册beanDefintion到注册中心,然后返回生成的beanName
				String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

				// Create the TransactionInterceptor definition.
				//准备拦截器的bean定义
				RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
				interceptorDef.setSource(eleSource);
				//标注为基础设置bean
				interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				//指定拦截器中事务管理器的beanName
				registerTransactionManager(element, interceptorDef);
				//指定拦截器中TransactionAttributeSource对应的beanName
				interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
				//注册拦截器的bean定义到注册中心,返回生成的拦截器beanName
				String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

				// Create the TransactionAttributeSourceAdvisor definition.
				//增强器准备--增强器类型为BeanFactoryTransactionAttributeSourceAdvisor
				RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
				advisorDef.setSource(eleSource);
				//标注为基础设置类--InfrastructureAdvisorAutoProxyCreator靠beanDefinition来过滤增强器
				advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				//增强器需要transactionAttributeSource--因为内部的pointcut通过其来进行类级别和方法级别的过滤
				advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
				//指定advice--拦截器
				advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
				//是否指定了增强器的顺序--到时候aop过程中对筛选出来的增强器排序时会用到
				if (element.hasAttribute("order")) 
					advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
				
				//注册增强器
				parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
                
                //释放相关注册注册好的事件
				CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
				compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
				compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
				compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
				parserContext.registerComponent(compositeDef);
			
		
	

可以看出,上面的源码思路非常清晰,和我们进行模拟的过程是一样的思路:

  • 注册自动代理创建器
  • 是否要注册增强器—>用户是否已经注册过了beanName为org.springframework.transaction.config.internalTransactionAdvisor的增强器
  • 没有的话,先准备AnnotationTransactionAttributeSource
  • 再准备TransactionInterceptor ,将AnnotationTransactionAttributeSource设置给他,并且设置事务管理器
  • 最后就是TransactionAttributeSourceAdvisor,拦截器TransactionInterceptor 肯定要给他,其次就是transactionAttributeSource,负责初始化pointcut用的

如果看到这里,还对TransactionAttributeSource,TransactionAdvisor,TransactionInterceptor 不熟悉的,建议先回看之前的事务管理上和中

PropertyValues在populateBean进行属性注入时,会根据PropertyValue中保存的beanName或者字面值进行属性赋值

标签中的order属性指定的是自动生成的事务增强器的advisor的order属性值,用于在筛选完后的增强器集合中进行比较排序


registerTransactionManager–注册事务管理器给拦截器

	private static void registerTransactionManager(Element element, BeanDefinition def) 
		def.getPropertyValues().add("transactionManagerBeanName",
				TxNamespaceHandler.getTransactionManagerName(element));
	

这里重点是事务管理器的beanName怎么得到的:

TxNamespaceHandler.getTransactionManagerName(element)

如果我们在标签中指定了transaction-manager属性对应的值,即TransactionManager在容器中的名字,那么就会采用我们指定的,否则会去容器中寻找名字默认为transactionManagerbeanName

	static String getTransactionManagerName(Element element) 
		return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
				element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
	

AopNamespaceUtils.registerAutoProxyCreatorIfNecessary—注册自动代理创建器

	public static void registerAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) 
        //注册自动代理创建器,这里注册的就是InfrastructureAdvisorAutoProxyCreator			 
		BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
	    //从标签中读取出proxyClass属性,然后设置到自动代理创建器中,自动代理创建器都继承了ProxyConfig
	    //在创建代理对象的时候,ProxyFactory会拷贝当前自动代理创建器的ProxyConfig相关属性值
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		//释放注册bean的事件
		registerComponentIfNecessary(beanDefinition, parserContext);
	

AopConfigUtils的registerAutoProxyCreatorIfNecessary过程不清楚的看下面这篇文章,这里不分析

Spring读源码系列之AOP–05—aop常用工具类学习

useClassProxyingIfNecessary稍微看看:

	private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) 
		if (sourceElement != null) 
		//标签中是否设置了proxy-target-class值
			boolean proxyTargetClass = Boolean跟我学Spring Cloud(Finchley版)-09-Feign

Spring的bean的存储和管理机制

将Bean放入Spring容器中的五种方式

老王读Spring Transaction-3TransactionDefinition原理和源码解析

跟我学Spring Cloud(Finchley版)-19-配置中心-Spring Cloud Co

Spring整合JDBC以及AOP管理事务