Spring源码阅读-AOP(一)

451 阅读8分钟

前言

本篇文章我们将开始阅读springAOP相关的源码,因为aop的实现是基于动态代理机制的,包括jdk和cglib,因此如果对动态代理不了解的同学先建议去了解动态代理相关的内容,再返回过来阅读,这样才能更好的理解aop

阅读目标

  • 了解spring解析apo:config标签的过程,解析完之后产生什么对象
  • 了解spring是如何产生的代理对象
  • springAOP产生的代理对象是怎么执行的 下面我们就带着上面的问题进入spring的源码阅读过程。

spring解析apo:config标签的过程

首先,我们需要寻找阅读的入口,在spring中解析标签也就是解析xml配置文件的过程。如果对spring有所了解,或者看过我之前文章的同学就会知道,在sping中解析xml的入口就是一个类DefaultBeanDefinitionDocumentReader,我们可以看下这个类中的parseBeanDefinitions,就是解析配置文件的入口
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 {
					//使用自定义的规则解析标签节点,例如tx标签,aop标签等
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

上面这段代码就是获取根节点并根据不同的子节点用不同的解析规则解析,我们需要关注的是delegate.parseCustomElement这行代码,这个就是解析aop标签的入口,我们继续往下跟 就会进到BeanDefinitionParserDelegate类的parseCustomElement方法

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		// 获取命名空间URI类似:http://www.springframework.org/schema/aop
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//根据不同的命名空间uri获取不同的命名空间解析器,一个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;
		}
		// 使用对应的NamespaceHandler的解析方法
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

其中DefaultNamespaceHandlerResolver类的resolve方法主要有两个步骤

  • 找到对应的NamespaceHandler
  • 调用namespaceHandler的init方法对NamespaceHandler进行初始化 在这里我们解析aop标签对应的NamespaceHandler是AopNamespaceHandler,我们可以点进去看下初始化方法
public void init() {
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}

在这里我们可以清楚的看到初始化其实就是在registerBeanDefinitionParser中注册对应的解析器,而我们要解析的config标签使用的解析器就是ConfigBeanDefinitionParser, 因此,上面的parseCustomElement方法的最后一行代码进去就是两个步骤

  • 找到标签对应的BeanDefinitionParser
  • 调用标签对应的BeanDefinitionParse解析标签
public BeanDefinition parse(Element element, ParserContext parserContext) {
		// NamespaceHandler里面初始化了不同的BeanDefinitionParser来处理不同的自定义标签
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		// 调用指定自定义标签的解析器,完成具体解析工作
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

而查找对应的BeanDefinitionParser就是从beanDefinitionParser的map中找到对应的解析器,我们也一起看下

// 获取自定义的标签名称,在这里获取到的标签名字是config
		String localName = parserContext.getDelegate().getLocalName(element);
		// 根据标签名称找到对应的BeanDefinitionParser
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;

从初始化方法中我们就知道了,这里的parser就是ConfigBeanDefinitionParser这个类,因此我们就接着往下面看ConfigBeanDefinitionParser的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		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标签的代码,从这段代码中我们主要需要知道,在这里面都创建了哪些BeanDefinition,并且知道这些BeanDefinition的具体作用是什么

  • configureAutoProxyCreator我们跟进去这段代码,往下跟我们可以跟到AopNamespaceUtils的registerAspectJAutoProxyCreatorIfNecessary
public static void registerAspectJAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {
		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

从这里我们就可以知道,在这个方法中向容器中注册了AspectJAwareAdvisorAutoProxyCreator这个类,并且在useClassProxyingIfNecessary中设置了是否使用cglib代理和是否暴露最终代理。 那么这个类的作用是什么呢,这个类主要是用来产生代理对象。在这里我们就发现了解析的第一个类-AspectJAwareAdvisorAutoProxyCreator

  • 接下去的三个分支主要用来解析config下面的三个子标签aop:aspectaop:advisoraop:pointcut,在这里我们先说下aop:aspectaop:advisor这两子标签的区别
    • aop:aspect:使用的是spring整合AspectJ的方式实现AOP功能
    • aop:advisor:使用spring AOP自己的方式实现AOP功能
    • aop:pointcut:解析这个标签会产生一个AspectJExpressionPointcut对象,用于封装我们的切入点表达式。通过切入点表达式来确定需要对哪些类进行增强
  • 下面我们主要看解析aop:aspect的流程,也就是上面解析代码的最后一个分支parseAspect,我们跟进去看下
。。。
for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				// 判断当前节点是否是<aop:before>、<aop:after>、<aop:after-returning>、<aop:after-throwing method="">、<aop:around method="">
				if (isAdviceNode(node, parserContext)) {
					if (!adviceFoundAlready) {
						adviceFoundAlready = true;
						if (!StringUtils.hasText(aspectName)) {
							parserContext.getReaderContext().error(
									"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
									aspectElement, this.parseState.snapshot());
							return;
						}
						beanReferences.add(new RuntimeBeanReference(aspectName));
					}
					// 解析<aop:after>等五个子标签
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					beanDefinitions.add(advisorDefinition);
				}
			}
。。。

接着我们主要来看下具体解析这5个子标签的过程

private AbstractBeanDefinition parseAdvice(
			String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

		try {
			this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

			// create the method factory bean
			// 创建MethodLocatingFactoryBean对象:用于获取Advice增强类的Method对象
			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
			// MethodLocatingFactoryBean的targetBeanName为advice类的引用名称
			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
			// MethodLocatingFactoryBean的methodName为<aop:after>标签的method属性值(也就是advice方法名称)
			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
			methodDefinition.setSynthetic(true);

			// create instance factory definition
			// SimpleBeanFactoryAwareAspectInstanceFactory:用于创建增强类的实例
			RootBeanDefinition aspectFactoryDef =
					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
			//SimpleBeanFactoryAwareAspectInstanceFactory的aspectBeanName为advice类的引用名称
			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
			aspectFactoryDef.setSynthetic(true);

			// register the pointcut
			// 创建通知增强类的BeanDefinition对象(重点)
			AbstractBeanDefinition adviceDef = createAdviceDefinition(
					adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
					beanDefinitions, beanReferences);

			// configure the advisor
			// 通知器类的BeanDefinition对象
			RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
			advisorDefinition.setSource(parserContext.extractSource(adviceElement));
			// 给AspectJPointcutAdvisor属性值
			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
				advisorDefinition.getPropertyValues().add(
						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
			}

			// register the final advisor
			// 将AspectJPointcutAdvisor对应的beandefinition对象注册到IoC容器中
			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

			return advisorDefinition;
		}
		finally {
			this.parseState.pop();
		}
	}

首先上了就创建了两个类

  • MethodLocatingFactoryBean:增强方法的工厂bean,就是用来获取增强类的增强方法的method对象
  • SimpleBeanFactoryAwareAspectInstanceFactory:增强类的工厂bean,用于获取增强类的实例对象

说白了,这两个就是为了通过反射调用增强方法,接着我们看下创建通知增强类过程createAdviceDefinition

private AbstractBeanDefinition createAdviceDefinition(
			Element adviceElement, ParserContext parserContext, String aspectName, int order,
			RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
			List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

		// 根据通知类型的不同,创建不同的通知对象
		RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
		adviceDefinition.setSource(parserContext.extractSource(adviceElement));

		//添加统一的属性值
		adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
		adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);

		if (adviceElement.hasAttribute(RETURNING)) {
			adviceDefinition.getPropertyValues().add(
					RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
		}
		if (adviceElement.hasAttribute(THROWING)) {
			adviceDefinition.getPropertyValues().add(
					THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
		}
		if (adviceElement.hasAttribute(ARG_NAMES)) {
			adviceDefinition.getPropertyValues().add(
					ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
		}

		// 设置构造参数
		ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
		// 第一个参数:方法工厂对象的BeanDefinition
		cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

		// 解析<aop:before>、<aop:after>、<aop:after-returning>标签中的pointcut或者pointcut-ref属性
		Object pointcut = parsePointcutProperty(adviceElement, parserContext);
		// 第二个参数:切入点BeanDefinition
		if (pointcut instanceof BeanDefinition) {
			cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
			beanDefinitions.add((BeanDefinition) pointcut);
		}
		else if (pointcut instanceof String) {
			RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
			cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
			beanReferences.add(pointcutRef);
		}
		// 第三个参数:实例工厂BeanDefinition
		cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

		return adviceDefinition;
	}

这个类就创建了5个不同类型的通知对象,就是before,after,after-returning,around,after-throwing这5中,具体类型通过getAdviceClass方法获取,就是if-else判断,我们就不细看了,后面,就是为这5个通知增强对象设置属性值,其中两个是我们上面说的两个工厂bean,还有一个就是AspectJExpressionPointcut对象,这个在上面也说过了就不多说了。 而这5个对象的左右就是为了调用增强方法 最后,这些通知增强类会被封装的通知器AspectJPointcutAdvisor中。 至此我们的标签解析就全部解析完毕了,在解析过程中我们一个产生了十个对象也分析了这是个对象的作用。下面我总结一下这十个对象及其作用。

  • AspectJAwareAdvisorAutoProxyCreator:用来产生代理对象
  • MethodLocatingFactoryBean:增强方法的工厂bean,就是用来获取增强类的增强方法的method对象
  • SimpleBeanFactoryAwareAspectInstanceFactory:增强类的工厂bean,用于获取增强类的实例对象 *AspectJPointcutAdvisor:通知器封装通知增强类和切入点对象
    • AspectJExpressionPointcut:用于解析切入点表达式筛选出需要增强的类和方法
    • AspectJMethodBeforeAdvice:下面5个通知增强类为了调用对应的增强方法
    • AspectJAfterAdvice
    • AspectJAfterReturningAdvice
    • AspectJAfterThrowingAdvice
    • AspectJAroundAdvice

结语

本篇文章主要是阅读了springAOP标签的解析过程,分析解析过程中产生的类及其作用。针对我们开篇提出的第一个问题,我想我们应该是已经有了比较深入的了解,而下面的两个问题我们会在下一篇文章中,一起去了解。