从 AbstractPointcutAdvisor 开始: Spring AOP 之 Advisor、PointcutAdvisor 介绍

从 AbstractPointcutAdvisor 开始: Spring AOP 之 Advisor、PointcutAdvisor 介绍

从AOP开始说起

为了能够更好地将系统级别的代码抽离出来,去掉与对象的耦合,就产生了面向AOP(面向切面)。AOP全称 Aspect-Oriented Programming , 即面向切面编程。

OOP属于一种横向扩展,AOP是一种纵向扩展。

AOP依托于OOP,进一步将系统级别的代码抽象出来,进行纵向排列,实现低耦合。

几个概念的区别:

  1. AspectJ
    AspectJ是一个基于Java语言的AOP框架,它采用编译时增强,会将增强目标编译得到一个新的AOP代理类。

  2. Spring AOP
    Spring提供的AOP框架,使用了和AspectJ一样的注解,但是通过动态生成代理类的方式生成AOP代理类。

  3. JDK Dynamic AOP
    Spring AOP中AOP代理的一种实现,使用原生JDK反射和动态代理生成AOP代理,需要代理类与目标实现相同的接口。

  4. CGLib AOP
    Spring AOP中AOP代理的另一种实现,使用CGLib动态生成AOP代理类,需要代理类为目标类的子类。

AOP术语

  1. Aspect
    一个切面,可以理解为一个切面模块,将相关的增强内容写进同一个切面。例如:一个负责日志的切面。

  2. Join Point
    代表可以由AOP增强织入的程序执行节点。

  3. Advice
    所要做的增强处理

  4. Pointcut
    切入点,定义了将被Advice增强的一个或多个Join Point,可以使用正则表达式或模式匹配。

  5. Target object
    被增强的目标对象

Adivce的种类

  1. Before
    方法执行之前

  2. After
    方法执行之后

  3. After-returning
    方法成功执行完成之后

  4. After-throwing
    方法抛出异常之后

  5. 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:

<bean id="Pointcut的Id" class="org.springframework.aop.support.NameMatchMethodPoint">
    <property name="mappedName" vlaue="方法名"/>
</bean>

用xml配置Advisor,将pointcut与advice关联起来:

<bean id="Advisor的Id" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="pointcut" ref="要关联的Pointcut"/>
    <property name="advice" ref="要关联的Advice"/>
</bean>

用xml配置代理类:

<bean id="生成的代理的Id" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="要代理的类(即需要劫持的类)"/>
    <property>
        <list>
            <value>Advisor的Id</value>
        </list>
    </property>
</bean>

切点表达式,在早期Spring中的使用较多,一般这么使用:

<!-- 自己书写的日志切面 -->
<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,这样配置起来非常的方便

<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框架在目标实例初始化完成后,也就是使用BeanPostProcessorpostProcessAfterInitialization方法,根据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。

接口定义:

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。

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

PointcutAdvisor:和切点有关的Advisor,位于org.springframework.aop.support 包。
PointcutAdvisor它的实现类非常的多:

AbstractPointcutAdvisor:抽象实现

AbstractGenericPointcutAdvisor: 通用 PointcutAdvisor

DefaultPointcutAdvisor 通用的,最强大的Advisor
它是Spring提供的通用的,也被认为是最强大的Advisor。它可以把任意的两个Advice和Pointcut放在一起:

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 抽象实现类

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:


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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容