【小家Spring】Spring AOP原理使用的基础类打点(AopInfrastructureBean、ProxyProcessorSupport、Advised、AjType)(中)

简介: 【小家Spring】Spring AOP原理使用的基础类打点(AopInfrastructureBean、ProxyProcessorSupport、Advised、AjType)(中)

AdvisorAdapter


spring aop框架对BeforeAdvice、AfterAdvice、ThrowsAdvice三种通知类型的支持实际上是借助适配器模式来实现的,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型


的AdvisorAdapter是一个适配器接口,它定义了自己支持的Advice类型,并且能把一个Advisor适配成MethodInterceptor(这也是AOP联盟定义的借口),以下是它的定义

public interface AdvisorAdapter {
    // 判断此适配器是否支持特定的Advice  
    boolean supportsAdvice(Advice advice);  
    // 将一个Advisor适配成MethodInterceptor  
    MethodInterceptor getInterceptor(Advisor advisor);  
}


一般我们自己并不需要自己去提供此接口的实现(除非你还行适配被的Advice进来),因为Spring为我们提供了对应的实现:


image.png

实现也非常的简单,如下:


class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
  @Override
  public boolean supportsAdvice(Advice advice) {
    return (advice instanceof MethodBeforeAdvice);
  }
  @Override
  public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return new MethodBeforeAdviceInterceptor(advice);
  }
}
// 都转为了AOP联盟的MethodInterceptor 从而实现拦截统一的拦截工作
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
  private MethodBeforeAdvice advice;
  /**
   * Create a new MethodBeforeAdviceInterceptor for the given advice.
   * @param advice the MethodBeforeAdvice to wrap
   */
  public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
    Assert.notNull(advice, "Advice must not be null");
    this.advice = advice;
  }
  @Override
  public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    // 最终调用,实现了链式调用的效果
    return mi.proceed();
  }
}


参考:AdvisorAdapterRegistry和DefaultAdvisorAdapterRegistry,GlobalAdvisorAdapterRegistry用于管理管理AdvisorAdapter的


如果我们想把自己定义的AdvisorAdapter注册到spring aop框架中,怎么办?

  1. 把我们自己写好得AdvisorAdapter放进Spring IoC容器中
  2. 配置一个AdvisorAdapterRegistrationManager,它是一个BeanPostProcessor,它会检测所有的Bean。若是AdvisorAdapter类型,就:this.advisorAdapterRegistry.registerAdvisorAdapter((AdvisorAdapter) bean);


TargetSource


该接口代表一个目标对象,在aop调用目标对象的时候,使用该接口返回真实的对象。

比如它有其中两个实现SingletonTargetSource和PrototypeTargetSource代表着每次调用返回同一个实例,和每次调用返回一个新的实例

TargetClassAware


所有的Aop代理对象或者代理工厂(proxy factory)都要实现的接口,该接口用于暴露出被代理目标对象类型;

public interface TargetClassAware {
  // 返回被代理得目标类型  AopUtils#getTargetClass(Object)
  @Nullable
  Class<?> getTargetClass();
}

AspectMetadata:Metadata for an AspectJ aspect class


表示一个切面的元数据类。


public class AspectMetadata implements Serializable {
  private final String aspectName;
  private final Class<?> aspectClass;
  // AjType这个字段非常的关键,它表示有非常非常多得关于这个切面的一些数据、方法(位于org.aspectj下)
  private transient AjType<?> ajType;
  // 解析切入点表达式用的,但是真正的解析工作为委托给`org.aspectj.weaver.tools.PointcutExpression`来解析的
  //若是单例:则是Pointcut.TRUE  否则为AspectJExpressionPointcut
  private final Pointcut perClausePointcut;
  public AspectMetadata(Class<?> aspectClass, String aspectName) {
    this.aspectName = aspectName;
    Class<?> currClass = aspectClass;
    AjType<?> ajType = null;
    // 此处会一直遍历到顶层知道Object  直到找到有一个是Aspect切面就行,然后保存起来
    // 因此我们的切面写在父类上 也是欧克的
    while (currClass != Object.class) {
      AjType<?> ajTypeToCheck = AjTypeSystem.getAjType(currClass);
      if (ajTypeToCheck.isAspect()) {
        ajType = ajTypeToCheck;
        break;
      }
      currClass = currClass.getSuperclass();
    }
    // 由此可见,我们传进来的Class必须是个切面或者切面的子类的~~~
    if (ajType == null) {
      throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect");
    }
    // 显然Spring AOP目前也不支持优先级的声明。。。
    if (ajType.getDeclarePrecedence().length > 0) {
      throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP");
    }
    this.aspectClass = ajType.getJavaClass();
    this.ajType = ajType;
    // 切面的处在类型:PerClauseKind  由此可议看出,Spring的AOP目前只支持下面4种 
    switch (this.ajType.getPerClause().getKind()) {
      case SINGLETON:
        // 如国是单例,这个表达式返回这个常量
        this.perClausePointcut = Pointcut.TRUE;
        return;
      case PERTARGET:
      case PERTHIS:
        // PERTARGET和PERTHIS处理方式一样  返回的是AspectJExpressionPointcut
        AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
        ajexp.setLocation(aspectClass.getName());
        //设置好 切点表达式
        ajexp.setExpression(findPerClause(aspectClass));
        ajexp.setPointcutDeclarationScope(aspectClass);
        this.perClausePointcut = ajexp;
        return;
      case PERTYPEWITHIN:
        // Works with a type pattern
        // 组成的、合成得切点表达式~~~
        this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
        return;
      default:
        // 其余的Spring AOP暂时不支持
        throw new AopConfigException(
            "PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
    }
  }
  private String findPerClause(Class<?> aspectClass) {
    String str = aspectClass.getAnnotation(Aspect.class).value();
    str = str.substring(str.indexOf('(') + 1);
    str = str.substring(0, str.length() - 1);
    return str;
  }
  ...
  public Pointcut getPerClausePointcut() {
    return this.perClausePointcut;
  }
  // 判断perThis或者perTarger,最单实例、多实例处理
  public boolean isPerThisOrPerTarget() {
    PerClauseKind kind = getAjType().getPerClause().getKind();
    return (kind == PerClauseKind.PERTARGET || kind == PerClauseKind.PERTHIS);
  }
  // 是否是within的
  public boolean isPerTypeWithin() {
    PerClauseKind kind = getAjType().getPerClause().getKind();
    return (kind == PerClauseKind.PERTYPEWITHIN);
  }
  // 只要不是单例的,就都属于Lazy懒加载,延迟实例化的类型~~~~
  public boolean isLazilyInstantiated() {
    return (isPerThisOrPerTarget() || isPerTypeWithin());
  }
}


Spring AOP切面实例化模型


Spring AOP支持AspectJ的singleton、perthis、pertarget、pertypewithin实例化模型(目前不支持percflow、percflowbelow) 参见枚举类PerClauseKind


  1. singleton:即切面只会有一个实例;
  2. perthis:每个切入点表达式匹配的连接点对应的AOP对象(代理对象)都会创建一个新切面实例;
  3. pertarget:每个切入点表达式匹配的连接点对应的目标对象都会创建一个新的切面实例
  4. pertypewithin:


默认是singleton实例化模型,Schema风格只支持singleton实例化模型,而@AspectJ风格支持这三种实例化模型


singleton:使用@Aspect()指定,即默认就是单例实例化模式,在此就不演示示例了

perthis:每个切入点表达式匹配的连接点对应的AOP代理对象都会创建一个新的切面实例,使用@Aspect("perthis(切入点表达式)")指定切入点表达式;

// 他将为每个被切入点表达式匹配上的代理对象,都创建一个新的切面实例(此处允许HelloService是接口)
@Aspect("perthis(this(com.fsx.HelloService))") 


pertarget:每个切入点表达式匹配的连接点对应的目标对象都会创建一个新的切面实例,使用@Aspect("pertarget(切入点表达式)")指定切入点表达式; 此处要求HelloService不能是接口


另外需要注意一点:若在Spring内要使用perthis和pertarget,请把切面的Scope定义为:prototype

AspectInstanceFactory:切面工厂


专门为切面创建实例的工厂(因为切面也不一定是单例的,也支持各种多例形式。上面已有说明)


// 它实现了Order接口哦~~~~支持排序的
public interface AspectInstanceFactory extends Ordered {
  //Create an instance of this factory's aspect.
  Object getAspectInstance();
  //Expose the aspect class loader that this factory uses.
  @Nullable
  ClassLoader getAspectClassLoader();
}


它的实现类如下:


image.png


SimpleAspectInstanceFactory:根据切面的aspectClass,调用空构造函数反射.newInstance()创建一个实例(备注:构造函数private的也没有关系)

SingletonAspectInstanceFactory:这个就更简单了,因为已经持有aspectInstance得引用了,直接return即可


MetadataAwareAspectInstanceFactory


AspectInstanceFactory的子接口。提供了获取AspectMetadata的方法

public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
  AspectMetadata getAspectMetadata();
  // Spring4.3提供  和beanFactory.getSingletonMutex()  否则一般都是this
  Object getAspectCreationMutex();
}


SimpleMetadataAwareAspectInstanceFactory和SingletonMetadataAwareAspectInstanceFactory已经直接关联到AspectMetadata,所以直接return即可。

LazySingletonAspectInstanceFactoryDecorator也只是个简单的装饰而已。


BeanFactoryAspectInstanceFactory


这个就和Bean工厂有关了。比较重要


public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInstanceFactory, Serializable {
  // 持有对Bean工厂的引用
  private final BeanFactory beanFactory;
  // 需要处理的名称
  private final String name;
  private final AspectMetadata aspectMetadata;
  // 传了Name,type可议不传,内部判断出来
  public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) {
    this(beanFactory, name, null);
  }
  public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, @Nullable Class<?> type) {
    this.beanFactory = beanFactory;
    this.name = name;
    Class<?> resolvedType = type;
    // 若没传type,就去Bean工厂里看看它的Type是啥  type不能为null~~~~
    if (type == null) {
      resolvedType = beanFactory.getType(name);
      Assert.notNull(resolvedType, "Unresolvable bean type - explicitly specify the aspect class");
    }
    // 包装成切面元数据类
    this.aspectMetadata = new AspectMetadata(resolvedType, name);
  }
  // 此处:切面实例 是从Bean工厂里获取的  需要注意
  // 若是多例的,请注意Scope的值
  @Override
  public Object getAspectInstance() {
    return this.beanFactory.getBean(this.name);
  }
  @Override
  @Nullable
  public ClassLoader getAspectClassLoader() {
    return (this.beanFactory instanceof ConfigurableBeanFactory ?
        ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
        ClassUtils.getDefaultClassLoader());
  }
  @Override
  public AspectMetadata getAspectMetadata() {
    return this.aspectMetadata;
  }
  @Override
  @Nullable
  public Object getAspectCreationMutex() {
    if (this.beanFactory.isSingleton(this.name)) {
      // Rely on singleton semantics provided by the factory -> no local lock.
      return null;
    }
    else if (this.beanFactory instanceof ConfigurableBeanFactory) {
      // No singleton guarantees from the factory -> let's lock locally but
      // reuse the factory's singleton lock, just in case a lazy dependency
      // of our advice bean happens to trigger the singleton lock implicitly...
      return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex();
    }
    else {
      return this;
    }
  }
  @Override
  public int getOrder() {
    Class<?> type = this.beanFactory.getType(this.name);
    if (type != null) {
      if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) {
        return ((Ordered) this.beanFactory.getBean(this.name)).getOrder();
      }
      // 若没实现接口,就拿注解的值
      return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE);
    }
    return Ordered.LOWEST_PRECEDENCE;
  }
}


PrototypeAspectInstanceFactory:多例专用的工厂 若是多例的,推荐使用


public class PrototypeAspectInstanceFactory extends BeanFactoryAspectInstanceFactory implements Serializable {
  public PrototypeAspectInstanceFactory(BeanFactory beanFactory, String name) {
    super(beanFactory, name);
    // 若是单例,直接报错了
    if (!beanFactory.isPrototype(name)) {
      throw new IllegalArgumentException(
          "Cannot use PrototypeAspectInstanceFactory with bean named '" + name + "': not a prototype");
    }
  }
}

下面介绍下aspectj这个jar包下几个重要的类




相关文章
|
7天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
28天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
170 73
|
16天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
65 8
|
28天前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
1月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
110 14
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
99 5
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
54 5
|
2天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
72 17
Spring Boot 两种部署到服务器的方式
|
2天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
33 17
springboot自动配置原理