Spring源码-AOP分析

简介: Spring源码-AOP分析

一、AOP


1. 涉及的相关概念



  先回顾下核心的概念,比如:Advice,Pointcut,Aspect等


image.png


更加形象的描述:

image.png

2. 相关核心的设计


Advice:

image.png

Pointcut:

image.png

Aspect:

image.png

Advisor:

image.png

织入:

image.png

image.png




二、AOP相关概念的类结构


  回顾了前面的内容,然后我们来看看Spring中AOP是如何来实现的了。


1. Advice类结构


  我们先来看看Advice的类结构,advice--》通知,需要增强的功能

image.png

相关的说明

image.png



2. Pointcut类结构


  然后来看看Pointcut的设计,也就是切入点的处理。


image.png

Pointcut的两种实现方式

image.png



3. Advisor类结构


  Advisor的类结构比较简单。一个是PointcutAdvisor,一个是IntroductionAdvisor

image.png


我们要看的重点是 PointcutAdvisor 及实现 AspectJPointcutAdvisor。



三、织入的实现


1. BeanPostProcessor


1.1 案例演示


  我们通过案例来看,首先使用AOP来增强。


定义切面类


/**
 * 切面类
 */
@Component
@EnableAspectJAutoProxy
@Aspect
public class AspectAdviceBeanUseAnnotation {
  // 定义一个全局的Pointcut
  @Pointcut("execution(* com.study.spring.sample.aop.*.do*(..))")
  public void doMethods() {
  }
  @Pointcut("execution(* com.study.spring.sample.aop.*.service*(..))")
  public void services() {
  }
  // 定义一个Before Advice
  @Before("doMethods() and args(tk,..)")
  public void before3(String tk) {
    System.out.println("----------- AspectAdviceBeanUseAnnotation before3  增强  参数tk= " + tk);
  }
  @Around("services() and args(name,..)")
  public Object around2(ProceedingJoinPoint pjp, String name) throws Throwable {
    System.out.println("--------- AspectAdviceBeanUseAnnotation arround2 参数 name=" + name);
    System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-前增强 for " + pjp);
    Object ret = pjp.proceed();
    System.out.println("----------- AspectAdviceBeanUseAnnotation arround2 环绕-后增强 for " + pjp);
    return ret;
  }
  @AfterReturning(pointcut = "services()", returning = "retValue")
  public void afterReturning(Object retValue) {
    System.out.println("----------- AspectAdviceBeanUseAnnotation afterReturning 增强 , 返回值为: " + retValue);
  }
  @AfterThrowing(pointcut = "services()", throwing = "e")
  public void afterThrowing(JoinPoint jp, Exception e) {
    System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强  for " + jp);
    System.out.println("----------- AspectAdviceBeanUseAnnotation afterThrowing 增强  异常 :" + e);
  }
  @After("doMethods()")
  public void after(JoinPoint jp) {
    System.out.println("----------- AspectAdviceBeanUseAnnotation after 增强  for " + jp);
  }
  /*
   * BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor
   * InstantiationAwareBeanPostProcessor Bean实例创建前后 BeanPostProcessor
   */
}
复制代码


需要增强的目标类


@Component
public class BeanQ {
  public void do1(String task, int time) {
    System.out.println("-------------do1 do " + task + " time:" + time);
  }
  public String service1(String name) {
    System.out.println("-------------servce1 do " + name);
    return name;
  }
  public String service2(String name) {
    System.out.println("-------------servce2 do " + name);
    if (!"s1".equals(name)) {
      throw new IllegalArgumentException("参数 name != s1, name=" + name);
    }
    return name + " hello!";
  }
}
复制代码


测试代码


@Configuration
@ComponentScan
public class AopMainAnno {
  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(AopMainAnno.class);
    BeanQ bq = context.getBean(BeanQ.class);
    bq.do1("task1", 20);
    System.out.println();
    bq.service1("service1");
    System.out.println();
    bq.service2("s1");
  }
}
复制代码


执行即可看到增强的效果


1.2 @EnableAspectJAutoProxy


  我们需要使用代理增强处理,必须添加@EnableAspectJAutoProxy才生效。我们来看看他做了什么事情

image.png

image.png

image.png

在registerOrEscalateApcAsRequired方法中会把上面的Java类注入到容器中。

image.png


所以我们需要看看 AnnotationAwareAspectJAutoProxyCreator 的结构


1.3 AnnotationAwareAspectJAutoProxyCreator


  我们直接来看类图结构,可以发现其本质就是一个 BeanPostProcessor ,只是扩展了更多的功能。


image.png

那么具体处理的逻辑

image.png

1.4 如何串联


  Bean的IoC是如何和对应的BeanPostProcessor串联的呢?我们来看看。

image.png


image.png

image.png

image.png

image.png


isInfrastructureClass方法判断是否是基础设施


image.png

shouldSkip:是否应该跳过,会完成相关的advisor的收集

image.png

具体的处理流程


public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
      // Do not initialize FactoryBeans here: We need to leave all regular beans
      // uninitialized to let the auto-proxy creator apply to them!
      // 获取当前BeanFactory中所有实现了Advisor接口的bean的名称
      advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
          this.beanFactory, Advisor.class, true, false);
      this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
      return new ArrayList<>();
    }
    // 对获取到的实现Advisor接口的bean的名称进行遍历
    List<Advisor> advisors = new ArrayList<>();
    // 循环所有的beanName,找出对应的增强方法
    for (String name : advisorNames) {
      // isEligibleBean()是提供的一个hook方法,用于子类对Advisor进行过滤,这里默认返回值都是true
      if (isEligibleBean(name)) {
        // 如果当前bean还在创建过程中,则略过,其创建完成之后会为其判断是否需要织入切面逻辑
        if (this.beanFactory.isCurrentlyInCreation(name)) {
          if (logger.isTraceEnabled()) {
            logger.trace("Skipping currently created advisor '" + name + "'");
          }
        }
        else {
          try {
            // 将当前bean添加到结果中
            advisors.add(this.beanFactory.getBean(name, Advisor.class));
          }
          catch (BeanCreationException ex) {
            // 对获取过程中产生的异常进行封装
            Throwable rootCause = ex.getMostSpecificCause();
            if (rootCause instanceof BeanCurrentlyInCreationException) {
              BeanCreationException bce = (BeanCreationException) rootCause;
              String bceBeanName = bce.getBeanName();
              if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                if (logger.isTraceEnabled()) {
                  logger.trace("Skipping advisor '" + name +
                      "' with dependency on currently created bean: " + ex.getMessage());
                }
                // Ignore: indicates a reference back to the bean we're trying to advise.
                // We want to find advisors other than the currently created bean itself.
                continue;
              }
            }
            throw ex;
          }
        }
      }
    }
    return advisors;
  }
复制代码




2. 代理类的结构


  在上面的分析中出现了很多代理相关的代码,为了更好的理解,我们来梳理下Spring中的代理相关的结构


2.1 AopProxy


  在Spring中创建代理对象都是通过AopProxy这个接口的两个具体实现类来实现的,也就是jdk和cglib两种方式。


image.png


2.2 AopProxyFactory


  在Spring中通过AopProxyFactory这个工厂类来提供AopProxy。


image.png


默认的实现类是DefaultAopProxyFactory


/**
   * 真正的创建代理,判断一些列条件,有自定义的接口的就会创建jdk代理,否则就是cglib
   * @param config the AOP configuration in the form of an
   * AdvisedSupport object
   * @return
   * @throws AopConfigException
   */
  @Override
  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 这段代码用来判断选择哪种创建代理对象的方式
    // config.isOptimize()   是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false
    // config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false
    // hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是SpringProxy类型
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      // 上面的三个方法有一个为true的话,则进入到这里
      // 从AdvisedSupport中获取目标类 类对象
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
        throw new AopConfigException("TargetSource cannot determine target class: " +
            "Either an interface or a target is required for proxy creation.");
      }
      // 判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象
      // 如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
        return new JdkDynamicAopProxy(config);
      }
      // 配置了使用Cglib进行动态代理或者目标类没有接口,那么使用Cglib的方式创建代理对象
      return new ObjenesisCglibAopProxy(config);
    }
    else {
      // 使用JDK的提供的代理方式生成代理对象
      return new JdkDynamicAopProxy(config);
    }
  }
复制代码


2.3 ProxyFactory


  ProxyFactory代理对象的工厂类,用来创建代理对象的工厂。


image.png

然后我们来看看 ProxyFactory的体系结构

image.png

ProxyConfig


这个类主要保存代理的信息,如果是否使用类代理,是否要暴露代理等。


public class ProxyConfig implements Serializable {
  /** use serialVersionUID from Spring 1.2 for interoperability. */
  private static final long serialVersionUID = -8409359707199703185L;
  // 是否代理的对象是类,动态代理分为代理接口和类,这里的属性默认是代理的接口
  private boolean proxyTargetClass = false;
  // 是否进行主动优化,默认是不会主动优化
  private boolean optimize = false;
  // 是否由此配置创建的代理不能被转成Advised类型,默认时候可转
  boolean opaque = false;
  // 是否会暴露代理在调用的时候,默认是不会暴露
  boolean exposeProxy = false;
  // 是否冻结此配置,不能被修改
  private boolean frozen = false;
}
复制代码


Advised


由持有 AOP 代理工厂配置的类实现的接口。此配置包括拦截器和其他advice、advisor和代理接口。从 Spring 获得的任何 AOP 代理都可以转换为该接口,以允许操作其 AOP 通知。

image.png


AdvisedSupport


  • AOP代理配置管理器的基类。 此类的子类通常是工厂,从中可以直接获取 AOP 代理实例。此类可释放Advices和Advisor的内部管理子类,但实际上并没有实现代理创建方法,实现由子类提供


  • AdvisedSupport实现了Advised中处理Advisor和Advice的方法,添加Advice时会被包装成一个Advisor,默认使用的Advisor是DefaultPointcutAdvisor,DefaultPointcutAdvisor默认的Pointcut是TruePointcut(转换为一个匹配所有方法调用的Advisor与代理对象绑定)。


  • AdvisedSupport同时会缓存对于某一个方法对应的所有Advisor(Map<MethodCacheKey, List<Object>> methodCache),当Advice或Advisor发生变化时,会清空该缓存。getInterceptorsAndDynamicInterceptionAdvice用来获取对应代理方法对应有效的拦截器链 。


ProxyCreatorSupport


  继承了AdvisedSupport,ProxyCreatorSupport正是实现代理的创建方法,ProxyCreatorSupport有一个成员变量AopProxyFactory,而该变量的值默认是DefaultAopProxyFactory

image.png


这个也就和前面的AopProxyFactory串联起来了。

相关文章
|
1月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
309 0
|
5月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
2月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
2月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
414 70
|
4月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
517 0
|
6月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
270 0
|
7月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
799 0