【spring源码分析(三)】AOP源码分析---解析xml中的aop:config标签内容并存储在BeanDefinition

本文深入剖析Spring AOP的工作原理,从AOP配置文件解析入手,详细解读如何将AOP配置转换为Bean定义并注册到IoC容器的过程。涵盖了AOP切面、通知、连接点的定义与注册机制。

前期准备

分析AOP源码,首先看下AOP的一个应用例子:
UserService和UserServiceImpl实现类

public interface UserService {
	void saveUser();
	void updateUser();
}
@Service
public class UserServiceImpl implements UserService {

	@Override
	public void saveUser() {
		System.out.println("添加用户");
	}
	@Override
	public void updateUser() {
		System.out.println("修改用户");
	}

}
}

aop.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 配置目标对象 -->
	<bean class="sourcecode.aop.target.UserServiceImpl"></bean>
	<!-- 配置通知类 -->
	<bean id="myAdvice" class="sourcecode.aop.advice.MyAdvice"></bean>

	<!-- AOP配置 -->
	<aop:config>
		<!-- 配置AOP切面,切面是由通知和切入点组成的 -->
		<aop:aspect ref="myAdvice">
			<aop:before method="before"
				pointcut="execution(* *..*.*ServiceImpl.*(..))" />
		</aop:aspect>
	</aop:config>
</beans>

单元测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/spring-aop.xml")
public class TestAOP {

	@Autowired
	private UserService userService;

	@Test
	public void test() {
		userService.saveUser();
	}
}

可以通过上述例子,设置断点调试,一步步看下aop的原理。接下来开始具体分析源码。

一、AOP源码入口

前面有篇文章已经分析过spring的ioc的源码,再次找到DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 加载的Document对象是否使用了Spring默认的XML命名空间(beans命名空间)
		if (delegate.isDefaultNamespace(root)) {
			// 获取Document对象根元素的所有子节点(bean标签、import标签、alias标签和其他自定义标签context、aop等)
			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;
					// bean标签、import标签、alias标签,则使用默认解析规则
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {//像context标签、aop标签、tx标签,则使用用户自定义的解析规则解析元素节点
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

通过代码中注释可知,aop.xml文件中的<aop:config>标签,是通过调用delegate.parseCustomElement(ele)方法解析的,它的源码如下:

@Nullable
	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));
	}

之前aop.xml文件已经被解析成org.w3c.dom.Document,并且org.w3c.dom.Document以树的形式展示,一个节点就是一个Node(Element是Node的子接口)。

对于解析aop.xml文件中的<aop:config>标签,上述代码第3行,返回的字符串为"http://www.springframework.org/schema/aop"的String对象。
分析第7行,this.readerContext.getNamespaceHandlerResolver()其实返回的是DefaultNamespaceHandlerResolver类,然后调用该类的resolve方法,该行最终返回的是AopNamespaceHandler类。resolve方法参数为第3行获取到的String对象,方法源码如下:

@Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
		// 读取spring所有工程的META-INF/spring.handlers文件,获取namespaceUri和NamespaceHandler的映射关系
		Map<String, Object> handlerMappings = getHandlerMappings();
		// 获取 指定namespaceUri对应的namespaceHandler
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			// META-INF/spring.handlers文件中存储的value都是String类型的类名
			String className = (String) handlerOrClassName;
			try {
				// 根据类名通过反射获取到NamespaceHandler的Class类对象
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				// 实例化NamespaceHandler
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				// 调用NamespaceHandler类的init方法,初始化一些专门处理指定标签的BeanDefinitionParsers类
				namespaceHandler.init();
				// 将namespaceUri对应的String类型的类名,替换为NamespaceHandler对象,下一次再获取的话,就不会重复创建实例
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
			}
		}
	}

分析上述代码:第7行返回的是AopNamespaceHandler类。27行代码调用AopNamespaceHandler类的init方法,初始化一些专门处理指定标签的BeanDefinitionParsers类,init方法源码:

@Override
	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());
	}

分析上述代码:总共4个用于具体标签转换的Parser,分别为:
config–>ConfigBeanDefinitionParser
aspectj-autoproxy–>AspectJAutoProxyBeanDefinitionParser
scoped-proxy–>ScopedProxyBeanDefinitionDecorator
spring-configured–>SpringConfiguredBeanDefinitionParser

通过查看init方法中的其中一个方法registerBeanDefinitionParser可知,这些标签解析类是存储在AopNamespaceHandler的父类NamespaceHandlerSupport的map集合中,供后面使用,如下:

public abstract class NamespaceHandlerSupport implements NamespaceHandler {

	/**
	 * Stores the {@link BeanDefinitionParser} implementations keyed by the
	 * local name of the {@link Element Elements} they handle.
	 */
	private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();

回头再接着看BeanDefinitionParserDelegate类的parseCustomElement方法,第12行最终调用的是AopNamespaceHandler类的parse方法。因为通过查看AopNamespaceHandler类,类中无parse方法定义,所以,调用的其实是父类NamespaceHandlerSupport的parse方法。该方法源码如下:

@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

分析上述代码:第4行根据aop标签,获取对应的解析类。对于标签aop:config,第4行获取到它的解析类ConfigBeanDefinitionParser,第5行接着调用该类的parse方法。

二、AOP Bean定义加载(根据织入方式将<aop:before>转换成名为adviceDef的RootBeanDefinition)

接着分析,查看ConfigBeanDefinitionParser类的parse方法,如下:

/**
	 * element : 就是<aop:config>标签元素对象
	 */
	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		// 向IoC容器中注册AspectJAwareAdvisorAutoProxyCreator类的BeanDefinition:(用于创建AOP代理对象的)
		// BeanPostProcessor可以对实例化之后的bean进行一些操作
		// AspectJAwareAdvisorAutoProxyCreator 实现了BeanPostProcessor接口,可以对目标对象实例化之后,创建对应的代理对象
		configureAutoProxyCreator(parserContext, element);

		// 获取<aop:config>标签的子标签<aop:aspect>、<aop:advisor> 、<aop:pointcut>
		List<Element> childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			// 获取子标签的节点名称或者叫元素名称
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {// 解析<aop:pointcut>标签
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {// 解析<aop:advisor>标签
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {// 解析<aop:aspect>标签
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

分析上述代码,查看第28行,该方法是处理<aop:config>下的节点为<aop:aspect>,查看源码:

private void parseAspect(Element aspectElement, ParserContext parserContext) {
		// 获取<aop:aspect>标签的id属性值
		String aspectId = aspectElement.getAttribute(ID);
		// 获取<aop:aspect>标签的ref属性值
		String aspectName = aspectElement.getAttribute(REF);

		try {
			this.parseState.push(new AspectEntry(aspectId, aspectName));
			List<BeanDefinition> beanDefinitions = new ArrayList<>();
			List<BeanReference> beanReferences = new ArrayList<>();

			// 处理<aop:declare-parents>标签
			List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
			for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
				Element declareParentsElement = declareParents.get(i);
				beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
			}

			// We have to parse "advice" and all the advice kinds in one loop, to get the
			// ordering semantics right.
			
			// 获取<aop:aspect>标签的所有子标签
			NodeList nodeList = aspectElement.getChildNodes();
			boolean adviceFoundAlready = false;
			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));
					}
					//方法主要做了三件事:
					//	1、根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义
					//	2、将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义
					//	3、将advisorDefinition注册到DefaultListableBeanFactory中
					AbstractBeanDefinition advisorDefinition = parseAdvice(
							aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
					beanDefinitions.add(advisorDefinition);
				}
			}

			AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
					aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
			parserContext.pushContainingComponent(aspectComponentDefinition);

			// 得到所有<aop:aspect>下的<aop:pointcut>子标签
			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
				// 解析<aop:pointcut>子标签
				parsePointcut(pointcutElement, parserContext);
			}

			parserContext.popAndRegisterContainingComponent();
		}
		finally {
			this.parseState.pop();
		}
	}

接着进入第43行的parseAdvice方法

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
			// 创建方法工厂Bean的BeanDefinition对象:用于获取Method对象
			RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
			methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
			methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
			methodDefinition.setSynthetic(true);

			// create instance factory definition
			// 创建实例工厂BeanDefinition:用于创建增强类的实例
			RootBeanDefinition aspectFactoryDef =
					new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
			aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
			aspectFactoryDef.setSynthetic(true);
			
			//以上的两个BeanDefinition的作用主要是应用在以下这行代码
			// method.invoke(obj,args)

			// 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));
			advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
			if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
				advisorDefinition.getPropertyValues().add(
						ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
			}

			// register the final advisor
			// 将advisorDefinition注册到IoC容器中
			parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

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

接着进入第27行的createAdviceDefinition方法:

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

		// 根据通知类型的不同,分别创建对应的BeanDefinition对象(可以去看看getAdviceClass方法)
		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;
	}

分析上述代码:第7行,根据切入方式的不同,分别创建对应的BeanDefinition对象。
before对应AspectJMethodBeforeAdvice
After对应AspectJAfterAdvice
after-returning对应AspectJAfterReturningAdvice
after-throwing对应AspectJAfterThrowingAdvice
around对应AspectJAroundAdvice

到这里为止,aop:before标签对应的AbstractBeanDefinition创建成功。

三、AOP Bean定义加载(包装名为adviceDef的RootBeanDefinition 成 名字为advisorDefinition的RootBeanDefinition)

回来看ConfigBeanDefinitionParser类parseAdvice方法:

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

分析上述代码:第4行创建新的名字为advisorDefinition的RootBeanDefinition类。第5行是用于判断aop:aspect标签中是否存在"order"属性的(用来控制切入方法优先级的)。

四、AOP Bean定义加载(AopNamespaceHandler处理<aop:pointcut>标签)

接着看ConfigBeanDefinitionParser类parseAdvice方法中的处理<aop:pointcut>标签的代码:

// 得到所有<aop:aspect>下的<aop:pointcut>子标签
			List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
			for (Element pointcutElement : pointcuts) {
				// 解析<aop:pointcut>子标签
				parsePointcut(pointcutElement, parserContext);
			}

进入第5行的parsePointcut方法:

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
		String id = pointcutElement.getAttribute(ID);
		String expression = pointcutElement.getAttribute(EXPRESSION);

		AbstractBeanDefinition pointcutDefinition = null;

		try {
			this.parseState.push(new PointcutEntry(id));
			// 此处创建一个AspectJExpressionPointcut类对应的BeanDefinition对象,处理pointcut
			pointcutDefinition = createPointcutDefinition(expression);
			pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

			String pointcutBeanName = id;
			// 如果配置id属性,则走下面代码
			if (StringUtils.hasText(pointcutBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
			}
			// 如果没有配置id属性,则走下面代码
			else {
				pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
			}

			parserContext.registerComponent(
					new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
		}
		finally {
			this.parseState.pop();
		}

		return pointcutDefinition;
	}

进入第10行代码的createPointcutDefinition方法:

protected AbstractBeanDefinition createPointcutDefinition(String expression) {
		RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
		beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
		beanDefinition.setSynthetic(true);
		beanDefinition.getPropertyValues().add(EXPRESSION, expression);
		return beanDefinition;
	}

分析上述代码:<aop:pointcut>标签对应解析出来的BeanDefinition是RootBeanDefinition,且RootBenaDefinitoin中的Class是org.springframework.aop.aspectj.AspectJExpressionPointcut,且RootBeanDefinition对应的Bean被定义成原型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

和光同其尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值