从 AbstractPointcutAdvisor 开始: Spring AOP 之 Advisor、PointcutAdvisor 介绍
从AOP开始说起
为了能够更好地将系统级别的代码抽离出来,去掉与对象的耦合,就产生了面向AOP(面向切面)。AOP全称 Aspect-Oriented Programming , 即面向切面编程。
OOP属于一种横向扩展,AOP是一种纵向扩展。
AOP依托于OOP,进一步将系统级别的代码抽象出来,进行纵向排列,实现低耦合。
几个概念的区别:
- AspectJ AspectJ是一个基于Java语言的AOP框架,它采用编译时增强,会将增强目标编译得到一个新的AOP代理类。
- Spring AOP Spring提供的AOP框架,使用了和AspectJ一样的注解,但是通过动态生成代理类的方式生成AOP代理类。
- JDK Dynamic AOP Spring AOP中AOP代理的一种实现,使用原生JDK反射和动态代理生成AOP代理,需要代理类与目标实现相同的接口。
- CGLib AOP Spring AOP中AOP代理的另一种实现,使用CGLib动态生成AOP代理类,需要代理类为目标类的子类。
AOP术语
- Aspect 一个切面,可以理解为一个切面模块,将相关的增强内容写进同一个切面。例如:一个负责日志的切面。
- Join Point 代表可以由AOP增强织入的程序执行节点。
- Advice 所要做的增强处理
- Pointcut 切入点,定义了将被Advice增强的一个或多个Join Point,可以使用正则表达式或模式匹配。
- Target object 被增强的目标对象
Adivce的种类
- Before 方法执行之前
- After 方法执行之后
- After-returning 方法成功执行完成之后
- After-throwing 方法抛出异常之后
- Around 环绕方法执行的整个周期
AOP 的核心模型
PointCut
即在哪个地方进行切入,它可以指定某一个点,也可以指定多个点。 比如类A的methord函数,当然一般的AOP与语言(AOL)会采用多用方式来定义PointCut,比如说利用正则表达式,可以同时指定多个类的多个函数。
Advice
在切入点干什么,指定在PointCut地方做什么事情(增强),打日志、执行缓存、处理异常等等。
Advisor/Aspect
PointCut Advice 形成了切面Aspect,这个概念本身即代表切面的所有元素。但到这一地步并不是完整的,因为还不知道如何将切面植入到代码中,解决此问题的技术就是PROXY。
Proxy
Proxy 即代理,其不能算做AOP的家庭成员,更相当于一个管理部门,它管理 了AOP的如何融入OOP。之所以将其放在这里,是因为Aspect虽然是面向切面核心思想的重要组成部分,但其思想的践行者却是Proxy,也是实现AOP的难点与核心据在。
通过下面的几个配置,来直观理解一下:
用xml配置Pointcut:
代码语言:javascript复制<bean id="Pointcut的Id" class="org.springframework.aop.support.NameMatchMethodPoint">
<property name="mappedName" vlaue="方法名"/>
</bean>
用xml配置Advisor,将pointcut与advice关联起来:
代码语言:javascript复制<bean id="Advisor的Id" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="要关联的Pointcut"/>
<property name="advice" ref="要关联的Advice"/>
</bean>
用xml配置代理类:
代码语言:javascript复制<bean id="生成的代理的Id" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="要代理的类(即需要劫持的类)"/>
<property>
<list>
<value>Advisor的Id</value>
</list>
</property>
</bean>
切点表达式,在早期Spring中的使用较多,一般这么使用:
代码语言:javascript复制<!-- 自己书写的日志切面 -->
<bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
<!-- 使用JDK的正则切点~~~~~~ -->
<bean id="regexPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>find.*</value><!-- 拦截所有方法名以find开始的方法 -->
</list>
</property>
</bean>
<!-- 切面 切点 组合成一个增强器即可~~~~~~ -->
<aop:config>
<aop:advisor advice-ref="logBeforeAdvice" pointcut-ref="regexPointcut"/>
</aop:config>
其实Spring为我们提供了一个简便的Advisor定义,可以方便的让我们同时指定一个JdkRegexpMethodPointcut和其需要对应的Advice,它就是RegexpMethodPointcutAdvisor,这样配置起来非常的方便
代码语言:javascript复制<bean id="logBeforeAdvice" class="com.fsx.aop.LogBeforeAdvice" />
<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice"/>
<property name="pattern" value="find.*"/>
</bean>
Spring AOP的执行过程
从代码执行角度来看,Spring AOP的执行过程分为四大步骤:
步骤一:Spring框架生成Advisor实例,可以是@Aspect,@Async等注解生成的实例,也可以是程序员自定义的AbstractAdvisor子类的实例。
步骤二:Spring框架在目标实例初始化完成后,也就是使用BeanPostProcessor
的postProcessAfterInitialization
方法,根据Advisor实例中切入点Pointcut
的定义,选择出适合该目标对象的Advisor
实例。
步骤三:Spring框架根据Advisor
实例生成代理对象。
步骤四:调用方法执行过程时,Spring 框架执行Advisor
实例的通知Advice
逻辑。
Spring中有大量的机制都是通过AOP实现的,比如说@Async的异步调用和@Transational。此外,用户也可以使用@Aspect注解定义切面或者直接继承AbstractPointcutAdvisor来提供切面逻辑。上述这些情况下,AOP都会生成对应的Advisor实例。
下面进入本文的正题:Advisor。
什么是 Advisor ?
Advisor是Spring AOP的顶层抽象,用来管理Advice和Pointcut(PointcutAdvisor和切点有关,但IntroductionAdvisor和切点无关)
注意:Advice是aopalliance对通知(增强器)的顶层抽象,请注意区分; Pointcut是Spring AOP对切点的抽象。切点的实现方式有多种,其中一种就是AspectJ。
接口定义:
代码语言:javascript复制public interface Advisor {
//@since 5.0 Spring5以后才有的 空通知 一般当作默认值
Advice EMPTY_ADVICE = new Advice() {};
// 该Advisor 持有的通知器
Advice getAdvice();
// 这个有点意思:Spring所有的实现类都是return true(官方说暂时还没有应用到)
// 注意:生成的Advisor是单例还是多例不由isPerInstance()的返回结果决定,而由自己在定义bean的时候控制
// 理解:和类共享(per-class)或基于实例(per-instance)相关 类共享:类比静态变量 实例共享:类比实例变量
boolean isPerInstance();
}
它的继承体系主要有如下两个:PointcutAdvisor和IntroductionAdvisor。
代码语言:javascript复制public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
PointcutAdvisor:和切点有关的Advisor,位于org.springframework.aop.support 包。 PointcutAdvisor它的实现类非常的多:
AbstractPointcutAdvisor:抽象实现
AbstractGenericPointcutAdvisor: 通用 PointcutAdvisor
DefaultPointcutAdvisor 通用的,最强大的Advisor 它是Spring提供的通用的,也被认为是最强大的Advisor。它可以把任意的两个Advice和Pointcut放在一起:
代码语言:javascript复制public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
private Pointcut pointcut = Pointcut.TRUE;
public DefaultPointcutAdvisor() {
}
// 若没有指定advice,默认Pointcut.TRUE,也就是说会匹配所有的方法的执行
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
// 显然,这个构造函数式非常强大的~~
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
}
更多介绍,参考:https://blog.csdn.net/f641385712/article/details/89303088
AbstractPointcutAdvisor 抽象实现类
代码语言:javascript复制package org.springframework.aop;
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
package org.springframework.aop.support;
import java.io.Serializable;
import org.aopalliance.aop.Advice;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.core.Ordered;
import org.springframework.util.ObjectUtils;
public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
private Integer order;
public AbstractPointcutAdvisor() {
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
if (this.order != null) {
return this.order;
} else {
Advice advice = this.getAdvice();
return advice instanceof Ordered ? ((Ordered)advice).getOrder() : 2147483647;
}
}
public boolean isPerInstance() {
return true;
}
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof PointcutAdvisor)) {
return false;
} else {
PointcutAdvisor otherAdvisor = (PointcutAdvisor)other;
return ObjectUtils.nullSafeEquals(this.getAdvice(), otherAdvisor.getAdvice()) && ObjectUtils.nullSafeEquals(this.getPointcut(), otherAdvisor.getPointcut());
}
}
public int hashCode() {
return PointcutAdvisor.class.hashCode();
}
}
通过继承 AbstractPointcutAdvisor,实现一个分布式锁注解的 Adviser:
代码语言:javascript复制import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.core.Ordered;
public class DistributedLockMethodPointcutAdvisor extends AbstractPointcutAdvisor {
private final LockAspectSupport lockInterceptor;
public DistributedLockMethodPointcutAdvisor(LockAspectSupport lockInterceptor) {
this.lockInterceptor = lockInterceptor;
}
@Override
public Pointcut getPointcut() {
return AnnotationMatchingPointcut.forMethodAnnotation(DistributedLock.class);
}
@Override
public Advice getAdvice() {
return lockInterceptor;
}
}
参考资料
https://segmentfault.com/a/1190000018281577
https://blog.csdn.net/f641385712/article/details/89303088
https://blog.csdn.net/infoflow/article/details/78177992
https://blog.csdn.net/f641385712/article/details/89178421