Spring 中一个少见的引介增强 IntroductionAdvisor

这篇具有很好参考价值的文章主要介绍了Spring 中一个少见的引介增强 IntroductionAdvisor。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们平时做 AOP 开发的时候,基本上都是增强某一个方法,在某一个方法执行之前或者执行之后做一些事情,这种叫做 PointcutAdvisor,实际上,Spring 中的 Advisor 大致可以分为两种类型,除了 PointcutAdvisor 之外,还有另外一种 Advisor 叫做 IntroductionAdvisor,因为最近想和小伙伴们聊一聊 Spring AOP 的源码,看源码有一个前提就是得先掌握 Spring 的各种用法,这样看源码的时候往往就有一种醍醐灌顶的感觉,否则看源码的时候就容易懵!

1. 实践

不同于 PointcutAdvisor,IntroductionAdvisor 这种增强主要是针对一个类来增强。

接下来松哥写一个简单的案例,小伙伴们来看下 IntroductionAdvisor 到底做了什么工作。

假设我有一个 Animal 接口,如下:

public interface Animal {    void eat();}

复制代码

这个动物具备吃的能力。

现在我还有一个 Dog,如下:

public interface Dog {    void run();}public class DogImpl implements Dog{    @Override    public void run() {        System.out.println("Dog run");    }}

复制代码

Dog 具备跑的能力,注意,Dog 和 Animal 之间并无继承/实现的关系。

现在,我们通过 Spring 中的 IntroductionAdvisor,就能让 Dog 具备 Animal 的能力,我们来看下具体怎么做。

首先,我们先来开发一个 Advice,这个 Advice 同时也是 Animal 接口的实现类,如下:

public class AnimalIntroductionInterceptor implements IntroductionInterceptor, Animal {    @Override    public Object invoke(MethodInvocation invocation) throws Throwable {        if (implementsInterface(invocation.getMethod().getDeclaringClass())) {            return invocation.getMethod().invoke(this, invocation.getArguments());        }        return invocation.proceed();    }
    @Override    public void eat() {        System.out.println("Animal eat");    }
    @Override    public boolean implementsInterface(Class<?> intf) {        return intf.isAssignableFrom(this.getClass());    }}

复制代码

跟普通 AOP 一样,当目标方法被拦截下来的时候,这里的 invoke 方法会被触发,在 invoke 方法中我们需要先调用 implementsInterface 方法进行判断,如果被拦截下来的方法所属的类是 Animal 的话,即 implementsInterface 方法返回 true 的情况(this 其实就是 Animal),那么就直接获取到 method 对象然后通过反射去调用就行了,这样会就会导致这里的 eat 方法被触发;否则,说明是被拦截下来的方法本身,那么就调用 invocation.proceed(); 让拦截器链继续往下执行即可。

接下来我们来定义 Advisor:

@Componentpublic class DogIntroductionAdvisor implements IntroductionAdvisor {    @Override    public ClassFilter getClassFilter() {        return new ClassFilter() {            @Override            public boolean matches(Class<?> clazz) {                return Dog.class.isAssignableFrom(clazz);            }        };    }
    @Override    public void validateInterfaces() throws IllegalArgumentException {
    }
    @Override    public Advice getAdvice() {        return new AnimalIntroductionInterceptor();    }
    @Override    public boolean isPerInstance() {        return true;    }
    @Override    public Class<?>[] getInterfaces() {        return new Class[]{Animal.class};    }}

复制代码

这里有几个方法需要实现:

  1. getClassFilter:哪些类需要拦截在这里配置,ClassFilter 松哥在上篇文章中已经讲过了,这里只需要返回被拦截的类就行了,不需要具体到哪个方法被拦截。

  2. getAdvice:这个就是返回拦截下来后执行的通知,我们就返回前面定义的通知即可,这里有一个要求,就是 这个 Advice 需要实现 Animal 接口。

  3. getInterfaces:这个方法还比较重要,生成代理对象的时候,代理对象需要实现哪些接口,就是从这个地方定义的,这里返回 Animal,所以将来我拿到手的代理对象就实现了 Animal 接口,就能调用 Animal 中的方法了。

  4. isPerInstance:这个方法暂时没有实现,返回 true 就行了。

  5. validateInterfaces:这个方法是做接口校验的,我这里就不校验了。

好啦,我的代码现在就写好了,我们来测试看下:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("introduction.xml");Dog dog = ctx.getBean(Dog.class);dog.run();System.out.println("Animal.class.isAssignableFrom(dog.getClass()) = " + Animal.class.isAssignableFrom(dog.getClass()));Animal animal = (Animal) dog;animal.eat();

复制代码

执行结果如下:

Spring 中一个少见的引介增强 IntroductionAdvisor,spring,java,sql

我们拿到手的 dog 对象其实也是一个 Animal。

这就是 Spring AOP 中的 IntroductionAdvisor,当一个类需要具备另一个类的能力的时候,可以使用 IntroductionAdvisor。

2. 源码分析

那么这一切是怎么实现的呢?

因为这篇文章我主要是想和小伙伴们分享 IntroductionAdvisor 的知识点,所以关于 AOP 完整的创建流程我先不说,在后续的文章中我会和大家做一个详细介绍,我今天就来和大家聊一聊在 Spring AOP 执行的过程中,究竟是如何处理 IntroductionAdvisor 的。

Spring AOP 中创建代理对象,一般是通过后置处理器来完成,从 AbstractAutoProxyCreator#postProcessAfterInitialization 方法开始,大致时序图如下:

Spring 中一个少见的引介增强 IntroductionAdvisor,spring,java,sql

我们就从 buildProxy 方法开始看起吧,这个方法看名字就知道是用来构建代理对象的。

AbstractAutoProxyCreator#buildProxy:

private Object buildProxy(Class<?> beanClass, @Nullable String beanName,    @Nullable Object[] specificInterceptors, TargetSource targetSource, boolean classOnly) {  //...  Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);  proxyFactory.addAdvisors(advisors);  //...  return (classOnly ? proxyFactory.getProxyClass(classLoader) : proxyFactory.getProxy(classLoader));}

复制代码

这里有一个 buildAdvisors 方法,这个方法是用来处理 Advisor 的,我们自定义的 DogIntroductionAdvisor 将在这里被读取进来,然后将之添加到 proxyFactory 对象中,在添加的过程中,会进行一些额外的处理,proxyFactory#addAdvisors 最终会来到 AdvisedSupport#addAdvisors 方法中:

public void addAdvisors(Collection<Advisor> advisors) {  if (!CollectionUtils.isEmpty(advisors)) {    for (Advisor advisor : advisors) {      if (advisor instanceof IntroductionAdvisor introductionAdvisor) {        validateIntroductionAdvisor(introductionAdvisor);      }      this.advisors.add(advisor);    }    adviceChanged();  }}

复制代码

在这里会遍历所有的 Advisor,判断类型是不是 IntroductionAdvisor 类型的,我们自定义的 DogIntroductionAdvisor 恰好就是 IntroductionAdvisor 类型的,所以会进一步调用 validateIntroductionAdvisor 方法,如下:

private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {  advisor.validateInterfaces();  Class<?>[] ifcs = advisor.getInterfaces();  for (Class<?> ifc : ifcs) {    addInterface(ifc);  }}public void addInterface(Class<?> intf) {  if (!this.interfaces.contains(intf)) {    this.interfaces.add(intf);    adviceChanged();  }}

复制代码

小伙伴们看一下,advisor.getInterfaces(); 实际上就调用到我们自定义的 DogIntroductionAdvisor 中的 getInterfaces 方法了,所以这里会返回 Animal 接口,然后这里会把 Animal 接口存入到 interfaces 这个变量中,将来在生成 AOP 对象的时候会用到。

好啦,现在回到 buildProxy 方法中,该方法最终会执行到 proxyFactory.getProxy 方法,该方法最终执行的时候,要么是 JDK 动态代理,要么是 CGLIB 动态代理,我们分别来说一下。

2.1 JDK 动态代理

先说如果是 JDK 动态代理,那么 proxyFactory.getProxy 方法就需要构建一个 JdkDynamicAopProxy 出来,如下:

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {  this.advised = config;  this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);  findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);}

复制代码

参数 config 中就包含了我们前面说的要实现的接口,所以这里 proxiedInterfaces 变量中保存的就是代理对象将来要实现的接口,以我们前面的代码为例,这里 proxiedInterfaces 的值如下:

Spring 中一个少见的引介增强 IntroductionAdvisor,spring,java,sql

可以看到,就包含了 Animal 接口。

最后,调用 JdkDynamicAopProxy#getProxy 方法生成代理对象,如下:

@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {  return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);}

复制代码

这就是大家比较熟悉的 JDK 动态代理了,可以看到,生成的代理对象有五个接口,生成的代理对象不仅仅是 Dog、Animal 的实例,也是 SpringProxy 等的实例。现在大家就明白了为什么我们拿到手的 dog 对象还能强转成 Animal 了。

2.2 CGLIB 动态代理

再来看 CGLIB 动态代理的实现逻辑,其实也差不多:

public CglibAopProxy(AdvisedSupport config) throws AopConfigException {  this.advised = config;  this.advisedDispatcher = new AdvisedDispatcher(this.advised);}@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {  return buildProxy(classLoader, false);}private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) {  //...  enhancer.setSuperclass(proxySuperClass);  enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));  //...  // Generate the proxy class and create a proxy instance.  return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));}

复制代码

可以看到,其实跟 JDK 里边的思路差不多,也是从 advised 中提取出来接口设置进去,advised 也是在 CglibAopProxy 对象构建的时候传入进来的。

3. 小结

好了,现在小伙伴们应该明白了什么是 IntroductionAdvisor 了吧?说白了,就是在生成代理对象的时候,把我们在 Advisor 中设置好的接口也考虑进去,这样生成的代理对象同时也是该接口的实现类,当然,在我们提供的 Advice 中,必须也要实现该接口,否则代理对象执行接口中的方法,找不到具体实现的时候就会报错了。

感兴趣的小伙伴赶紧体验一把吧~文章来源地址https://www.toymoban.com/news/detail-609508.html

到了这里,关于Spring 中一个少见的引介增强 IntroductionAdvisor的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 通过JDK动态代理类实现一个类中多种方法的不同增强

    JDK动态代理是基于接口的代理,它要求目标类(被代理的类)必须实现一个或多个接口。这是因为JDK动态代理是通过创建目标类的接口的代理对象来实现的,代理对象实现了目标接口,并在方法调用时委托给 InvocationHandler 中的 invoke 方法来处理。 在JDK动态代理中, Proxy 类的

    2024年02月03日
    浏览(36)
  • Spring-MVC使用JSR303及拦截器,增强网络隐私安全

    目录 一、JSR303 ( 1 )  是什么 ( 2 )  作用 ( 3 )  常用注解 ( 4 )  入门使用 二、拦截器 2.1  是什么 2.2  拦截器与过滤器的区别 2.3  应用场景 2.4 基础使用 2.5 用户登录权限控制 给我们带来的收获 JSR 303是Java规范请求(Java Specification Request)的一部分, 它定义了一套标准的Jav

    2024年02月09日
    浏览(42)
  • ARToolKitPlus是一个开源的Python库,用于实现增强现实(AR)应用程序

    ARToolKitPlus是一个开源的Python库,用于实现增强现实(AR)应用程序。它提供了一组工具和API,使开发人员能够轻松地创建AR应用程序,并与各种AR硬件设备集成。 要开始使用ARToolKitPlus,您需要安装它。您可以使用pip来安装ARToolKitPlus: ```shell pip install artoolkitplus ``` 一旦安装完成,

    2024年02月04日
    浏览(48)
  • Spring Cloud Gateway集成聚合型Spring Boot API发布组件knife4j,增强Swagger

    大家都知道,在前后端分离开发的时代,前后端接口对接是一项必不可少的工作。 可是, 作 为后端开发,怎么和前端更好的配合,才能让自己不心累、脑累 ,直接扔给前端一个后端开放api接口文档或者页面,让前端不用看着难受,也不用前端老问你,来愉快的合作呢? 原

    2024年04月22日
    浏览(34)
  • Java Spring IoC&DI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性

    💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录文章:Java Spring IoCDI :探索Java Spring中控制反转和依赖注入的威力,增强灵活性和可维护性 🎉欢迎大家点赞👍评论📝收藏⭐文章 我们一下要学习的内容都是为了实现⾼内聚低耦合来进行的 软件设计原则:⾼内聚低耦合. ⾼内聚指

    2024年04月15日
    浏览(46)
  • com.google.common.collect 是 Google Guava 库中的一个包,它提供了一系列扩展和增强 Java 集合框架的工具类和数据结构

    com.google.common.collect 是 Google Guava 库中的一个包,它提供了一系列扩展和增强 Java 集合框架的工具类和数据结构。Guava 的集合工具在设计上强调性能、不可变性、功能性和易用性。 以下是 com.google.common.collect 包中一些重要的类和接口: Immutable Collections: ImmutableSet:一个不可变

    2024年03月19日
    浏览(57)
  • 【Spring框架全系列】第一个Spring程序

    🏙哈喽,大家好,我是小浪。那么从今天开始,我就要开始更新spring框架全系列的博客了;本专栏免费阅读,最好能够点个订阅,以便于后续及时收到更新信息哈!🏟 📲目录 一、为什么要学习框架? 二、什么是Spring? 三、Spring的创建和使用 一、新建一个maven项目 二、添

    2024年02月02日
    浏览(40)
  • 【Spring Boot】第一个Spring Boot项目:helloworld

    本节从简单的helloworld程序开始介绍创建Spring Boot项目的方法和流程,以及Spring Boot项目结构,最后介绍项目中非常重要的pom.xml文件。 有两种方式来构建Spring Boot项目的基础框架: 第一种是使用Spring官网提供的构建页面; 第二种是使用IntelliJ IDEA中的Spring插件。 (1)使用Spri

    2024年02月13日
    浏览(57)
  • 第一个spring程序

    我们今天写第一个spring程序 我们采用maven形式创建工程。 我们首先在pom.xml中加入引用。 需要 spring - context,spring - core,spring - beans,commons - logging。由于需要单元测试就加入了junit。 下面开始写代码 很简单构造函数打印一句log 选择Project Structure   选择resources文件夹,右键标

    2024年02月12日
    浏览(22)
  • 【Spring Boot学习】Spring Boot的创建,第一个Spring Boot页面.

    前言: 大家好,我是 良辰丫 ,前面几篇文章,我们系统的学习了Spring框架,今天开始,我们就要学习更高级的SpringBoot框架了,不要着急哦,我们一起畅游SpringBoot框架的世界.💌💌💌 🧑个人主页:良辰针不戳 📖所属专栏:javaEE进阶篇之框架学习 🍎励志语句:生活也许会让我们遍体

    2024年02月08日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包