Spring源码情操陶冶-AOP之Advice通知类解析与使用

本文内容纲要:

- 入口

- ConfigBeanDefinitionParser#parseAdvice()-解析通知类并注册到bean工厂

- ConfigBeanDefinitionParser#createAdviceDefinition()-具体解析通知类

- 例子结尾

阅读本文请先稍微浏览下上篇文章Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器,本文则对aop模式的通知类作简单的分析

入口

根据前文讲解,我们知道通知类的解析主要建立在aop:aspect节点的解析上。废话少说我们直接观察ConfigBeanDefinitionParser#parseAdvice()方法

ConfigBeanDefinitionParser#parseAdvice()-解析通知类并注册到bean工厂

先奉上源码

/**

* Parses one of '{@code before}', '{@code after}', '{@code after-returning}',

* '{@code after-throwing}' or '{@code around}' and registers the resulting

* BeanDefinition with the supplied BeanDefinitionRegistry.

* @return the generated advice RootBeanDefinition

*/

/**

** 这稍微对入参作下备注

** @param aspectName 待绑定的切面名

** @param order 排序号

** @param aspectElement <aop:aspect>节点

** @param adviceElement <aop:advice>节点

** @param parserContext 解析节点的上下文对象

** @param beanDefinitions 与aspect相关的所有bean对象集合

** @param beanReferences 与aspect相关的所有bean引用对象集合

**/

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

// 解析advice节点中的"method"属性,并包装为MethodLocatingFactoryBean对象

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

// 关联aspectName,包装为SimpleBeanFactoryAwareAspectInstanceFactory对象

RootBeanDefinition aspectFactoryDef =

new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);

aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);

aspectFactoryDef.setSynthetic(true);

// register the pointcut

// 涉及point-cut属性的解析,并结合上述的两个bean最终包装为AbstractAspectJAdvice通知对象

AbstractBeanDefinition adviceDef = createAdviceDefinition(

adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,

beanDefinitions, beanReferences);

// configure the advisor,最终包装为AspectJPointcutAdvisor对象

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();

}

}

由代码可知,最终解析得到的bean对象为AspectJPointcutAdvisor.class类型的,其内部拥有Advice的接口对象属性,而具体的解析则需要查看ConfigBeanDefinitionParser#createAdviceDefinition()方法。

ConfigBeanDefinitionParser#createAdviceDefinition()-具体解析通知类

源码奉上

/**

* Creates the RootBeanDefinition for a POJO advice bean. Also causes pointcut

* parsing to occur so that the pointcut may be associate with the advice bean.

* This same pointcut is also configured as the pointcut for the enclosing

* Advisor definition using the supplied MutablePropertyValues.

*/

private AbstractBeanDefinition createAdviceDefinition(

Element adviceElement, ParserContext parserContext, String aspectName, int order,

RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,

List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

// 首先根据adviceElement节点分析出是什么类型的Advice。

RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));

adviceDefinition.setSource(parserContext.extractSource(adviceElement));

// 设置aspectName属性和declarationOrder属性

adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);

adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);

// 解析节点是否含有`returning`/`throwing`/`arg-names`,有则设置

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));

}

// 设置构造函数的入参变量

// Method/AspectJExpressionPointcut/AspectInstanceFactory三个入参

ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();

cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

// 解析point-cut属性

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;

}

  1. Advice接口类与节点对应关系如下,其均是AbstractAspectJAdvice.class的子类

  • aop:before对应AspectJMethodBeforeAdvice.class
  • aop:after对应AspectJAfterAdvice.class
  • aop:after-returning对应AspectJAfterReturningAdvice.class
  • aop:after-throwing对应AspectJAfterThrowingAdvice.class
  • aop:around对应AspectJAroundAdvice.class

  1. 通知类生成的bean对象,其会设置aspectName切面名、declarationOrder序列等属性;且对其公共构造函数三个入参Method/AspectJExpressionPointcut/AspectInstanceFactory都会进行设置
  2. parseAdvice()最主要的目的是使aspect对象中的方法与通知类结合起来,从而起到多样化的作用,下面的简单实例就是如此

例子结尾

public class TestAdvice {  

/**

* 在核心业务执行前执行,不能阻止核心业务的调用。

* @param joinPoint

*/

private void doBefore(JoinPoint joinPoint) {

System.out.println("-----doBefore().invoke-----");

System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");

System.out.println(" 可通过joinPoint来获取所需要的内容");

System.out.println("-----End of doBefore()------");

}

/**

* 手动控制调用核心业务逻辑,以及调用前和调用后的处理,

*

* 注意:当核心业务抛异常后,立即退出,转向After Advice

* 执行完毕After Advice,再转到Throwing Advice

* @param pjp

* @return

* @throws Throwable

*/

private Object doAround(ProceedingJoinPoint pjp) throws Throwable {

System.out.println("-----doAround().invoke-----");

System.out.println(" 此处可以做类似于Before Advice的事情");

//调用核心逻辑

Object retVal = pjp.proceed();

System.out.println(" 此处可以做类似于After Advice的事情");

System.out.println("-----End of doAround()------");

return retVal;

}

/**

* 核心业务逻辑退出后(包括正常执行结束和异常退出),执行此Advice

* @param joinPoint

*/

private void doAfter(JoinPoint joinPoint) {

System.out.println("-----doAfter().invoke-----");

System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");

System.out.println(" 可通过joinPoint来获取所需要的内容");

System.out.println("-----End of doAfter()------");

}

/**

* 核心业务逻辑调用正常退出后,不管是否有返回值,正常退出后,均执行此Advice

* @param joinPoint

*/

private void doReturn(JoinPoint joinPoint) {

System.out.println("-----doReturn().invoke-----");

System.out.println(" 此处可以对返回值做进一步处理");

System.out.println(" 可通过joinPoint来获取所需要的内容");

System.out.println("-----End of doReturn()------");

}

/**

* 核心业务逻辑调用异常退出后,执行此Advice,处理错误信息

* @param joinPoint

* @param ex

*/

private void doThrowing(JoinPoint joinPoint,Throwable ex) {

System.out.println("-----doThrowing().invoke-----");

System.out.println(" 错误信息:"+ex.getMessage());

System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");

System.out.println(" 可通过joinPoint来获取所需要的内容");

System.out.println("-----End of doThrowing()------");

}

}

对应的spring配置如下

<bean id="xmlHandler" class="com.jing.aop.TestAdvice" />  

<aop:config>

<aop:aspect id="aspect" ref="xmlHandler">

<aop:pointcut id="pointUserMgr" expression="execution(* com.tgb.aop.*.find*(..))"/>

<aop:before method="doBefore" pointcut-ref="pointUserMgr"/>

<aop:after method="doAfter" pointcut-ref="pointUserMgr"/>

<aop:around method="doAround" pointcut-ref="pointUserMgr"/>

<aop:after-returning method="doReturn" pointcut-ref="pointUserMgr"/>

<aop:after-throwing method="doThrowing" throwing="ex" pointcut-ref="pointUserMgr"/>

</aop:aspect>

</aop:config>


具体的如何触发相应的Advice我们放在后续的篇章讲解,敬请期待

本文内容总结:入口,ConfigBeanDefinitionParser#parseAdvice()-解析通知类并注册到bean工厂,ConfigBeanDefinitionParser#createAdviceDefinition()-具体解析通知类,例子结尾,

原文链接:https://www.cnblogs.com/question-sky/p/7732214.html

以上是 Spring源码情操陶冶-AOP之Advice通知类解析与使用 的全部内容, 来源链接: utcz.com/z/362891.html

回到顶部