Spring源码情操陶冶-AOP之Advice通知类解析与使用
2017-10-25 20:34
746 查看
入口
根据前文讲解,我们知道通知类的解析主要建立在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; }
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
通知类生成的bean对象,其会设置
aspectName切面名、
declarationOrder序列等属性;且对其公共构造函数三个入参
Method/AspectJExpressionPointcut/AspectInstanceFactory都会进行设置
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我们放在后续的篇章讲解,敬请期待
相关文章推荐
- SpringAop源码情操陶冶-JdkDynamicAopProxy
- SpringAop源码情操陶冶-AspectJAwareAdvisorAutoProxyCreator
- Spring源码情操陶冶-自定义节点的解析
- Spring源码情操陶冶-tx:advice解析器
- Spring源码情操陶冶-AOP之ConfigBeanDefinitionParser解析器
- spring 核心与源码解析(3):AOP如何使用
- spring AOP 源码解析 及其实现原理
- 做一个合格的程序猿之浅析Spring AOP源码(十八) Spring AOP开发大作战源码解析
- Spring AOP源码解析——AOP动态代理原理和实现方式
- 使用springboot+springsession实现分布式session以及源码解析
- Spring源码情操陶冶-ContextLoader
- Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
- spring 源码探索 -- aop 标签解析和创建代理
- Spring中AOP源码深入解析
- Spring源码情操陶冶-AbstractApplicationContext
- Spring源码分析----AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现
- Spring.Net AOP 学习之旅: 使用 Throws Advice 处理异常
- spring.net 1.1.0 P3版本 中spring.core.dll 1.1.0.2版本对于AOP使用ADVICE中涉及使用ref/out参数的小问题
- Spring核心框架 - AOP的原理及源码解析
- spring aop自动配置源码解析