#yyds干货盘点# Spring源码三千问AdviceAdvisorAdvised都是什么接口?

Posted 老王学源码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# Spring源码三千问AdviceAdvisorAdvised都是什么接口?相关的知识,希望对你有一定的参考价值。

@TOC

前言

在看 Spring AOP 的源码时,经常可以看到 Advice、Advisor、Advised 等接口,它们长的很像,初次见面时,看着都有些让人犯糊涂,但是却拥有着不同的功能。
理解这些接口的作用,能够让我们更好的理解 Spring AOP。

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

  • Advice: org.aopalliance.aop.Advice
    "通知",表示 Aspect 在特定的 Join point 采取的操作。包括 “around”, “before” and “after 等

  • Pointcut: org.springframework.aop.Pointcut
    "切点",它是用来匹配连接点 Join point 的,可以说"Pointcut"表示的是"Join point"的集合。

  • Advisor: org.springframework.aop.Advisor
    "通知者",它持有 Advice,是 Spring AOP 的一个基础接口。

  • Advised: org.springframework.aop.framework.Advised
    AOP 代理工厂配置类接口。提供了操作和管理 Advice 和 Advisor 的能力。

下面我们通过类的继续关系图的方式,来从一个比较高的视角来观察一下 Advice、Advisor、Advised 接口。

Advice、Advisor、Advised 类图

  1. Advisor 可以获取到 Advice。
  2. PointcutAdvisor 可以获取到 Pointcut 和 Advice。
    Pointcut 可以匹配 join point,Advice 是具体的通知,所以,PointcutAdvisor 是一个功能完善接口。
  3. Advised 是 AOP 代理工厂配置类接口,它可以操作和管理 Advice 和 Advisor,它的实现类有 ProxyFactoryAspectJProxyFactory,用于生成AOP 代理类。

Advice

Advice 大体上分为了三类:BeforeAdvice、MethodInterceptor、AfterAdvice

可以看出,MethodInterceptor 是功能最强大的,它能够处理 BeforeAdvice、AroundAdvice、AfterAdvice、ThrowsAdvice、@Valid方法参数校验、@Async异步等

MethodInterceptor

MethodInterceptor 是功能最强大的,它能够处理 BeforAdvice、AroundAdvice、AfterAdvice、ThrowsAdvice、限流、@Valid方法参数校验、@Async异步、事务等
MethodInterceptor 除了可以处理 Advice 类的通知拦截外,还是一个比较能用的方法拦截接口。

例如:给接口 FooService 添加一个方法拦截器

FooService fooService = ProxyFactory.getProxy(FooService.class, new MyInterceptor());

在执行 FooService 的任意方法时,都会经过 MyInterceptor 的处理。

public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) 
    return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();

Advisor

Advisor 大体分为了三类:PointcutAdvisor、IntroductionAdvisor、PrototypePlaceholderAdvisor
其中,用到的最多的就是 PointcutAdvisor,它涵盖了绝大部分的 Advisor。

PointcutAdvisor

PointcutAdvisor 是一个功能完善接口,也是 Spring AOP 中使用最多的,它涵盖了绝大部分的 Advisor。

通过 PointcutAdvisor 可以获取到 Pointcut 和 Advice。Pointcut 可以完成 join point 的匹配,而 Advice 就是在 join point 上具体要执行的"通知"。

Advised


Advised 是 AOP 代理工厂配置类接口。

它的实现类有:ProxyFactory、AspectJProxyFactory、ProxyFactoryBean。
Advised 提供了操作和管理 Advice 和 Advisor 的能力,所以,ProxyFactory 实现 Advised 之后,就可以方便的获取和操作 Advice、Advisor,从而创建 AOP 代理类了。

Advised、ProxyConfig、AdvisedSupport 都是跟 Spring AOP 代理配置相关的接口和类,它们可以统一 Spring AOP 的代理配置。

Spring AOP 代理类可以转换为 Advised 类型

Spring AOP 在产生代理类时,会调用 AopProxyUtils#completeProxiedInterfaces(),将 AdvisedSpringProxy 添加为代理类实现的接口。
这样,所有的 Spring AOP 代理类都实现了 Advised 接口,所以,Spring AOP 代理类可以转换为 Advised 类型

既然 Spring AOP 代理类可以转换为 Advised 类型,那么代理类就可以操作 Advice 和 Advisor 了。
我们可以测试一下:

@RestController
@SpringBootApplication
//@EnableAspectJAutoProxy
public class AopApplication 
    @Resource
    private FoService foService;
    @Resource
    private FoService foService2;
    @Resource
    private XoService xoService;

    public static void main(String[] args) 
        SpringApplication app = new SpringApplication(AopApplication.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    

    @GetMapping("/status")
    public String status() 
        if (foService instanceof Advised) 
            // 动态添加 Advice 
            ((Advised) foService).addAdvice(new MethodBeforeAdvice() 
                @Override
                public void before(Method method, Object[] args, Object target) throws Throwable 
                    System.out.println("动态添加:before execute:" + method);
                
            );
        
        foService.doBiz();

        System.out.println(">>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<");
        // 测试对相同 bean 和不同 bean 的影响
        foService2.doBiz();
        xoService.doBiz();
        return ObjectUtils.identityToString(foService);
    

输出:

before...public java.lang.String com.kvn.aop.advised.FoService.doBiz()
动态添加:before execute:public java.lang.String com.kvn.aop.advised.FoService.doBiz()
FoooooooService#doBiz
finally...public java.lang.String com.kvn.aop.advised.FoService.doBiz()
>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<
before...public java.lang.String com.kvn.aop.advised.FoService.doBiz()
动态添加:before execute:public java.lang.String com.kvn.aop.advised.FoService.doBiz()
FoooooooService#doBiz
finally...public java.lang.String com.kvn.aop.advised.FoService.doBiz()
before...public java.lang.String com.kvn.aop.advised.XoService.doBiz()
XxxxxoService#doBiz
finally...public java.lang.String com.kvn.aop.advised.XoService.doBiz()

可以看出:
Spring AOP 代理类都可以转换为 Advised 接口,并可以使用它来操作 Advice 和 Advisor。
如果更改了 FooService 的 Advice 后,对所有注入 FooService 的地方都有影响,但是不会影响到其他类型的 bean。

原因分析:
Spring AOP 代理类的 Advice、Advisor 等 ProxyConfig 配置是保存在 ProxyFactory 中的。
由于 Spring AOP 代理对象每次都是通过 new ProxyFactory 来创建的,对于不同的 proxy bean 而言,ProxyConfig 代理配置都是各自持有,所以,对 bean 对应的 Advised 的操作只会体现在这一个类型的 bean 上面。
不同类型的 bean 之间是互不影响的。测试例子中也证明了这一点

小结

Advice、Advisor、Advised 都是 Spring AOP 相关的基本接口,理解这些接口的作用,对于更好的理解 Spring AOP 有很大的好处:

  • Advice: org.aopalliance.aop.Advice
    "通知",表示 Aspect 在特定的 Join point 采取的操作。包括 “around”, “before” and “after 等
    Advice 大体上分为了三类:BeforeAdvice、MethodInterceptor、AfterAdvice
    MethodInterceptor 是功能最强大的,是一个通用的方法拦截接口,它能够处理 BeforeAdvice、AroundAdvice、AfterAdvice、ThrowsAdvice、@Valid方法参数校验、@Async异步等

  • Advisor: org.springframework.aop.Advisor
    "通知者",它持有 Advice,是 Spring AOP 的一个基础接口。
    它的子接口 PointcutAdvisor 是一个功能完善接口,它涵盖了绝大部分的 Advisor。

  • Advised: org.springframework.aop.framework.Advised
    AOP 代理工厂配置类接口。提供了操作和管理 Advice 和 Advisor 的能力。
    它的实现类 ProxyFactory 是 Spring AOP 主要用于创建 AOP 代理类的核心类。

如果本文对你有所帮助,欢迎点赞收藏

有关 Spring 源码方面的问题欢迎一起交流,备注:51cto (vx: Kevin-Wang001)


博主好课推荐:

课程 地址
Dubbo源码解读——通向高手之路 https://edu.51cto.com/course/23382.html
正则表达式基础与提升 https://edu.51cto.com/course/16391.html

以上是关于#yyds干货盘点# Spring源码三千问AdviceAdvisorAdvised都是什么接口?的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点#Spring源码三千问@Lazy延迟加载与延迟注入有什么区别?

#yyds干货盘点# Spring源码三千问Bean的Scope有哪些?scope=request是什么原理?

#yyds干货盘点# Spring源码三千问BeanDefinition详解——什么是 RootBeanDefinition?merged bean definition 又是什么鬼?

#yyds干货盘点# Spring源码三千问Spring动态代理:什么时候使用的 cglib,什么时候使用的是 jdk proxy?

#yyds干货盘点# Spring 源码三千问同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错?

#yyds干货盘点# Spring源码三千问为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?