前言
上篇介绍了一些AOP的基础知识,本篇来说一下spring aop的源码分析。初读AOP时,茫然地不知道从哪里开始,可能是当初对IOC的理解不够深刻。后面发现其实AOP的入口和IOC一样,也是从两个方法开始。一个是AbstractApplicationContext的refresh方法中调用obtainFreshBeanFactory去读取配置文件进行初始化操作,另一个就是getBean方法了。
概述
在读IOC的时候,我们知道IOC分为两个过程:初始化和依赖注入,那么AOP就是在这两种过程加了特殊处理。
- 在初始化加载Bean定义时加了特殊处理
- 在依赖注入时加了特殊处理
避免篇幅过长,本篇介绍AOP的初始化操作时的特殊处理。
初始化加载Bean定义时的特殊处理
我们在IOC中分析知道,在refresh方法中调用obtainFreshBeanFactory方法,一步步跟进去,最终会把XML的配置信息解析成一个个BeanDefinition对象并注册到DefaultListableBeanFactory上。在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中把对定义信息的解析分为了默认元素解析和用户元素解析。AOP的话,走的就是用户元素解析的分支。下面我们以这个为起点,进行分析。
-
root标签是默认命名空间下的,而<aop:conf>标签不是,则会走parseCustomElement方法进行解析
//DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法
//默认命名空间下的标签走默认标签解析的方法(比如<bean>),用户标签走用户标签解析的方法(比如<aop:conf>)
//这里走用户定义的解析方式
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); } }
-
这个方法可以分成两部分,
-
先取获取命名空间处理器,对应到AOP就是AOPNamespaceHandler,
-
然后是AOPNameSpaceHandler解析标签的过程
-
//BeanDefinitionParserDelegate类的方法
//获取命名空间处理器对标签进行解析
//这里通过AopNamespaceHander对<aop:conf>、<aop:aspect>等标签进行解析
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele); 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)); }
- 获取相应的NamespaceHandler,这里也就是获取AopNamespaceHandler。我们需要回过去看一些代码
- 回到XmlBeanDefintionReader的registerBeanDefintions方法,这里创建了ReadContext。
- 然后跟进去,可以看到创建了DefaultNamespaceHandlerResolver对象,重要的是我们要看到DefaultNamespaceHandlerResolver的构造函数,这里赋值了一个默认的命名空间处理器的定位地址,即DEFAULT_HANDLER_MAPPINGS_LOCATION
//XmlBeanDefinitionReader类的方法
//解析Document成BeanDefinition,并注册到容器上
//这里我们关注的是创建命名空间解析器
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); }
//DefaultNamespaceHandlerResolver类的属性
//通过构造函数设置资源定位地址
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
- 上面我们回过去看了一些代码,主要是为了搞清楚资源定位路径的地址是什么。我们再回到上上步,看到调用了this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri),
- 跟进去看到其实就是加载META-INF/spring.handlers文件内容到Map中,然后根据namespace就可以找到对应的处理类了(AopNamespaceHandler)
- 然后调用处理器的init方法,对于AopNamespaceHandler就是注册了四个处理类
-
-
- config-->ConfigBeanDefinitionParser
- aspectj-autoproxy-->AspectJAutoProxyBeanDefinitionParser
- scoped-proxy-->ScopedProxyBeanDefinitionDecorator
- spring-configured-->SpringConfiguredBeanDefinitionParser
-
//DefaultNamespaceHandlerResolver类的方法
//得到合适的标签处理器,并对标签处理器进行初始化
//这里得到AopNamespaceHander,并注册了四个解析器。这里的
//getHandlerMappings方法可以跟进去看看,其实就是加载了上个步骤中分析的定制地址下的资源,得到处理器的全类名
//init方法作了一初始化操作,这里就是注册了四个解析器
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { 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) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } }
//这里是spring-aop项目的资源路径META-INF/spring.handlers下spring.hander文件的内容,正是通过这个拿到了处理类名的
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
- 通过上面,我们已经拿到处理器,并注册了四个解析器。下面就开始分析parse对解析过程。
- 首先拿到合适的解析器,这个分析的是<aop:config>标签,则用之前注册的ConfigBeanDefinitionParser进行解析
- 再进行解析
//NamespaceHandlerSupport类的方法
//获取合适的解析器结标签进行解析
//这里是ConfigBeanDefinitionParser对<aop:conf>进行解析
public BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext); }
//ConfigBeanDefinitionParser类的方法
//解析<aop:conf>标签,首先我们可以看到会向容器中注册一个实现了org.springframework.aop.config.internalAutoProxyCreator接口的实现类的BeanDefinition,
//然后再去循环解析下面的子节点
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; }
- 注册一个实现了InternalAutoProxyCreator接口的实现类的信息,根据优先级(其实就是在List的索引值大小),默认是AspectJAwareAdvisorAutoProxyCreator,这个类是整个AOP分析的重点,在后面分析依赖注入时重点进行分析。
//AopConfigUtils类的方法
//注册一个优先级最高的InternalAutoProxyCreator接口的实现类,默认是AspectJAwareAdvisorAutoProxyCreator
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
//这里我们可以看到,优先级其实很简单,就是在List中的索引值大小,AnnotationAwareAspectJAutoProxyCreator的优先级就最高,我们在JAVA CONFIG配置中就是使用的它。
//当然,我们自己也可以自己实现InternalAutoProxyCreator接口并添加到列表中
static { APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); }
- InternalAutoProxyCreator注册完成后,接下来的的第一个子节点的解析就是<aop:aspect>节点
- 解析<aop:declare-parents>,注册DeclareParentsAdvisor类的BeanDefinition
- 解析<aop:before>,注册AspectJPointcutAdvisor类的BeanDefinition
- 解析<aop:pointcut>,注册AspectJExpressionPointcut类的BeanDefinition
//ConfigBeanDefinitionParser类的方法
//解析<aop:aspect>标签
private void parseAspect(Element aspectElement, ParserContext parserContext) { String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); try { this.parseState.push(new AspectEntry(aspectId, aspectName)); List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>(); List<BeanReference> beanReferences = new ArrayList<BeanReference>(); 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. NodeList nodeList = aspectElement.getChildNodes(); boolean adviceFoundAlready = false; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); 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)); } 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); List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }
- 解析<aop:declare-parents>标签,创建DeclareParentsAdvisor类的BeanDefinition,并注册到DefaultLisableBeanFactory
//ConfigBeanDefinitionParser类的方法
//创建DeclareParentsAdvisor类的BeanDefinition,并注册到DefaultLisableBeanFactory中,这里我们看到了一种新的Advisor,而常用的五种类型都是AspectJPointcutAdvisor,具体使用可以参照官方文档
private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class); builder.addConstructorArgValue(declareParentsElement.getAttribute(IMPLEMENT_INTERFACE)); builder.addConstructorArgValue(declareParentsElement.getAttribute(TYPE_PATTERN)); String defaultImpl = declareParentsElement.getAttribute(DEFAULT_IMPL); String delegateRef = declareParentsElement.getAttribute(DELEGATE_REF); if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) { builder.addConstructorArgValue(defaultImpl); } else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) { builder.addConstructorArgReference(delegateRef); } else { parserContext.getReaderContext().error( "Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified", declareParentsElement, this.parseState.snapshot()); } AbstractBeanDefinition definition = builder.getBeanDefinition(); definition.setSource(parserContext.extractSource(declareParentsElement)); parserContext.getReaderContext().registerWithGeneratedName(definition); return definition; }
- 解析<aop:before>等五个标签,这里主要分为三个步骤
- 根据通知类型不同,创建不同Advice类的RootBeanDefinition
- 将上面的RootBeanDefinition再封装到AspectJPointcutAdvisor类的RootBeanDefinition
- 把上面的RootBeanDefinition注册到DefaultListableBeanFactory
//ConfigBeanDefinitionParser类的方法
//判断是否是一种类型的通知
private boolean isAdviceNode(Node aNode, ParserContext parserContext) { if (!(aNode instanceof Element)) { return false; } else { String name = parserContext.getDelegate().getLocalName(aNode); return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) || AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name)); } }
//ConfigBeanDefinitionParser类的方法
//解析五种通知类型,创建相应通知的BeanDefinition,并注册到DefaultLisableBeanFactory中
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 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 RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName); aspectFactoryDef.setSynthetic(true); // register the pointcut AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences); // configure the advisor 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 parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; } finally { this.parseState.pop(); }
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(); cav.addIndexedArgumentValue(METHOD_INDEX, methodDef); Object pointcut = parsePointcutProperty(adviceElement, parserContext); 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); } cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef); return adviceDefinition; }
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) { String elementName = parserContext.getDelegate().getLocalName(adviceElement); if (BEFORE.equals(elementName)) { return AspectJMethodBeforeAdvice.class; } else if (AFTER.equals(elementName)) { return AspectJAfterAdvice.class; } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) { return AspectJAfterReturningAdvice.class; } else if (AFTER_THROWING_ELEMENT.equals(elementName)) { return AspectJAfterThrowingAdvice.class; } else if (AROUND.equals(elementName)) { return AspectJAroundAdvice.class; } else { throw new IllegalArgumentException("Unknown advice kind [" + elementName + "]."); } }
- 解析切点,这里分为两步
- 创建一个AspectJExpressionPointcut类的RootBeanDefinition类的方法
- 注册RootBeanDefinition到DefaultListableBeanFactory上
//ConfigBeanDefinitionParser类的方法
//
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)); pointcutDefinition = createPointcutDefinition(expression); pointcutDefinition.setSource(parserContext.extractSource(pointcutElement)); String pointcutBeanName = id; if (StringUtils.hasText(pointcutBeanName)) { parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition); } else { pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); } parserContext.registerComponent( new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression)); } finally { this.parseState.pop(); } return pointcutDefinition; }
//ConfigBeanDefinitionParser类的方法
//创建AspectJExpressionPointcut类的RootBeanDefinition
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; }
总结
到这里我们已经结束初始化的分析了,在下一篇再看依赖注册时的特殊处理,有两个地方可以注意下
- 注册了一个AspectComponentDefinition的复杂组件定义
- 最后在注册AspectJExpressionPointcut的定义时,我们发现是原型的,并不是单例的
参考链接
- https://wenku.baidu.com/view/6ce3121da300a6c30c229f89.html(核心关注点与横切关注点)
- https://www.cnblogs.com/syf/archive/2012/05/09/2491780.html(OOP与AOP)
- https://blog.csdn.net/garfielder007/article/details/78057107(连接点用地概念)
- https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference(spring官方文档)
- http://www.cnblogs.com/xrq730/p/6753160.html((源码分析的参考文章)
- https://blog.csdn.net/elim168/article/details/78166296(@DeclareParents使用)