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织入在代理对象里。

时间: 2024-10-10 16:11:34

Spring Framework源码(六):Spring AOP之解析标签的相关文章

[自编号1-2]将spring framework源码导入Spring Tool Suite中

先讲基本步骤,如下: 去GitHub中下载spring framework的源码,https://github.com/spring-projects/spring-framework/releases 下载3.2.X其中的一个版本; 安装Gradle软件,官网下载,解压即可,设置GRADLE_HOME,和PATH. 命令行中运行gradle -v,检查一下是否正确安装配置: 命令行中运行spring framework根目录下的import-into-eclipse.bat 连续两次回车,就可

Spring Framework源码:spring beans之BeanFactory

先来认识一下两个重要容器BeanFactory和ApplicationContext的类图: 接下来,我们挨个对出现的类做个介绍: ??public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredT

Spring Framework源码(十三):SpringMVC之从ModelMap到页面渲染

SpringMVC在调用了Controller的方法后会返回ModelAndView对象,这个对象会被传回DispatcherServlet的doDispatch方法中.接下来再调用以下的方法渲染页面: processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); private void processDispatchResult(HttpServletRequest reque

spring framework源码下载并导入eclipse

工作后开始学习Javaspring,好多地方都不明白,工厂模式来管理bean,他是怎么管理的呢,于是想到了看源码,但是源码好难搞,下载下来了,不知从哪下手,花了很多时间,都没弄好,偶然在网上看到篇文章,这才搞定,拿出来分享.原文地址:http://blog.csdn.net/buyaore_wo/article/details/8977746 一. 准备工作 1.下载安装sts(springsource推荐使用), 毕竟人家的框架用他自家的ide是最好的,当然sts也是基本eclipse的, 下

Spring:源码解读Spring IOC原理

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

Spring:源码解读Spring IOC原理--(转载)

转自:http://www.cnblogs.com/ITtangtang/p/3978349.html 这篇文章个人觉得整理的很不错,很值得学习,为了方便自己学习和大家学习,特转载此文保留.请尊重原创~~ Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileS

Spring mvc 源码 和 Spring+springmvc+hibernate整合源码

本来想着再写下spring+springmvc+hibernate整合的文章 暂时就不写了 直接附上源码,有需要的直接下载看吧.还有前面文章中的springmvc源码  前面文章只是简单的说了下 搭建环境 访问页面成功.这个源码中有模拟的增删改查和文件上传,转换json 实体类验证等功能.有问题可以留言我. http://download.csdn.net/detail/qinyanbin123/8631175 springmvc源码下载地址 http://download.csdn.net/d

Spring Framework源码(十):SpringMVC之文件上传

我们这一章讲SpringMVC中文件上传的应用,首先我们还是从DispatcherServlet这个核心分发器开始讲起: processedRequest = checkMultipart(request); 还记得上一章讲doDispatch这个方法时见过的方法吧?现在我们来分析下这个方法的具体解析过程: protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartExcepti

spring beans源码解读之--bean definiton解析器

spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean definition解析器和xml文件格式的bean definition解析器. 我们先从简单的PropertiesBeanDefinitionReader开始深入挖掘. 1. PropertiesBeanDefinitionReader 属性文件bean definition解析器 1.1