目录

Pointcut

ClassFilter

MethodMatcher

Advisor

IntroductionAdvisor

PointcutAdvisor

AbstractPointcutAdvisor

Advice

AspectJ Advice

AbstractAspectJAdvice

Interceptor

AdvisorAdapter

TargetSource


在了解Spring事务源码时,根据调用跟踪,最后发现是TransactionAttributeSourcePointcut判断是否应用了@Transactional注解,来进行AOP。TransactionAttributeSourcePointcutPointcut接口的一个实现。

Pointcut

Pointcut的唯一作用就是筛选要拦截的目标方法,Pointcut只是一种筛选规则(或者叫过滤器)。

Pointcut由ClassFilter(类过滤器)和MethodMatcher(方法匹配器)两个组件组成。ClassFilter检查当前筛选规则与目标类是否匹配,MethodMatcher检查当前筛选规则与目标方法是否匹配。两个组件的共同作用,可以筛选出一个符合既定规则的方法的集合。通过Advisor(通知器)和Advice(通知)和Pointcut(切点)组合起来,就可以把指定的通知应用到指定的方法集合上。

Pointcut定义源码如下:

public interface Pointcut {/*** 返回当前切点持有的类过滤器,不能返回null值。* @return*/ClassFilter getClassFilter();/*** 返回当前切点持有的方法匹配器,不能返回null值* @return*/MethodMatcher getMethodMatcher();/*** 一个最简单切点的实现。* 这个切点由ClassFilter.TRUE和MethodMatcher.TRUE两个组件组成* 它匹配所有的目标类和目标方法。*/Pointcut TRUE = TruePointcut.INSTANCE;}

Spring Framework实现了以下PointCut。

其中比较重要的有:

  • AnnotationMatchingPointcut:

注解匹配切点。根据类上或方法上是否存在指定的注解判断切点的匹配性,如果没有显示指定注解,则匹配所有。

  • DynamicMethodMatcherPointcut:

动态方法匹配器切点。它本质上是一个方法匹配器,但同时具有了切点的功能。

  • ComposablePointcut:

可组合的切点。这种切点可以与或逻辑,任意组合其他的Pointcut、ClassFilter和MethodMatcher。其本质是通过ClassFilters和MethodMatchers两个工具类进行Pointcut内部组件的组合。

  • JdkRegexpMethodPointcut:

JDK正则表达式切点,即使用正则表达式描述方法的拦截规则和排除规则。

  • StaticMethodMatcherPointcut

静态方法匹配器切点。包括TransactionAttributeSourcePointcut(事务相关)

  • AspectJExpressionPointcut:

AspectJ切点表达式切点,ExpressionPointcut的一个具体实现。顾名思义,使用AspectJ的切点表达式描述筛选规则。表达式基本语法如下(非完整语法):

execution(<方法修饰符>? <方法返回值类型> <包名>.<类名>.<方法名>(<参数类型>) [throws <异常类型>]?)

ClassFilter

ClassFilter,用于约束一个Advisor,与指定的targetClass是否匹配。只有匹配的前提下,Advisor才能使用其内部持有的Advice对targetClass进行增强。

@FunctionalInterface
public interface ClassFilter {/**    * 判断指定接口或类是否匹配*/boolean matches(Class<?> clazz);ClassFilter TRUE = TrueClassFilter.INSTANCE;}

ClassFilter有4中简单方式的实现:

  1. TypePatternClassFilter:基于AspectJ的类型匹配实现
  2. AnnotationClassFilter:通过检查目标类是否存在指定的注解,决定是否匹配
  3. RootClassFilter:通过判断目标类是否是指定类型(或其子类型),决定是否匹配

除此之外,还有两种特殊方式的实现:

  1. DefaultIntroductionAdvisor:默认的引介通知器,它是一种通知器,但同时兼具了类过滤器的功能,且matches总返回true。它的作用是给所有bean追加指定接口。
  2. AspectJExpressionPointcut:AspectJ表达式切点(通过解析XML配置文件中的<aop:pointcut>元素生成的就是此类型的bean)。它是一种切点,但与一般的切点不同。一般的切点需要持有单独的ClassFilter和MethodMatcher。但是AspectJ表达式切点本身就兼具了这两个组件的功能。因为切点表达式,就是用来描述要代理的目标类和目标方法的。

MethodMatcher

方法匹配器。判断目标类的方法是否匹配。

两个参数的matches()被称为静态匹配,在匹配条件不是太严格时使用,可以满足大部分场景的使用,称之为静态的主要是区分为三个参数的matches()方法需要在运行时动态的对参数的类型进行匹配;两个方法的分界线就是boolean isRuntime()方法,进行匹配时先用两个参数的matches()方法进行匹配,若匹配成功,则检查boolean isRuntime()的返回值,若为true,则调用三个参数的matches()方法进行匹配(若两个参数的都匹配不中,三个参数的必定匹配不中)

public interface MethodMatcher {/**静态检查目标类的方法是否匹配。*/boolean matches(Method method, Class<?> targetClass);/*** 此MethodMatcher 是否动态的。*/boolean isRuntime();boolean matches(Method method, Class<?> targetClass, Object... args);/*** Canonical instance that matches all methods.*/MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

MethodMatcher 的几种主要实现:

  • DynamicMethodMatcher:动态方法匹配器。isRuntime()一直返回true。
  • StaticMethodMatcher:静态方法匹配器。isRuntime()一直返回false。
  • IntersectionMethodMatcher:And 匹配器,2个匹配器都匹配才匹配。
  • UnionMethodMatcher:Or匹配器,1个匹配器都匹配就匹配。
  • IntroductionAwareMethodMatcher:引介匹配器。

Advisor

把Advice(通知)应用到目标类。

public interface Advisor {Advice EMPTY_ADVICE = new Advice() {};Advice getAdvice();/**是与特定的实例关联还是所有实例共享。一般返回true,关联指定实例。*/boolean isPerInstance();}

Advisor分两大类:IntroductionAdvisor(引介通知器)和PointcutAdvisor(切点通知器)。两类Advisor都是为了增强targetClass,但是作用不一样。IntroductionAdvisor主要为了给targetClass追加接口(或者说追加更多的方法),这种增强属于类级别的增强;而PointcutAdvisor主要为了拦截方法,这种增强属于方法级别的增强

正是由于两类Advisor的增强级别不同,而导致了对ClassFilter的使用方式不同。IntroductionAdvisor进行类级别增强,因此只需要直接持有ClassFilter即可;而PointcutAdvisor进行方法级别增强,因此需要同时使用ClassFilter和MethodMatcher(方法匹配器)。PointcutAdvisor内部持有一个Pointcut,而Pointcut就是由ClassFilter和MethodMatcher组成的。

IntroductionAdvisor

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {ClassFilter getClassFilter();void validateInterfaces() throws IllegalArgumentException;}

PointcutAdvisor

public interface PointcutAdvisor extends Advisor {Pointcut getPointcut();}

AbstractPointcutAdvisor

AbstractPointcutAdvisor继承Ordered接口,增加了Advice(实现了Ordered)排序功能。

 public int getOrder() {if (this.order != null) {return this.order;}Advice advice = getAdvice();if (advice instanceof Ordered) {return ((Ordered) advice).getOrder();}return Ordered.LOWEST_PRECEDENCE;}

Advice

AOP的目的在于对目标类或目标方法的逻辑增强(如:日志逻辑、统计逻辑、访问控制逻辑等),那么Advice就代表要增强的具体逻辑。Advice接口由AOP联盟(aopalliance.org)定义,它只是一个标记接口,用来强调概念,没有定义任何功能(或者说没有定义增强方式或增强内容)。

//org.aopalliance.aop;
public interface Advice {}

AspectJ Advice

AspectJ Advice是使用AspectJ注解的Advice。对应的Advice接口都是标记接口。

AbstractAspectJAdvice

AspectJ Advice,调用Advice方法:invokeAdviceMethod。

 protected Object invokeAdviceMethod(@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)throws Throwable {return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));}//最终调用。object参数为代理对象。
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);

Interceptor

AOP联盟在Advice的基础上扩展定义了子接口——Interceptor(拦截器)。拦截器定义了通知的增强方式,也就是通过对Joinpoint(连接点)的拦截。

public interface Interceptor extends Advice {}

Interceptor接口也在强调概念而非功能,也是一个标记接口。 由Interceptor扩展出的ConstructorInterceptor和MethodInterceptor两个子接口,才具体定义了拦截方式。它们一个用于拦截构造方法,一个用于拦截普通方法。

public interface ConstructorInterceptor extends Interceptor  {Object construct(ConstructorInvocation invocation) throws Throwable;}
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation invocation) throws Throwable;}

spring框架并没有支持AOP联盟对构造方法的拦截,原因很简单,spring框架本身,通过BeanPostProcessor的定义,对bean的生命周期扩展已经很充分了。

MethodInterceptor只定义了增强方式,我们可以通过实现此接口,自定义具体的增强内容。当然,spring框架也提供了3种预定义的增强内容——BeforeAdvice(前置通知)、AfterAdvice(后置通知)和DynamicIntroductionAdvice(动态引介通知)。BeforeAdvice和AfterAdvice更确切地说是定义了增强内容的执行时机(方法调用之前还是之后);而DynamicIntroductionAdvice比较特殊,它可以编辑目标类要实现的接口列表。最后,spring预定义的通知还是要通过对应的适配器,适配成MethodInterceptor接口类型的对象(如:MethodBeforeAdviceInterceptor负责适配MethodBeforeAdvice)。

重点介绍几个常用拦截器:

  1. MethodBeforeAdviceInterceptor:
            MethodBeforeAdvice(前置通知,其父接口是BeforeAdvice)接口的适配器,用于支持spring预定义的前置通知,在目标方法调用前调用MethodBeforeAdvice.before()。
  2. AspectJAfterAdvice :
            AspectJ框架的后置通知实现,在目标方法执行结束后,return之前,调用配置指定的方法(注意:此方法调用被写在finally块中,无论如何都会得到执行)。
  3. AfterReturningAdviceInterceptor :
            AfterReturningAdvice接口的适配器,用于支持spring预定义的后置通知,在目标方法执行结束后,return之前,调用AfterReturningAdvice.afterReturning()执行(注意:如果目标方法抛出异常,则不会执行这个方法)。
  4. AspectJAfterThrowingAdvice :
            AspectJ框架的异常通知,当目标方法执行时产生异常的时候,指定配置指定的方法。
  5. AspectJAroundAdvice :
            AspectJ框架的环绕通知,直接执行配置指定的方法。
  6. ThrowsAdviceInterceptor :
            spring框架预定义的异常通知的适配器,此适配器接受任意类型的对象,但是要求对象所在类必须声明public的名称为afterThrowing,且参数个数为1个或4个,且最后一个参数为Throwable类型的方法。该适配器会保存该Throwable对象的实际类型到该方法之间的映射,当目标方法执行产生异常时,根据产生的异常类型找到对应的通知方法进行调用。
  7. DelegatingIntroductionInterceptor :
            通过构造方法传入指定的引介对象,每当调用的目标方法是引介接口定义的方法时,都会调用该引介对象的对应方法。
  8. DelegatePerTargetObjectIntroductionInterceptor :
            通过构造函数传入指定的引介接口和接口对应的实现类,该拦截器会为每个目标对象创建新的引介对象(通过调用实现类的默认无参构造)。当调用的方法是引介接口定义的方法时,则调用该新建的引介对象对应的方法。

AdvisorAdapter

Advisor的适配器。默认有3个实现:AfterReturningAdviceAdapter,MethodBeforeAdviceAdapter,ThrowsAdviceAdapter。

public interface AdvisorAdapter {
//是否支持boolean supportsAdvice(Advice advice);
//返回拦截器MethodInterceptor getInterceptor(Advisor advisor);
}

TargetSource

TargetSource(目标源)是被代理的target(目标对象)实例的来源。TargetSource被用于获取当前MethodInvocation(方法调用)所需要的target(目标对象),这个target通过反射的方式被调用(如:method.invode(target,args))。换句话说,proxy(代理对象)代理的不是target,而是TargetSource,这点非常重要!!!

那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

接下来要说的另外一点,可能会颠覆你的既有认知:TargetSource组件本身与SpringIoC容器无关,换句话说,target的生命周期不一定是受spring容器管理的,我们以往的XML中的AOP配置,只是对受容器管理的bean而言的,我们当然可以手动创建一个target,同时使用Spring的AOP框架(而不使用IoC容器)。

public interface TargetClassAware {Class<?> getTargetClass();}public interface TargetSource extends TargetClassAware {/**返回targets类型。*/@Override@NullableClass<?> getTargetClass();/**target不变则返回true。getTarget返回同一个对象。*/boolean isStatic();/**Return a target instance*/@NullableObject getTarget() throws Exception;/**释放target对象(getTarget获取到的)*/void releaseTarget(Object target) throws Exception;}

不同的TargetSource实现类主要解决的是每次调用getTarget怎么返回的对象实例。

TargetSource包含4个简单实现和3大类实现。

四个简单实现包括:

  1. EmptyTargetSource:
    静态目标源,当不存在target目标对象,或者甚至连targetClass目标类都不存在(或未知)时,使用此类实例。
  2. HotSwappableTargetSource:
    动态目标源,支持热替换的目标源,支持spring应用运行时替换目标对象。
  3. JndiObjectTargetSource:
    spring对JNDI管理bean的支持,static属性可配置。
  4. SingletonTargetSource:
    静态目标源,单例目标源。Spring的AOP框架默认为受IoC容器管理的bean创建此目标源。换句话说,SingletonTargetSource、proxy与目标bean三者的声明周期均相同。如果bean被配置为prototype,则spring会在每次getBean时创建新的SingletonTargetSource实例。

三大类实现包括:

  1. AbstractBeanFactoryBasedTargetSource:
    此类目标源基于IoC容器实现,也就是说target目标对象可以通过beanName从容器中获取。此类又扩展出:
    (1)SimpleBeanTargetSource:简单实现,直接调用getBean从容器获取目标对象;
    (2)LazyInitTargetSource:延迟初始化目标源,子类可重写postProcessTargetObject方法后置处理目标对象;
    (3)AbstractPrototypeBasedTargetSource:原型bean目标源,此抽象类可确保beanName对应的bean的scope属性为prototype。其子类做了简单原型、池化原型、线程隔离原型这3种实现。
  2. AbstractRefreshableTargetSource:
    可刷新的目标源。此类实现可根据配置的刷新延迟时间,在每次获取目标对象时自动刷新目标对象。
  3. AbstractLazyCreationTargetSource:
    此类实现在调用getTarget()获取时才创建目标对象。

Spring AOP源码解析(一)——核心概念相关推荐

  1. Spring AOP源码解析-拦截器链的执行过程

    一.简介 在前面的两篇文章中,分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在得到了 bean 的代理对象,且通知也以合适的方式插在了目标方 ...

  2. Spring AOP源码解析——AOP动态代理原理和实现方式

    2019独角兽企业重金招聘Python工程师标准>>> Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和 ...

  3. Spring AOP源码解析——专治你不会看源码的坏毛病!

    昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些. 原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们, ...

  4. Spring AOP源码解析(二)—— AOP引入

    目录 配置类 AopAutoConfiguration AspectJAutoProxyingConfiguration ClassProxyingConfiguration @EnableAspec ...

  5. Spring : Spring AOP源码解析

    1. 美图 2. 使用 参考:SpringBoot之AOP之基本使用 3.寻找 <aop:aspectj-autoproxy/> 注解对应的解析器 但凡注解都有对应的解析器,以用来解析该注 ...

  6. Spring AOP源码解析(三)—— AOP引入(续)

    目录 AbstractAspectJAdvisorFactory AspectJAnnotation ReflectiveAspectJAdvisorFactory getAdvisors getAd ...

  7. Spring AOP源码(1)—<aop:config/>AOP配置标签解析【一万字】

      基于最新Spring 5.x,对Spring AOP中的<aop:config/>标签的解析源码进行了详细分析,这是Spring AOP源码的入口!   此前我们已经详细学习了Spri ...

  8. Spring AOP 源码系列(一)解析 AOP 配置信息

    在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读. 关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐 ...

  9. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

最新文章

  1. 产品经理利器之axure rp
  2. 谢百三:房价上涨的九大原因
  3. phpcms后台系统怎么去掉html目录_电子笔记本 | 好记性胜过烂笔头?基于python3的知识管理系统...
  4. java判断闰年通过多态方法_04748《Java语言程序设计》实验指导书.doc
  5. 第E题 转换任意进制 (java方法直接解)==输入一个十进制数N,将它转换成R进制数输出
  6. MySQL蜜罐在护网中提取攻击者微信ID
  7. 电脑telnet工具如何开启
  8. perform指标分析_Perform+3D-入门实战指导.ppt
  9. PLC可编程控制器、单片机开发应用及电气控制综合实训装置
  10. Windows7下IPV6设置方法详解
  11. 【爬虫教程】吐血整理,最详细的爬虫入门教程~
  12. C语言在一个数组中查找一个数字
  13. r910服务器增加内存,dellr910服务器硬件手册及安装方法
  14. 智能车浅谈——过程通道篇
  15. 三维CAD设计软件的核心技术解析----工业软件讲坛第二次讲座
  16. GuLi商城-在线打开pdm文件
  17. 企业运维之 CDN 内容分发网络
  18. arduino烧录esp8266出错:error: espcomm_upload_mem failed
  19. 图的最小生成树-Kruskal算法
  20. 【2013Esri中国用户大会】盘点影像技术三大亮点

热门文章

  1. 【SRX】RE与PFE策略不同步,导致Commit失败-----案例分析
  2. JavaScript高级程序设计学习(六)之设计模式
  3. 不要滥用UNLOGGED table 和 hash index
  4. NSURLSession
  5. maven java 编译乱码
  6. MFC中静态文本控件显示的几种实现方式
  7. 7个管理和优化网站资源的 Python 工具
  8. 信息安全不可错过的30门实验
  9. 延迟队列Delay Queue
  10. 幻读(phantom read)