Spring事务管理---下
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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过程不清楚的看下面这篇文章,这里不分析
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 Transaction-3TransactionDefinition原理和源码解析