Spring Boot自动装配原理详解(2)

简介: 1.环境和依赖1.1.spring boot版本springboot 2.2.X版本采用的maven构建,2.3.X采用gradle构建,因此采用2.2.X,mavan构建的便于源码阅读。本文以2.2.9为例进行Spring Boot自动装配原理的解析。1.2.依赖管理引入Spring Boot的方式有两种引入spring-boot-dependencies的pom文件将spring-boot-starter-parent作为父级pom

2.5.自动配置

在自动装载步骤中已经获得需要加载的自动配置类的全路径,接下来就是自动配置。

以随便一个AutoConfiguration类为例:

头上的一大串@Conditional注解其实就是过滤时的过滤条件,过滤列表其实就是通过这些条件注解生成的。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {}

这个@Configuration满足条件后其中的@Bean都会被自动装入IOC。

3.启动过程

3.1.整体流程

Spring boot的启动过程就是围绕上下文的创建、准备、刷新(填充)展开的

spring应用上下文和servletContext不是一个东西,servlet上下文用来维系当前应用的一块共享空间,目的是实现资源和数据在应用中的全局共享。spring的上下文是一个维护Bean定义以及对象之间协作关系的高级接口,目的是维护好整个spring中的资源,如配置文件、Bean对象等,其涵盖了IOC,但不只有IOC,可以理解为Spring应用的一个抽象。


在SpringApplication的run()方法中创建应用上下文,整个SpringApplication的run方法主要完成四个核心动作:


prepareEnvironment


创建环境信息对象,解析环境参数,包含配置文件、命令行传参等。


createApplicationContext


创建应用上下文对象


prepareContext


准备应用上下文对象


refreshContext

  1. 刷新应用上下文对象
// 类 SpringApplication 代码片段
  public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
          // 包装通过命令行传入的名命令行参数
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          // 结合命令行参数 准备环境对象,该环境对象将会被设置到应用上下文对象 ApplicationContext 上  ,
          // 环境对象通常包含如下信息 : 
          // 1. profile
          // 2. system properties
          // 3. system environment
          // 4. commandline arguments
          // 5. spring 配置文件
          // 6. 一个随机值属性源 random
          // 对于当前 WebFlux 应用来讲,这里实现类会使用 StandardReactiveWebEnvironment
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
          // 创建应用上下文对象 ApplicationContext  
          // 实现类会采用 : AnnotationConfigReactiveWebServerApplicationContext
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);
          // 准备应用上下文对象 ApplicationContext          
          // 1. 关联环境信息对象到应用上下文对象
          // 2. 对象创建后置处理 : 设置容器的类型转换服务
          // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer
          // 4. 广播事件 : ApplicationContextInitializedEvent
          // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments
          // 6. 将应用程序入口类作为 bean 注册到容器 (load)
          // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 
          //    ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          // 刷新应用上下文对象  ApplicationContext 
          // 主要是调用应用上下文对象  ApplicationContext  自身的 refresh 方法
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
          // 应用程序上下文对象 ApplicationContext 已经准备就绪,
          // 现在调用各种开发人员或者框架其他部分定义的 
          // ApplicationRunner 或者 CommandLineRunner
      callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
    }
    try {
      listeners.running(context);
    }
    catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
    }
    return context;
  }

3.2.创建环境信息对象

// SpringApplication 代码片段
  private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
    // Create and configure the environment
       // 创建环境信息对象 environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
       // 将应用程序参数关联到环境信息对象 environment
    configureEnvironment(environment, applicationArguments.getSourceArgs());
       // 发布应用程序事件 : 环境信息对象准备好了 ,
       // 同步调用各个事件监听器
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
          deduceEnvironmentClass());
    }       
    ConfigurationPropertySources.attach(environment);
    return environment;
  }

普通web应用和reactive创建的环境信息对象类型不同,但是实际功能相同,并没有什么太大区别。

// SpringApplication 代码片段
    private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
      return this.environment;
    }
    switch (this.webApplicationType) {
    case SERVLET:
      return new StandardServletEnvironment();
    case REACTIVE:
      return new StandardReactiveWebEnvironment();
    default:
      return new StandardEnvironment();
    }
  }

3.3.创建应用上下文对象

根据之前环境推断中得到的当前应用的环境类型来创建不同类型的应用上下文。

// SpringApplication 代码片段
  protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
      try {
             // 根据 this.webApplicationType 确定应用上下文实现类
        switch (this.webApplicationType) {
        case SERVLET:
            // DEFAULT_SERVLET_WEB_CONTEXT_CLASS 常量值为 : 
            // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
            // 对应 Spring MVC Servlet Web 环境
          contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
          break;
        case REACTIVE:
            // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS 常量值为 : 
            // org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
            // 对应 Spring WebFlux Reactive Web 环境
          contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
          break;
        default:
            // DEFAULT_CONTEXT_CLASS 常量值为 : 
            // org.springframework.context.annotation.AnnotationConfigApplicationContext
            // 不对应任何 Web 环境
          contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
        }
      }
      catch (ClassNotFoundException ex) {
        throw new IllegalStateException(
      "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
      ex);
      }
    }
       // 确定应用上下文实现类之后,实例化应用上下文对象 
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  }

3.4.刷新应用上下文对象

3.4.1.准备刷新

这一步主要是完成刷新前的准备工作,将除IOC相关的一切context中的东西全部赋值初始化好。

主要完成以下动作:

  • 关联环境信息
  • 查找调用各种前置、后置处理器(自定义的、自带的)
  • 调用各种回调
  • 获取主启动类的路径,将主启动类封装成一个BeanDefinition
// SpringApplication 代码片段
  private void prepareContext(ConfigurableApplicationContext context, 
      ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, 
      ApplicationArguments applicationArguments, 
      Banner printedBanner) {
       // 1. 关联环境信息对象到应用上下文对象     
    context.setEnvironment(environment);
       // 2. 对象创建后置处理 : 设置容器的类型转换服务 
    postProcessApplicationContext(context);
       // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer 
    applyInitializers(context);
       // 4. 广播事件 : ApplicationContextInitializedEvent 
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
       // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments 
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
          .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // 获取主启动类的路径
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
       // 6. 将主启动类封装成一个BeanDefinition 
    load(context, sources.toArray(new Object[0]));
       // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 
       //    ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent        
    listeners.contextLoaded(context);
  }

3.4.2.刷新

主要是调用应用上下文对象 ApplicationContext 自身的 refresh 方法,这是上下文对象的初始化中最关键的一步,该步骤中会完成几个核心动作:

  • 初始化IOC容器(即BeanFactory)
    该步骤中就会扫描解析注解,触发自动装配.

初始化WebServer容器


刷新应用上下文的动作其实是在spring相关的jar中,因此首先要有个概念,在这一步之前spring boot的动作已经完成,真正与IOC相关的动作还是由spring来完成,所以说spring boot是对spring的二次封装。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 做一些初始化动作
      prepareRefresh();
      // 获取bean factory
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 初始化bean factory,为其成员属性赋一些值
      prepareBeanFactory(beanFactory);
      try {
        // 获取所有bean后置处理器
        postProcessBeanFactory(beanFactory);
        // **最核心的方法,注解的扫描,自动配置类的装载,IOC的初始化等全在这个方法中
        invokeBeanFactoryPostProcessors(beanFactory);
        // Register bean processors that intercept bean creation.
        registerBeanPostProcessors(beanFactory);
        // Initialize message source for this context.
        initMessageSource();
        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();
        // Initialize other special beans in specific context subclasses.
        onRefresh();
        // Check for listener beans and register them.
        registerListeners();
        // Instantiate all remaining (non-lazy-init) singletons.
        finishBeanFactoryInitialization(beanFactory);
        // Last step: publish corresponding event.
        finishRefresh();
      }
      catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
          logger.warn("Exception encountered during context initialization - " +
              "cancelling refresh attempt: " + ex);
        }
        // Destroy already created singletons to avoid dangling resources.
        destroyBeans();
        // Reset 'active' flag.
        cancelRefresh(ex);
        // Propagate exception to caller.
        throw ex;
      }
      finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
      }
    }
  }

初始化IOC:

创建容器其实没有什么说的,就是new一个web server(tomcat、netty或者jetty)出来。这里着重要说一下初始化IOC。

入口在invokeBeanFactoryPostProcessors(beanFactory)。


IOC容器的初始化分为三步:


Resource定位


定位到需要的各种路径:


BasePackage


这一步在准备刷新的时候就已经完成,并在封装在了主启动类封装为的BeanDefinition中。


基于BasePackage去扫描通过注解自定义的需要注入IOC的Bean。


自动配置类的全路径


这一步在刷新应用上下文的时候进行,即去获取factory.properties。


基于自动配置类的全路径去将对应自动配置类注入IOC。


BeanDefinition载入


将定位到的Resource记录的Class分别封装为一个个的Definition。


BeanDefinition注册


将Definition注册进IOC中。其实就是注入到一个ConcurrentHashMap中,IOC就是通过这个Map来持有这些BeanDefinition的。


IOC涉及的两个核心概念:


BeanDefinition


BeanFactory


IOC容器其实就是BeanFactory,BeanFactory就是IOC容器的规范接口,有多个实现,最典型的就是DefalutListableBeanFactory,IOC容器中有一个成员Map(BeanDefinitionMap),该Map持有所有的BeanDefinition,用来维护Bean的基本信息(class、作用域等)

目录
相关文章
|
12小时前
|
安全 Java Spring
Spring之Aop的底层原理
Spring之Aop的底层原理
|
12小时前
|
XML Java Shell
【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring的核心注解开发和实现指南(Spring5的常见的注解)
【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究Spring的核心注解开发和实现指南(Spring5的常见的注解)
22 1
|
12小时前
|
XML 存储 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache管理器的实战开发指南(修正篇)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache管理器的实战开发指南(修正篇)
35 0
|
12小时前
|
XML Java 开发者
springboot 启动原理、启动过程、启动机制的介绍
【5月更文挑战第13天】Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。
17 3
|
12小时前
|
监控 安全 Java
Spring cloud原理详解
Spring cloud原理详解
17 0
|
12小时前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
15 1
|
12小时前
|
XML Java 数据库
【SpringBoot:详解Bean装配】
【SpringBoot:详解Bean装配】
10 3
|
12小时前
|
Java 开发者 微服务
Spring Cloud原理详解
【5月更文挑战第4天】Spring Cloud是Spring生态系统中的微服务框架,包含配置管理、服务发现、断路器、API网关等工具,简化分布式系统开发。核心组件如Eureka(服务发现)、Config Server(配置中心)、Ribbon(负载均衡)、Hystrix(断路器)、Zuul(API网关)等。本文讨论了Spring Cloud的基本概念、核心组件、常见问题及解决策略,并提供代码示例,帮助开发者更好地理解和实践微服务架构。此外,还涵盖了服务通信方式、安全性、性能优化、自动化部署、服务网格和无服务器架构的融合等话题,揭示了微服务架构的未来趋势。
35 6
|
12小时前
|
负载均衡 Java 开发者
Spring Cloud:一文读懂其原理与架构
Spring Cloud 是一套微服务解决方案,它整合了Netflix公司的多个开源框架,简化了分布式系统开发。Spring Cloud 提供了服务注册与发现、配置中心、消息总线、负载均衡、熔断机制等工具,让开发者可以快速地构建一些常见的微服务架构。
|
12小时前
|
Java
SpringBoot之@Conditional衍生条件装配详解
SpringBoot之@Conditional衍生条件装配详解