死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置 (7)

解析 <aop:advisor /> 标签,创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象,并指定了 Advice

private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) { // <1> 创建一个 DefaultBeanFactoryPointcutAdvisor 类型的 RootBeanDefinition 对象 RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class); // <2> 设置来源 advisorDefinition.setSource(parserContext.extractSource(advisorElement)); // <3> 获取 `advice-ref` 属性配置,必须配置一个对应的 Advice String adviceRef = advisorElement.getAttribute(ADVICE_REF); if (!StringUtils.hasText(adviceRef)) { parserContext.getReaderContext().error( "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot()); } else { // <4> 将 `advice-ref` 添加至 `adviceBeanName` 属性,也就是指向这个 Advice 引用 advisorDefinition.getPropertyValues().add( ADVICE_BEAN_NAME, new RuntimeBeanNameReference(adviceRef)); } // <5> 根据 `order` 配置为 RootBeanDefinition 设置优先级 if (advisorElement.hasAttribute(ORDER_PROPERTY)) { advisorDefinition.getPropertyValues().add( ORDER_PROPERTY, advisorElement.getAttribute(ORDER_PROPERTY)); } // <6> 返回刚创建的 RootBeanDefinition return advisorDefinition; }

获取 id 属性

注册第 1 步创建的 RootBeanDefinition

如果 id 不为空,则取其作为名称

否则,生成一个名称,也就是 className

获取这个 Advisor 对应的 Pointcut(也许就是一个 AspectJExpressionPointcut,也可能是引用的 Pointcut 的名称)

如果是 AspectJExpressionPointcut,第 1 步创建的 RootBeanDefinition 添加 pointcut 属性,指向这个 AspectJExpressionPointcut

否则,如果是一个引用的 Pointcut 的名称,第 1 步创建的 RootBeanDefinition 添加 pointcut 属性,指向这个名称对应的引用

<aop:aspect /> <beans> <aop:aspectj-autoproxy/> <bean/> <aop:config> <aop:aspect ref="aspectXmlConfig"> <aop:pointcut expression="execution(public * *(..))"/> <aop:around method="aroundAnyPublicMethod" pointcut-ref="anyPublicMethod"/> <aop:before method="beforeAnyPublicMethod" pointcut-ref="anyPublicMethod"/> <aop:before method="beforeAnyPublicMethod" pointcut="execution(public * *(..))"/> <aop:after method="finalizeAnyPublicMethod" pointcut-ref="anyPublicMethod"/> <aop:after-returning method="afterAnyPublicMethod" pointcut-ref="anyPublicMethod"/> <aop:after-throwing method="afterThrowingAnyPublicMethod" pointcut-ref="anyPublicMethod"/> </aop:aspect> </aop:config> </beans>

处理过程在 parseAspect(..) 方法中,如下:

private void parseAspect(Element aspectElement, ParserContext parserContext) { // <1> 获取 `id` 和 `ref` 属性 String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); try { this.parseState.push(new AspectEntry(aspectId, aspectName)); // <2> 定义两个集合 `beanDefinitions`、`beanReferences` // 解析出来的 BeanDefinition List<BeanDefinition> beanDefinitions = new ArrayList<>(); // 需要引用的 Bean List<BeanReference> beanReferences = new ArrayList<>(); // <3> 获取所有的 <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); // <3.1> 解析 <aop:declare-parents /> 子标签 // 解析出 DeclareParentsAdvisor 对象,添加至 `beanDefinitions` beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext)); } // We have to parse "advice" and all the advice kinds in one loop, to get the // ordering semantics right. // <4> 获取 <aop:aspectj /> 所有的子节点,遍历进行处理 NodeList nodeList = aspectElement.getChildNodes(); boolean adviceFoundAlready = false; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); // <4.1> 如果是 <aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing /> 标签,则进行处理 if (isAdviceNode(node, parserContext)) { // <4.2> 如果第一次进来,那么就是配置了 Advice,则 `ref` 必须指定一个 Bean,因为这些 Advice 的 `method` 需要从这个 Bean 中获取 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; } // <4.2.1> 往 `beanReferences` 添加需要引用的 Bean beanReferences.add(new RuntimeBeanReference(aspectName)); } // <4.3> 根据 Advice 标签进行解析 // 创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册 AbstractBeanDefinition advisorDefinition = parseAdvice( aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences); // <4.4> 添加至 `beanDefinitions` 中 beanDefinitions.add(advisorDefinition); } } // <5> 将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中 // 并放入 `parserContext` 上下文中,暂时忽略 AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); // <6> 获取所有的 <aop:pointcut /> 子标签,进行遍历处理 List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { // <6.1> 解析出 AspectJExpressionPointcut 对象并注册 parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }

解析过程大致如下:

获取 id 和 ref 属性

定义两个集合 beanDefinitions、beanReferences,分别保存解析出来的 BeanDefinition 和需要引用的 Bean

获取所有的 <aop:declare-parents /> 子标签,遍历进行处理

解析 <aop:declare-parents /> 子标签,解析出 DeclareParentsAdvisor 对象并注册,添加至 beanDefinitions

获取 <aop:aspectj /> 所有的子节点,遍历进行处理

如果是 <aop:around />、<aop:before />、<aop:after />、<aop:after-returning />、<aop:after-throwing /> 标签,则进行处理

如果第一次进来,那么就是配置了 Advice,则 ref 必须指定一个 Bean,因为这些 Advice 的 method 需要从这个 Bean 中获取

往 beanReferences 添加需要引用的 Bean

根据 Advice 标签进行解析,创建一个 AspectJPointcutAdvisor 对象,里面包含了 Advice 对象和对应的 Pointcut 对象,并进行注册

添加至 beanDefinitions 中

将上面创建的所有 Advisor 和引用对象都封装到 AspectComponentDefinition 对象中,并放入 parserContext 上下文中,暂时忽略

获取所有的 <aop:pointcut /> 子标签,进行遍历处理

解析出 AspectJExpressionPointcut 对象并注册,前面已经讲过了

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygssw.html