Spring之ApplicationContext加载含有事务标签的配置文件的流程

在之前的文章中我们详细的介绍了关于AOP的加载,实例化以及执行的流程,在接下来这几篇文章中,我们将讲解利用AOP实现的事务的这一系列流程。在本文中,我们先来看一下配置文件的解析过程,下面是我们这个专题的类、配置文件以及main方法:
在这里插入图片描述

ApplicationContext beanFactory = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService)beanFactory.getBean("userService");
userService.save();
userService.update();

在本文,我们主要针对第一行代码展开讲解,忽略到一开始之前重复的代码,并且由于之前详细的讲过了这些流程,所以本文中只看关键代码,从下面这个方法开始分析:

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) {
					Element ele = (Element) node;
					//判断是不是默认的命名空间的子节点,从而选择不同的方法来解析
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

按照之前我们讲过的解析默认命名空间的标签同样的流程解析dbcTemplate、userDao、userService、transactionManager,我们着重来看一下有关事务的标签是如何创建的:

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		//得到命名空间
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//根据命名空间找到对应的解析器
		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));
	}

这次我们解析的是事务的标签,找到的解析器也是org.springframework.transaction.config.TxNamespaceHandler,我们看接下来是怎么处理的:

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
		//添加transactionManager属性
		builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
		//获得子节点中标签名字为ATTRIBUTES_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) {
			// 解析这个子节点
			Element attributeSourceElement = txAttributes.get(0);
			RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
			builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
		}
		else {
			// 假设注释来源。
			builder.addPropertyValue("transactionAttributeSource",
					new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
		}
	}

在这个方法中获得了transactionManager属性和<tx:attributes/>子节点,接下来看一下是怎么解析这个子节点的,一部分一部分分析这个方法parseAttributeSource:

List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
		ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
				new ManagedMap<>(methods.size());
		transactionAttributeMap.setSource(parserContext.extractSource(attrEle));

获得所有的<tx:method/>标签

for (Element methodEle : methods) {
			...
		}

接下来,在这个循环中解析所有的<tx:method/>标签,代码太长,我们也来一步一步的分析:

String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
TypedStringValue nameHolder = new TypedStringValue(name);
nameHolder.setSource(parserContext.extractSource(methodEle));

获得那么属性,并且封装成TypedStringValue

RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
			String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
			String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
			String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
			String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
			if (StringUtils.hasText(propagation)) {
				attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
			}
			if (StringUtils.hasText(isolation)) {
				attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
			}
			if (StringUtils.hasText(timeout)) {
				try {
					attribute.setTimeout(Integer.parseInt(timeout));
				}
				catch (NumberFormatException ex) {
					parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
				}
			}
			if (StringUtils.hasText(readOnly)) {
				attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
			}

解析针对当前解析出的方法的事务传播行为、隔离级别、超时等属性并且设置到RuleBasedTransactionAttribute对象上

RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
return attributeSourceDefinition;

返回一个<tx:attributes/>的bean定义,这样,一个<tx:advice/>的定义就算创建完了,看一下还有什么后续的操作:

String id = resolveId(element, definition, parserContext);

解析了标签的id属性

BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());

封装出了BeanDefinitionHolder对象,并将解析出来的定义注册到IOC容器中

BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);

为解析出来的bean定义封装出了一个新的定义,而这个定义的构造方法获取到了bean定义注入参数的引用和内部bean引用,下一个方法是执行用户自定义的方法,在这里什么都没有做,最后一个方法是让它和他的子节点绑定,在这里它没有子节点,不需要绑定。
就这样,<tx:…/>标签就解析完了,比AOP标签解析起来简单多了,虽然是这样,后面还有一个AOP的标签等着我们去解析呢,继续看吧:

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		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));
	}

又是这个方法,获取标签的命名空间,找到对应的解析器

public BeanDefinition parse(Element element, ParserContext parserContext) {
		//创建\<aop:config/\>的定义
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		//放到栈中等待和子元素绑定到一起
		parserContext.pushContainingComponent(compositeDef);
		//设置自动代理,并且将\<aop:config/\>的定义放到IOC容器中
		configureAutoProxyCreator(parserContext, element);
		//得到所有子节点
		List<Element> childElts = DomUtils.getChildElements(element);
		//解析子节点
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				parseAspect(elt, parserContext);
			}
		}
		//和子节点绑定
		parserContext.popAndRegisterContainingComponent();
		return null;
	}

这个方法和之前文章中的方法一样,在这里不做过多的解释,注释已经很清楚了,对<aop:pointcut/>也和之前的一样,这里着重看一下对<aop:advisor/>的解析,我们之前还没有遇到直接解析一个advisor的:

private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {
		//创建一个advisor的定义
		RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
		//设置advisor定义的节点
		advisorDefinition.setSource(parserContext.extractSource(advisorElement));
		//获得advice-ref的属性值
		String adviceRef = advisorElement.getAttribute(ADVICE_REF);
		...
		return advisorDefinition;
	}

在创建advisor的bean定义的时候同时得到了他的属性值,创建完这个bean定义后开始解析他的pointcut属性值

private Object parsePointcutProperty(Element element, ParserContext parserContext) {
		//同时有pointcut和pointcut-ref属性值
		if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {
			parserContext.getReaderContext().error(
					"Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
					element, this.parseState.snapshot());
			return null;
		}
		//有pointcut属性值
		else if (element.hasAttribute(POINTCUT)) {
			// Create a pointcut for the anonymous pc and register it.
			String expression = element.getAttribute(POINTCUT);
			AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
			pointcutDefinition.setSource(parserContext.extractSource(element));
			return pointcutDefinition;
		}
		//有pointcut-ref属性值
		else if (element.hasAttribute(POINTCUT_REF)) {
			String pointcutRef = element.getAttribute(POINTCUT_REF);
			if (!StringUtils.hasText(pointcutRef)) {
				parserContext.getReaderContext().error(
						"'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot());
				return null;
			}
			return pointcutRef;
		}
		else {
			parserContext.getReaderContext().error(
					"Must define one of 'pointcut' or 'pointcut-ref' on <advisor> tag.",
					element, this.parseState.snapshot());
			return null;
		}
	}

这个方法最后解析到了pointcut属性值,然后根据pointcut的类型给advisor的定义设置不同类型的参数:

if (pointcut instanceof BeanDefinition) {
				advisorDef.getPropertyValues().add(POINTCUT, pointcut);
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
			}
			else if (pointcut instanceof String) {
				advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef));
			}

在这之前我们漏掉了一个判断:

if (StringUtils.hasText(advisorBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
			}
			else {
				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
			}

不管是那个分支,最后都会把advisor的定义注册到IOC容器中。
到这里,配置文件就解析完了,这个过程不是很复杂,在下一篇文章中我们将看一下事务实例化的流程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值