文档章节

Spring事务源码梳理1----事务配置解析及切面织入

德胜
 德胜
发布于 2014/08/29 20:59
字数 1637
阅读 964
收藏 5

基于注解, 首先,我们会在配置文件中使用如下的配置来开启Spring事务注解配置.

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />


当Spring解析配置文件的这一行时,发现元素的命名空间不是 http://www.springframework.org/schema/beans 则会

调用 BeanDefinitionParserDelegate.parseCustomElement() 方法来解析, 该方法的内部又是调用的下面方法来处理的

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);//获取元素命名空间
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);//查找相应的命名空间处理器
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));//调用处理器来处理元素, 内部会从map中获取对应的bean处理器来处理, 见后面说明
}


在这里可以看到,是通过 this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 来获取对应的处理器, 具体代码继续跟踪

最后是调用DefaultNamespaceHandlerResolver.resolve(namespaceUri)方法来实现的, 

该方法内部通过查找classpath下面的/META-INF/spring.handlers属性文件来找到命名空间和处理器的对应关系, 

比如说在spring-tx的jar包中,可以看到/META-INF/spring.handlers的内容为

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler


于是, 我们知道了, 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);
	}

	public void init() {
		registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
	}

}


代码很简单 NamespaceHandlerResolver 找到处理器后, 会反射创建实例, 然后调用init方法, 我们继续看看 registerBeanDefinitionParser做了什么处理

这个方法主要是将bean定义处理器加入到一个map中, 然后在 parse 方法中, 会根据名字去找到对应的bean定义处理器, 然后调用 处理器的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
        //findParserForElement 在map中找到对应的bean定义处理器, 然后调用 parse
	return findParserForElement(element, parserContext).parse(element, parserContext);
}


这里 TxNamespaceHandler 注册了三个bean定义处理器 分别是 TxAdviceBeanDefinitionParser AnnotationDrivenBeanDefinitionParser JtaTransactionManagerBeanDefinitionParser

分别对应于 tx:advice    tx:annotation-driven    tx:jta-transaction-manager   这里我们用的注解, 所以来看看 AnnotationDrivenBeanDefinitionParser.parse() 做了啥. 具体代码如下

public BeanDefinition parse(Element element, ParserContext parserContext) {
	String mode = element.getAttribute("mode");
	if ("aspectj".equals(mode)) {
		registerTransactionAspect(element, parserContext);// mode="aspectj"
	} else {
	        //对应配置文件中mode属性, 默认值proxy
		AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); // mode="proxy"
	}
	return null;
}


可以看到, 具体的逻辑在 AopAutoProxyConfigurer.configureAutoProxyCreator() 中, 通过名字可以看出, 和AOP有关, 代码如下

public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
    //这里配置org.springframework.aop.config.internalAutoProxyCreator这个bean的exposeProxy属性为true,这个bean用来创建代理的 对应 xml里面 tx: proxy-target-class="true" 属性设置  
    //(如果配置了<aop:aspectj-autoproxy proxy-target-class="true" />, 这个bean类型就是AnnotationAwareAspectJAutoProxyCreator)
    AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

    String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
    if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
        Object eleSource = parserContext.extractSource(element);

        // Create the TransactionAttributeSource definition.
        //AnnotationTransactionAttributeSource 主要是用来解析注解属性 继承自AbstractFallbackTransactionAttributeSource
        //主要作用就是解析方法注解, 将事物属性封装成对象缓存起来, 然后如果方法上没注解就去类上或者接口上找~~~
        RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
        sourceDef.setSource(eleSource);
        sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

        // Create the TransactionInterceptor definition.
        //TransactionInterceptor 一看名字就知道是拦截方法的, 通过拦截方法的调用, 处理事务
        RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
        interceptorDef.setSource(eleSource);
        interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registerTransactionManager(element, interceptorDef);//这里通过bean name关联事务管理器
        interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
        String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

        // Create the TransactionAttributeSourceAdvisor definition.
        //BeanFactoryTransactionAttributeSourceAdvisor 事务AOP配置 Pointcut:TransactionAttributeSourcePointcut, advice: TransactionInterceptor 
        RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
        advisorDef.setSource(eleSource);
        advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
        advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);//关联TransactionInterceptor
        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);
    }
}

在这个方法中, 想Spring容器注册了三个bean定义:

一个是 AnnotationTransactionAttributeSource 主要是解析方法或类上的事务注解,封装成对应对象缓存

一个是 TransactionInterceptor 实现 MethodInterceptor 接口, 负责拦截指定方法, 处理事务(方法调用前获取连接,开启事务,方法调用后提交或回滚事务等等)

一个是 BeanFactoryTransactionAttributeSourceAdvisor 固定bean name 为 "org.springframework.transaction.config.internalTransactionAdvisor" 实现了Advisor接口

并通过名字引用了 TransactionInterceptor(Advice) , 且该类内部包含了  TransactionAttributeSourcePointcut (Pointcut)


OK 到这里, 事务的切面已经配置好了, 接下来要看Spring是怎么创建代理的吧. 前面注释中有说, 

如果配置了<aop:aspectj-autoproxy proxy-target-class="true" />, Spring就会注册一个bean定义 : AnnotationAwareAspectJAutoProxyCreator

这个类是实现了 BeanPostProcessor 在bean实例创建后, 会调用 BeanPostProcessor.postProcessAfterInitialization 方法来创建代理对象返回


在创建bean实例成功后, Spring自动调用所有实现了 BeanPostProcessor 接口的 bean 的 postProcessAfterInitialization 方法, 而AnnotationAwareAspectJAutoProxyCreator重写了该方法, 具体如下

//bean初始化成功, 参数, bean:bean实例 beanName:bean在Spring容器中的名字
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		//如果之前调用过getEarlyBeanReference获取包装目标对象到AOP代理对象(如果需要),则不再执行  
		//getEarlyBeanReference 也是创建代理对象, 主要是通过代理来解决循环引用问题
		if (!this.earlyProxyReferences.containsKey(cacheKey)) {
			return wrapIfNecessary(bean, beanName, cacheKey);//创建代理对象
		}
	}
	return bean;
}


可以看到, 主要是wrapIfNecessary方法, 继续跟踪

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // Create proxy if we have advice. //注释说的很明白了,如果偶们有advice就创建代理
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));//创建代理
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
}


getAdvicesAndAdvisorsForBean方法内部通过内部在容器中找出所有的Advisor类型的bean,  然后根据条件来判断是否对当前bean应用Advisor.

这里事务是通过TransactionAttributeSourcePointcut继承而来的matches方法来判断, 而这个方法内部又是调用AnnotationTransactionAttributeSource的方法(判断是不是能取到事务配置).

源码大概路径是:

1.调用AbstractAdvisorAutoProxyCreator.findEligibleAdvisors()方法

    1.1调用AbstractAdvisorAutoProxyCreator.findCandidateAdvisors()方法, 具体就是通过Advisor.class在bean工厂中找到所有匹配的bean

    1.2调用AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply()方法, 过滤掉不匹配的Advisor(具体过滤方法是调用AOPUtils.findAdvisorsThatCanApply()方法)

    1.3 AOPUtils.findAdvisorsThatCanApply() 方法内部又是通过调用TransactionAttributeSourcePointcut.getClassFilter().matches(targetClass)和getMethodMatcher().matches(method, targetClass)

          来过滤掉不符合的advisor.

2.调用AbstractAutoProxyCreator.createProxy()方法创建代理


到这里, 我们已经获取到了, 被作用了事务代理功能的bean实例, 接下来就是 方法调用的处理逻辑了. 博客字数限制, 下一篇再继续写.


© 著作权归作者所有

德胜
粉丝 59
博文 31
码字总数 41512
作品 0
长沙
私信 提问
Spring事务用法示例与实现原理

关于事务,简单来说,就是为了保证数据完整性而存在的一种工具,其主要有四大特性:原子性,一致性,隔离性和持久性。对于Spring事务,其最终还是在数据库层面实现的,而Spring只是以一种比较...

爱宝贝丶
2018/08/28
0
0
Spring的四大优势,你是如何理解的?

1、方便解耦,简化开发 Spring是分层的 Java SE/EE 应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提...

骚年锦时
05/15
0
0
处理异常:org.springframework.beans.factory.BeanNotOfRequiredTypeException

异常信息: Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'baseDao' is expected to be of type [com.web.dao.base.BaseDaoImpl] but was act......

hi_jyf
2016/12/03
0
0
Spring事务——Spring 2.X的事务配置策略

Spring 2.X的事务配置策略 虽然前面介绍的TransactionProxyFactoryBean配置策略简单易懂,但配置起来极为麻烦:每个目标Bean都需要配置一个TransactionProxyFactoryBean代理,这种方式将导致...

摆渡者
2014/03/27
0
0
2019年一线大厂春招:Spring面试题和答案合集(下篇)

其他面试答案参考:2019年一线大厂春招:Spring面试题和答案合集(上篇) 35. @RequestMapping 注解 该注解是用来映射一个URL到一个类或一个特定的方处理法上。 36. 什么是基于Java的Spring注...

别打我会飞
04/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊Elasticsearch的MonitorService

序 本文主要研究一下Elasticsearch的MonitorService MonitorService elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/MonitorService.java public class MonitorServic......

go4it
38分钟前
2
0
二、Docker

1、Docker - The TLDR(Too Long,Don't Read,Linxu 终端工具 ) Docker是在Linux和Windows上运行的软件。它创建、管理和编排容器。该软件以开源方式开发,在Github上作为Moby开源项目的一部分。...

倪伟伟
51分钟前
3
0
Python猫荐书系列之七:Python入门书籍有哪些?

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/ArN-6mLPzPT8Zoq0Na_tsg 最近,猫哥的 Python 技术学习群里进来了几位比较特殊的同学:一...

豌豆花下猫
今天
5
0
Guava RateLimiter限流源码解析和实例应用

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高...

算法之名
今天
13
0
国产达梦数据库与MySQL的区别

背景 由于项目上的需要,把项目实现国产化,把底层的MySQL数据库替换为国产的达梦数据库,花了一周的时间研究了国产的数据库-达梦数据库,它和MySQL有一定的区别,SQL的写法也有一些区别。 ...

TSMYK
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部