博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码情操陶冶-AOP之Advice通知类解析与使用
阅读量:4625 次
发布时间:2019-06-09

本文共 9047 字,大约阅读时间需要 30 分钟。

阅读本文请先稍微浏览下上篇文章,本文则对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 
节点 ** @param adviceElement
节点 ** @param parserContext 解析节点的上下文对象 ** @param beanDefinitions 与aspect相关的所有bean对象集合 ** @param beanReferences 与aspect相关的所有bean引用对象集合 **/ private AbstractBeanDefinition parseAdvice( String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List
beanDefinitions, List
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
beanDefinitions, List
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
  2. 通知类生成的bean对象,其会设置aspectName切面名、declarationOrder序列等属性;且对其公共构造函数三个入参Method/AspectJExpressionPointcut/AspectInstanceFactory都会进行设置

  3. 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配置如下


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

转载于:https://www.cnblogs.com/question-sky/p/7732214.html

你可能感兴趣的文章
图的邻接表存储
查看>>
2018 leetcode
查看>>
PHP中获取当前页面的完整URL
查看>>
Chapter 4 Syntax Analysis
查看>>
vi/vim使用
查看>>
讨论Spring整合Mybatis时一级缓存失效得问题
查看>>
Maven私服配置Setting和Pom文件
查看>>
Xcode10 library not found for -lstdc++ 找不到问题
查看>>
Mysql 8.0.13如何重置密码
查看>>
发布功能完成
查看>>
excel 合并单元格
查看>>
How to Create Modifiers Using the API QP_MODIFIERS_PUB.PROCESS_MODIFIERS
查看>>
待飞笔记(第一天 )
查看>>
解惑好文:移动端H5页面高清多屏适配方案
查看>>
traefik添加多证书
查看>>
忽略UserInterfaceState.xcuserstate
查看>>
ReactNative--Flexbox布局
查看>>
java实现读取文件大全
查看>>
[Cordova] 无法显示Alert视窗
查看>>
借助过度区选择阈值
查看>>