Spring IOC 源码学习 事务相关的 BeanDefinition 解析过程 (XML)

事务相关的 BeanDefinition 解析过程 (XML)

bean 标签

对于 jdbcTemplate transactionManager dataSource bookService 走的是默认命名空间的处理器, IOC标准解析流程, 不再啰嗦了

\[Spring IOC 源码学习 XML详细加载流程总结\]

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

java 复制代码
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element ele) {//是否 是元素标签
				/**
				 * 处理默认命名空间的标签, 有如下四个
				 * <import></import>, <alias> </alias>, <bean></bean>, <beans></beans>
				 *
				 */
				if (delegate.isDefaultNamespace(ele)) {
					parseDefaultElement(ele, delegate);
				}
				else {
					/**
					 * 处理 非默认命名空间的标签;
					 * 	注意这里包括 <context:bean ...> <aop:xx ...> <tx:xx ...> 等等所有指定命名空间的xml配置
					 * 	主要逻辑是: 拿到元素的命名空间URI, 再从 XmlReaderContext 找到对应的 NamespaceHandler 调用解析 `parse`方法解析到 BeanDefinition 返回
					 */
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

aop 标签

对于 aop 部分的标签则的是 AOP 的流程

xml 复制代码
<aop:config>
	<!--切点配置	-->
	<aop:pointcut id="serviceOperation"
				  expression="execution(* org.yang.learn.spring.tx.BookService.*(..))"/>
	<!-- 通知/增强 配置 (关键是这个通知指向 txAdvice '事务增强')-->
	<aop:advisor pointcut-ref="serviceOperation"  advice-ref="txAdvice"/>
</aop:config>
  1. <aop:pointcut... : 解析为 org.springframework.aop.aspectj.AspectJExpressionPointcut 其 BeanDefinition
  2. <aop:advisor... : 解析为 org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor 其 BeanDefinition

internalAutoProxyCreator 的注册

\[Spring AOP 源码学习 详细流程总结\]

这里要注意AOP 的 ConfigBeanDefinitionParser 在解析时是会注册的一个internalAutoProxyCreator! (AOP解析流程, 在BPP回调时创建代理对象的)
org.springframework.aop.config.ConfigBeanDefinitionParser#parse

java 复制代码
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		/**
		 * 1. 注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的
		 * 名称是: org.springframework.aop.config.internalAutoProxyCreator
		 * 对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST
		 *	    InfrastructureAdvisorAutoProxyCreator.class,AspectJAwareAdvisorAutoProxyCreator.class, AnnotationAwareAspectJAutoProxyCreator.class
		 * 	注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的
		 *
		 */
		configureAutoProxyCreator(parserContext, element);

解析 <aop:pointcut>, <aop:advisor>, 没有切面标签
org.springframework.aop.config.ConfigBeanDefinitionParser#parse

java 复制代码
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		/**
		 * 1. 注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的
		 * 名称是: org.springframework.aop.config.internalAutoProxyCreator
		 * 对应的类, 根据情况有以下三个可能: org.springframework.aop.config.AopConfigUtils#APC_PRIORITY_LIST
		 *	    InfrastructureAdvisorAutoProxyCreator.class,AspectJAwareAdvisorAutoProxyCreator.class, AnnotationAwareAspectJAutoProxyCreator.class
		 * 	注册一个名称为`org.springframework.aop.config.internalAutoProxyCreator` 对AOP处理的Bean Definition; 它是实现 InstantiationAwareBeanPostProcessor 接口的
		 *
		 */
		configureAutoProxyCreator(parserContext, element);

		/**
		 * 2. 解析 <aop:config> 标签的子元素 (pointcut, advisor, aspect)
		 * 解析 <aspect ...>:
		 * 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor 的BeanDefinition 然后将其注册到 BeanFactory
		 *
		 *  AspectJPointcutAdvisor 的包含情况
		 * 每一个通知(Advice) 都会封装为一个 AspectJPointcutAdvisor(通知器) 类型的BeanDefinition 然后将其注册到 BeanFactory
		 * 	AspectJPointcutAdvisor 内部包含五种通知类类型:  AspectJAfterReturningAdvice AspectJAfterAdvice AspectJAroundAdvice AspectJMethodBeforeAdvice AspectJAfterThrowingAdvice
		 *  而每种通知类型的内部又主要有三个关键属性,包括:
		 *  1. java.lang.reflect.Method(通知切面的方法)
		 *	2. org.springframework.aop.aspectj.AspectJExpressionPointcut(切入点表达式)
		 * 	3. org.springframework.aop.aspectj.AspectInstanceFactory (切面实例工厂)
		 */
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			switch (localName) {
				/**
				 * 解析 pointcut/切入点  //筛选连接点, 即: 哪些方法需要被代理
				 * 解析为 org.springframework.aop.aspectj.AspectJExpressionPointcut 注册其 BeanDefinition
				 */
				case POINTCUT -> parsePointcut(elt, parserContext);
				/**
				 *  解析 advisor/通知/建议/增强处理  //即: 增强功能这一部分代码
				 * 解析为 org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor 注册其 BeanDefinition
				 */
				case ADVISOR -> parseAdvisor(elt, parserContext);
				/**
				
				 */
				case ASPECT -> parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

tx 标签

前文说了由 org.springframework.transaction.config.TxAdviceBeanDefinitionParser 负责XML解析

先来到父类方法解析 TransactionInterceptor

org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal

java 复制代码
/**
	 * Creates a {@link BeanDefinitionBuilder} instance for the
	 * {@link #getBeanClass bean Class} and passes it to the
	 * {@link #doParse} strategy method.
	 * @param element the element that is to be parsed into a single BeanDefinition
	 * @param parserContext the object encapsulating the current state of the parsing process
	 * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
	 * @throws IllegalStateException if the bean {@link Class} returned from
	 * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
	 * @see #doParse
	 *
	 */
	@Override
	protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
		/**
		 *
		 * 1. 解析 <tx:advice ... 标签 封装为`GenericBeanDefinition`
		 * 其名称和class为org.springframework.transaction.interceptor.TransactionInterceptor
		 * 注意这个 TransactionInterceptor 实现了MethodInterceptor相当于是个Advice
		 *
		 * org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
		 * #parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
		 * org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal
		 *
		 *
		 */
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
		String parentName = getParentName(element);
		if (parentName != null) {
			builder.getRawBeanDefinition().setParentName(parentName);
		}
		Class<?> beanClass = getBeanClass(element);
		if (beanClass != null) {
			builder.getRawBeanDefinition().setBeanClass(beanClass);
		}
		else {
			String beanClassName = getBeanClassName(element);
			if (beanClassName != null) {
				builder.getRawBeanDefinition().setBeanClassName(beanClassName);
			}
		}
		builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
		BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
		if (containingBd != null) {
			// Inner bean definition must receive same scope as containing bean.
			builder.setScope(containingBd.getScope());
		}
		if (parserContext.isDefaultLazyInit()) {
			// Default-lazy-init applies to custom bean definitions as well.
			builder.setLazyInit(true);
		}
		doParse(element, parserContext, builder);
		return builder.getBeanDefinition();
	}

在回来解析到 transactionAttributeSource

org.springframework.transaction.config.TxAdviceBeanDefinitionParser#doParse

java 复制代码
@Override
	protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
		/**
		 * 添加 transactionManager (事务管理)的 ref
		 */
		builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
		List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
		if (txAttributes.size() > 1) {
			parserContext.getReaderContext().error(
					"Element <attributes> is allowed at most once inside element <advice>", element);
		}
		else if (txAttributes.size() == 1) {
			/**
			 * 解析 <tx:attributes> ...  子标签
			 * 包括: 匹配的目标方法, 事务的传播属性, 是否只读..
			 * 类型是 `org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource`
			 */
			// Using attributes source.
			Element attributeSourceElement = txAttributes.get(0);
			RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
			builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
		}
		else {
			// Assume annotations source.
			builder.addPropertyValue("transactionAttributeSource",
					new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
		}
	}

汇总图

事务相关对象的BeanDefinition 对应AOP的三剑客

首先一点事务增强, 本质上就是AOP的逻辑, 在AOP最重要的就是AspectJPointcutAdvisor包含的三剑客

对应关系

可以看到少了AspectInstanceFactory 在事务这里的逻辑中不需它; 因为切面的逻辑是硬编码在TransactionInterceptor中了;

至此在事务相关的AOP对象已经齐了

相关推荐
鬼蛟2 小时前
Spring————事务
android·java·spring
西门吹-禅3 小时前
【sap fiori cds up error】
java·服务器·sap cap cds
敲代码的嘎仔3 小时前
Java后端面试——SSM框架面试题
java·面试·职场和发展·mybatis·ssm·springboot·八股
NGC_66113 小时前
Spring与SpringBoot
spring
大傻^4 小时前
Spring AI Alibaba RAG实战:基于向量存储的检索增强生成
java·人工智能·spring
大傻^4 小时前
Spring AI Alibaba 快速入门:基于通义千问的AI应用开发环境搭建
java·人工智能·后端·spring·springai·springaialibaba
伯恩bourne4 小时前
Google Guava:Java 核心工具库的卓越之选
java·开发语言·guava
小王不爱笑1324 小时前
Spring 基础核心
java
心勤则明4 小时前
用 Spring AI Alibaba 打造智能查询增强引擎
java·人工智能·spring