在之前的文章中我们详细的介绍了关于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容器中。
到这里,配置文件就解析完了,这个过程不是很复杂,在下一篇文章中我们将看一下事务实例化的流程。