阅读本文请先稍微浏览下上篇文章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我们放在后续的篇章讲解,敬请期待

Spring源码情操陶冶-AOP之Advice通知类解析与使用的更多相关文章

  1. Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器

    aop-Aspect Oriented Programming,面向切面编程.根据百度百科的解释,其通过预编译方式和运行期动态代理实现程序功能的一种技术.主要目的是为了程序间的解耦,常用于日志记录.事 ...

  2. Spring源码情操陶冶-自定义节点的解析

    本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...

  3. Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器

    本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...

  4. Spring源码情操陶冶-tx:advice解析器

    承接Spring源码情操陶冶-自定义节点的解析.本节关于事务进行简单的解析 spring配置文件样例 简单的事务配置,对save/delete开头的方法加事务,get/find开头的设置为不加事务只读 ...

  5. Spring源码情操陶冶-AbstractApplicationContext#finishBeanFactoryInitialization

    承接前文Spring源码情操陶冶-AbstractApplicationContext#registerListeners 约定web.xml配置的contextClass为默认值XmlWebAppl ...

  6. Spring源码情操陶冶-AbstractApplicationContext#registerListeners

    承接前文Spring源码情操陶冶-AbstractApplicationContext#onRefresh 约定web.xml配置的contextClass为默认值XmlWebApplicationC ...

  7. Spring源码情操陶冶-AbstractApplicationContext#onRefresh

    承接前文Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster 约定web.xml配置的contextClass ...

  8. Spring源码情操陶冶-AbstractApplicationContext#initApplicationEventMulticaster

    承接前文Spring源码情操陶冶-AbstractApplicationContext#initMessageSource 约定web.xml配置的contextClass为默认值XmlWebAppl ...

  9. Spring源码情操陶冶-AbstractApplicationContext#initMessageSource

    承接前文Spring源码情操陶冶-AbstractApplicationContext#registerBeanPostProcessors 约定web.xml配置的contextClass为默认值X ...

随机推荐

  1. 小巧玲珑:机器学习届快刀XGBoost的介绍和使用

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:张萌 序言 XGBoost效率很高,在Kaggle等诸多比赛中使用广泛,并且取得了不少好成绩.为了让公司的算法工程师,可以更加方便的 ...

  2. linux下修改rm命令防止误删除

    前言:相信很多朋友都遇到过在linux下用rm命令误删除文件的时候,此刻的心中仿佛有无数的羊驼在奔腾.那么怎么防止这种情况发生呢?当然是有方法的,我们可以写一个shell脚本,改变一下rm命令的作用. ...

  3. Oracle-一个中文汉字占几个字节?

    Oracle 一个中文汉字占用几个字节 Oracle 一个中文汉字 占用几个字节,要根据Oracle中字符集编码决定!!! 1. 如果定义为VARCHAR2(32 CHAR),那么该列最多就可以存储3 ...

  4. mybatis-mapper文件介绍

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-/ ...

  5. 一种Webconfig自动化升级方法

    1.方法功能 使用本方法,可以将开发环境最新版本的web.config结构与生产环境环境的config融合,而不用考虑两个config的版本差异值是多少.使用一种标记的方式,在开发环境webconfi ...

  6. CSS3 animation-timing-function steps()

    animation-timging-function 主要是控制css动画从开始到结束的速度. linear:线性过渡.等同于贝塞尔曲线(0.0, 0.0, 1.0, 1.0) ease:平滑过渡.等 ...

  7. AngularJS–Animations(动画)

    点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/   在AngularJS 1.3 中,给一些指令(eg:   ngRepeat,ngSw ...

  8. TETeLasr Cutting System 开机回零问题

    TETeLasr Cutting System 开机回零问题    :打开 "轴信息"    :打开 加工参数-->机器参数-->脉冲当量: X轴==4000 Y轴== ...

  9. 洗礼灵魂,修炼python(5)--python操作符,内置函数

    前面提到了BIF(内置函数)这个概念,什么是内置函数,就是python已经定义好的函数,不需要人为再自己定义,直接拿来就可以用的函数,那么都有哪些BIF呢? 可以在交互式界面(IDLE)输入这段代码, ...

  10. [SDOI2009]晨跑

    又是一道山东省选的题目,居然题目又十分水100行的代码就随随便便AC了. Description Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止 ...