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

简介: 【小家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...


相关文章
|
22小时前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
22小时前
|
XML 监控 安全
Spring特性之一——AOP面向切面编程
Spring特性之一——AOP面向切面编程
14 1
|
23小时前
|
Java Spring 容器
Spring AOP浅谈
Spring AOP浅谈
10 1
|
22小时前
|
XML Java 数据格式
Spring高手之路18——从XML配置角度理解Spring AOP
本文是全面解析面向切面编程的实践指南。通过深入讲解切面、连接点、通知等关键概念,以及通过XML配置实现Spring AOP的步骤。
22 6
Spring高手之路18——从XML配置角度理解Spring AOP
|
22小时前
|
XML Java 数据格式
Spring使用AOP 的其他方式
Spring使用AOP 的其他方式
16 2
|
22小时前
|
XML Java 数据格式
Spring 项目如何使用AOP
Spring 项目如何使用AOP
23 2
|
22小时前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
25 5
|
23小时前
|
Java 数据库连接 应用服务中间件
Spring5源码(39)-Aop事物管理简介及编程式事物实现
Spring5源码(39)-Aop事物管理简介及编程式事物实现
26 0
|
23小时前
AOP&面向切面编程
AOP&面向切面编程
58 0
|
23小时前
|
Java 程序员 Maven
Spring AOP入门指南:轻松掌握面向切面编程的基础知识
Spring AOP入门指南:轻松掌握面向切面编程的基础知识