【小家Spring】Spring AOP之Advisor、PointcutAdvisor、IntroductionAdvisor、IntroductionInterceptor(引介增强)(中)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【小家Spring】Spring AOP之Advisor、PointcutAdvisor、IntroductionAdvisor、IntroductionInterceptor(引介增强)(中)

AbstractAspectJAdvice的实现类如下:这5个实现类完完整整的对应着我们AspectJ的那5个注解。




image.png

AspectJPointcutAdvisor


显然是和AspectJ相关的,使用得很是广泛。注意它和AspectJExpressionPointcutAdvisor的区别。有名字也能看出来,AspectJExpressionPointcutAdvisor和表达式语言的切点相关的,而AspectJPointcutAdvisor是无关的。它哥俩都位于包org.springframework.aop.aspectj里。

public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
  // AbstractAspectJAdvice通知:它的子类看下面截图,就非常清楚了
  private final AbstractAspectJAdvice advice;
  // 可以接受任意的Pointcut,可谓非常的通用(当然也包含切点表达式啦)
  private final Pointcut pointcut;
  @Nullable
  private Integer order;
  // 只有这一个构造函数,包装一个advice
  public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) {
    Assert.notNull(advice, "Advice must not be null");
    this.advice = advice;
    // 然后pointcut根据advice直接给生成了一个。这是AbstractAspectJAdvice#buildSafePointcut的方法
    this.pointcut = advice.buildSafePointcut();
  }
}


InstantiationModelAwarePointcutAdvisor


它是PointcutAdvisor的一个子接口。


// 由SpringAOP顾问包装AspectJ实现的接口 可能具有延迟初始化策略的方面。
// 例如,一个PerThis实例化模型意味着对建议的初始化太慢
public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {
  // 该Advisor是否需要懒加载
  boolean isLazy();
  // 判断此Advisor它所拥有的Advice是否已经初始化了
  boolean isAdviceInstantiated();
}


它的唯一实现类:InstantiationModelAwarePointcutAdvisorImpl


// 默认的访问权限,显然是Spring内部自己用的
class InstantiationModelAwarePointcutAdvisorImpl
    implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation, Serializable {
  private static final Advice EMPTY_ADVICE = new Advice() {};
  // 和AspectJExpression
  private final AspectJExpressionPointcut declaredPointcut;
  ..
  // 通知方法
  private transient Method aspectJAdviceMethod;
  private final AspectJAdvisorFactory aspectJAdvisorFactory;
  private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;
  @Nullable
  private Advice instantiatedAdvice;
  @Nullable
  private Boolean isBeforeAdvice;
  @Nullable
  private Boolean isAfterAdvice;
  ...
  @Override
  public boolean isPerInstance() {
    return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
  }
  @Override
  public synchronized Advice getAdvice() {
    if (this.instantiatedAdvice == null) {
      this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
    return this.instantiatedAdvice;
  }
  // advice 由aspectJAdvisorFactory去生产  懒加载的效果
  private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
        this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return (advice != null ? advice : EMPTY_ADVICE);
  }
  @Override
  public boolean isBeforeAdvice() {
    if (this.isBeforeAdvice == null) {
      determineAdviceType();
    }
    return this.isBeforeAdvice;
  }
  @Override
  public boolean isAfterAdvice() {
    if (this.isAfterAdvice == null) {
      determineAdviceType();
    }
    return this.isAfterAdvice;
  }
  // 这里解释根据@Aspect方法上标注的注解,来区分这两个字段的值的
  private void determineAdviceType() {
    AspectJAnnotation<?> aspectJAnnotation =
        AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.aspectJAdviceMethod);
    if (aspectJAnnotation == null) {
      this.isBeforeAdvice = false;
      this.isAfterAdvice = false;
    }
    else {
      switch (aspectJAnnotation.getAnnotationType()) {
        case AtAfter:
        case AtAfterReturning:
        case AtAfterThrowing:
          this.isAfterAdvice = true;
          this.isBeforeAdvice = false;
          break;
        case AtAround:
        case AtPointcut:
          this.isAfterAdvice = false;
          this.isBeforeAdvice = false;
          break;
        case AtBefore:
          this.isAfterAdvice = false;
          this.isBeforeAdvice = true;
      }
    }
  }
}


这个Advisor是在Spring解析被 @AspectJ注解注释的类时生成的 Advisor,。


而这个 Advisor中的 Pointcut与Advice都是由ReflectiveAspectJAdvisorFactory 来解析生成的(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice,

Pointcut 则是AspectJExpressionPointcut), 解析的步骤是:


自动代理创建器:AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors() ->

Bean工厂相关的Advisor构建器:BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() ->

ReflectiveAspectJAdvisorFactory.getAdvisors() ->

ReflectiveAspectJAdvisorFactory.getAdvisor() 最终生成了InstantiationModelAwarePointcutAdvisorImpl(当然包括里面的 Pointcut与 advice 也都是由 ReflectiveAspectJAdvisorFactory 解析生成的)

IntroductionAdvisor:引介切面


Spring中有五种增强:BeforeAdvide(前置增强)、AfterAdvice(后置增强)、ThrowsAdvice(异常增强)、RoundAdvice(环绕增强)、IntroductionAdvice(引入增强)


RoundAdvice(环绕增强):就是BeforeAdvide(前置增强)、AfterAdvice(后置增强)的组合使用叫环绕增强。前四种都比较简单。。。


引入增强(Introduction Advice)的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。(非常强大有木有,A不需要动代码,就能有别的功能,吊炸天有木有)


IntroductionAdvisor纯粹就是为Introduction而生的。


IntroductionAdvisor 和 PointcutAdvisor接口不同,它仅有一个类过滤器ClassFilter 而没有 MethodMatcher,这是因为 `引介切面 的切点是类级别的,而 Pointcut 的切点是方法级别的(细粒度更细,所以更加常用)。


为了更好的了解IntroductionAdvisor,我先有必要讲解下IntroductionInfo和IntroductionInterceptor;


Introduction可以在不改动目标类定义的情况下,为目标类增加新的属性和行为。


IntroductionInfo:引介信息


IntroductionInfo 接口描述了目标类需要实现的新接口。

// 提供描述引言所需信息的接口
// IntroductionAdvisor必须实现这个接口。若`org.aopalliance.aop.Advice`直接实现了此接口,
// 它可议独立的当作introduction来使用而不用依赖IntroductionAdvisor。这种情况下,这个advice可议自描述,不仅提供。。。
public interface IntroductionInfo {
  //Return the additional interfaces introduced by this Advisor or Advice.
  // 返回额外给Advisor 或者 advice实现的接口们
  Class<?>[] getInterfaces();
}


它的继承结构如下:


image.png


IntroductionInterceptor:引介拦截器


在Spring中,为目标对象添加新的属性和行为必须声明相应的接口以及相应的实现。这样,再通过特定的拦截器将新的接口定义以及实现类中的逻辑附加到目标对象上。然后,目标对象(确切的说,是目标对象的代理对象)就拥有了新的状态和行为


这里面介绍这个非常强大的拦截器:IntroductionInterceptor


// IntroductionInterceptor它是对MethodInterceptor的一个扩展,同时他还继承了接口DynamicIntroductionAdvice
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
}


DynamicIntroductionAdvice


public interface DynamicIntroductionAdvice extends Advice {
  boolean implementsInterface(Class<?> intf);
}


过DynamicIntroductionAdvice,可以界定当前的 IntroductionInterceptor为哪些接口提供相应的拦截功能。通过MethodInterceptor,IntroductionInterceptor 就可以处理新添加的接口上的方法调用了


打个比方


如果把每个目标对象实例看作盒装牛奶生产线上的那一盒盒牛奶的话,那么生产合格证就是新的Introduction逻辑,而introductionInterceptor 就是把这些生产合格证贴到一盒盒牛奶上的那个人。


要对目标对象进行拦截并添加Introduction的逻辑,我们可以直接扩展IntroductionInterceptor,然后在子类的invoke方法中实现所有的拦截逻辑


除非特殊情况下需要直接扩展IntroductionInterceptor,大多数时候,直接使用Spring提供的两个现成的实现类就可以了:DelegatingIntroductionInterceptor 和 DelegatePerTargetObjectIntroductionInterceptor

image.png


例子


下面使用一个例子,加深一下对引介增强的了解:


// 定义一个新的行为接口,这个行为准备作用在目标对象上
public interface IOtherInte {
    void doOther();
}
// 自己定义一个IntroductionInterceptor来实现IntroductionInterceptor接口
// 注意:此处也实现了接口IOtherInte(这是类似于增强器部分)  相当于这个interptor目前就只处理 IOtherInte
public class SomeInteIntroductionInterceptor implements IntroductionInterceptor, IOtherInte {
    /**
     * 判断调用的方法是否为指定类中的方法
     * 如果Method代表了一个方法 那么调用它的invoke就相当于执行了它代表的这个方法
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (implementsInterface(invocation.getMethod().getDeclaringClass())) {
            System.out.println("我是引介增强的方法体~~~invoke");
            return invocation.getMethod().invoke(this, invocation.getArguments());
        }
        return invocation.proceed();
    }
    /**
     * 判断clazz是否为给定接口IOtherBean的实现
     */
    @Override
    public boolean implementsInterface(Class clazz) {
        return clazz.isAssignableFrom(IOtherInte.class);
    }
    @Override
    public void doOther() {
        System.out.println("给人贴标签 doOther...");
    }
}
// 方法测试
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new Person());
        factory.setProxyTargetClass(true); // 强制私用CGLIB 以保证我们的Person方法也能正常调用
        // 此处采用IntroductionInterceptor 这个引介增强的拦截器
        Advice advice = new SomeInteIntroductionInterceptor();
        // 切点+通知(注意:此处放的是复合切面)
        Advisor advisor = new DefaultIntroductionAdvisor((DynamicIntroductionAdvice) advice, IOtherInte.class);
        //Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);
        IOtherInte otherInte = (IOtherInte) factory.getProxy();
        otherInte.doOther();
        System.out.println("===============================");
        // Person本身自己的方法  也得到了保留
        Person p = (Person) factory.getProxy();
        p.run();
        p.say();
    }
输出:
我是引介增强的方法体~~~invoke
给人贴标签 doOther...
===============================
我在run...
我在say...


相关文章
|
8天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
16天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
68 8
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
99 5
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
92 8
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
54 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
60 4
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
96 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
314 1
什么是AOP面向切面编程?怎么简单理解?
|
2月前
|
XML Java 开发者
论面向方面的编程技术及其应用(AOP)
【11月更文挑战第2天】随着软件系统的规模和复杂度不断增加,传统的面向过程编程和面向对象编程(OOP)在应对横切关注点(如日志记录、事务管理、安全性检查等)时显得力不从心。面向方面的编程(Aspect-Oriented Programming,简称AOP)作为一种新的编程范式,通过将横切关注点与业务逻辑分离,提高了代码的可维护性、可重用性和可读性。本文首先概述了AOP的基本概念和技术原理,然后结合一个实际项目,详细阐述了在项目实践中使用AOP技术开发的具体步骤,最后分析了使用AOP的原因、开发过程中存在的问题及所使用的技术带来的实际应用效果。
81 5