Spring Framework源码(六):Spring AOP之解析标签

     首先看下spring framework配置例子:

<aop:config>
 <aop:aspect id="myaop" ref="log">
 <aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>
 <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
 <aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/>
 <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
 <aop:after pointcut-ref="mycut" method=“doReleaseAction"/>
 <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
 </aop:aspect>
</aop:config>

    服务器的servlet容器在加载web.xml文件启动后,会使用一个org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader类来读取applicationContext.xml文件,当解析aop标签时它会调用BeanDefinitionParserDelegate实例的parseCustomElement方法解析,这个代理类会寻找aop namespace中的handler即AopNamespaceHandler类并调用其resolve方法返回NamespaceHandler实例(这个过程中如果handlerMapping中没有这个handler则会调用AopNameSpaceHandler的init方法).

    我们来看下AopNamespaceHandler的初始化过程:

public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		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());
	}

    我们看到这里分别注册了<aop:config></aop:config>、<aop:aspectj-autoproxy></aop:aspectj-autoproxy>和<aop:scoped-proxy></aop:scoped-proxy>等主要标签的解析器。

    这里仅仅介绍Spring AOP(不解释Spring对AspectJ的支持),所以我们接下来看下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;
	}

    ConfigBeanDefinitionParser类的成员函数:

技术分享

     该方法第一步调用了configureAutoProxyCreator(parserContext, element)方法来注册一个AspectJAwareAdvisorAutoProxyCreator类型的bean。这个bean实现了BeanPostProcessor的子接口InstanitiationAwareBeanPostProcessor。实现方法如下图所示:

	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null;
			}
		}
		if (beanName != null) {
			TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
			if (targetSource != null) {
				this.targetSourcedBeans.add(beanName);
				Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
				Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
				this.proxyTypes.put(cacheKey, proxy.getClass());
				return proxy;
			}
		}
		return null;
	}

      它会在每次初始化bean时使用getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource)方法获取所有和bean相关联的advisor,并根据配置文件中advisor相关的配置选择能使用的advisor。接下来调用createProxy(beanClass, beanName, specificInterceptors)来创建代理(AOP使用代理模式来织入代码)。完成代理的创建后,AOP会根据配置文件中的节点类型来解析标签。这里分别会解析三种类型的标签:Pointcut、Advisor、Aspect。我们这里以Advisor为例看下标签的解析过程。

private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
		AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
		String id = advisorElement.getAttribute(ID);

		try {
			this.parseState.push(new AdvisorEntry(id));
			String advisorBeanName = id;
			if (StringUtils.hasText(advisorBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
			}
			else {
				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
			}

			Object pointcut = parsePointcutProperty(advisorElement, parserContext);
			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));
			}
		}
		finally {
			this.parseState.pop();
		}
	}

    这里解析了advisor标签的pointcut属性,并生成了一个DefaultBeanFactoryPointcutAdvisor的advisor并注册到parserContext中。

    总结一下Spring AOP的实现原理大致是:配置一个实现了InstantiationAwareBeanPostProcessor接口的bean,根据配置文件中关于Advisor和Pointcut的配置找到所有Advisor在bean初始化时根据需要为其生成代理。并在生成代理的过程中把advice织入在代理对象里。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。