Spring Boot源码解析——启动配置原理



文章目录

  • Spring Boot源码解析——启动配置原理
  • 一、@SpringBootConfiguration
  • 1.1 注解分析
  • 1.1.1 @SpringBootConfiguration
  • 1.1.2 @EnableAutoConfiguration——Spring Boot的全局开关
  • 1.2 AutoConfigurationImportSelector类
  • 1.3 Conditional机制实现——在配置类上加上相应注解
  • 二、启动配置流程与原理
  • 2.1 创建SpringApplication对象
  • 2.2 run()方法
  • 参考文档



一、@SpringBootConfiguration

1.1 注解分析

  该注解标注在某个类上说明这个类是Spring Boot的主配置类,Spring Boot应该运行这个类的main方法来启动Spring Boot应用。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// 重点分析后三个
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
	...
}

1.1.1 @SpringBootConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 配置类
@Configuration
public @interface SpringBootConfiguration {
	...
}
  • 本质就是个配置类,标注在某个类上,表示这是一个Spring Boot的配置类。
  • 配置类 ----- 配置文件:配置类也是容器中的一个组件,也即@Component。

1.1.2 @EnableAutoConfiguration——Spring Boot的全局开关

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

@AutoConfigurationPackage

@Import({AutoConfigurationImportSelector.class})

public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
  • 借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助Spring Boot应用将所有符合条件的 @Configuration配置类都加载到当前Spring Boot创建并使用的IoC容器中。
  • 将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中,最终会给容器中导入非常多的自动配置类xxxAutoConfiguration。
  • Spring Boot在启动时从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,通过Properties加载资源,并将这些值作为自动配置类导入到容器中。
  • 返回的全类名通过反射被实例化,就形成了具体的工厂实例,工厂实例通过createBean() 来生成组件具体需要的bean。

1.2 AutoConfigurationImportSelector类

  借助于SpringFactoriesLoader,能够把 spring-boot-autoconfigure.jar/META-INF/spring.factories中的每一个xxxAutoConfiguration文件都加载到容器中。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
			ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
			...
			protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
				List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
			...
			
			protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
				return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
			}
			...
			return configurations;
}

springboot bat启动加载配置文件conf springboot加载配置文件源码_spring

1.3 Conditional机制实现——在配置类上加上相应注解

  spring.factories文件里的每一个xxxAutoConfiguration文件一般都会有下面的条件注解

springboot bat启动加载配置文件conf springboot加载配置文件源码_List_02

二、启动配置流程与原理

启动过程中四个重要的事件回调机制

  • 自定义的话还需要配置在META-INF/spring.factories中:
  1. ApplicationContextInitializer:扩展该接口,并实现initialize方法,它会在准备IOC容器(prepareContext) 阶段运行。
  2. SpringApplicationRunListener(监听器):扩展该接口,实现所有方法,它们会在相应阶段执行,同时必须实现一个有参构造器。
  • 只需要放在IOC容器中:
  1. ApplicationRunner
  2. CommandLineRunner

  由于callRunners方法从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,所以自定义的ApplicationRunner必须加@Conponent注解。

@SpringBootApplication
public class SpringBoot02Config02Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot02Config02Application.class, args);
    }
}
  • run()——两步:
  1. 先new一个SpringApplication对象实例;
  2. 再执行其run()方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
}

2.1 创建SpringApplication对象

  • 构造函数
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        // 根据 classpath 里面是否存在 ConfigurableWebApplicationContext 来决定是否应该创建一个为 Web 应用使用的 ApplicationContext 类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        // 从多个配置类中找到有 main() 方法的主配置类
        this.mainApplicationClass = this.deduceMainApplicationClass();
}

2.2 run()方法

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 声明 IOC 容器 context
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        // 从类路径下 META-INF/spring.factories 中获取 SpringApplicationRunListeners 
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 然后回调所有的获取的 SpringApplicationRunListeners 的 starting() 方法
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 准备环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            // 根据当前环境利用反射创建 IOC 容器(创建 ApplicationContext)
            // 同时决定创建的是 web 环境的 IOC 容器还是普通的 IOC 容器,并将创建好的 IOC 容器返回
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 准备容器,bean 加载
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 刷新 IOC 容器,执行 IOC 容器初始化,如果是 web 应用还会创建嵌入式的 Tomcat
			// 实现 spring-boot-starter-* 的自动化配置的关键,包括 spring.factories 中的配置类的加载,bean 的实例化等
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			
			// 调用所有 SpringApplicationRunListener 的 started 方法
            listeners.started(context);
            // callRunners 方法从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调
            // ApplicationRunner 先回调,CommandLineRunner 再回调
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
        	// 所有的 SpringApplicationRunListeners 回调 running 方法
            listeners.running(context);
            // 整个 Spring Boot 应用启动完成以后返回启动的 IOC 容器
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
}
  • 准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		// 环境有则获取,无则创建
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        // 配置环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        // 创建环境完成后回调上面的所有 SpringApplicationRunListeners 的 environmentPrepared 方法,表示环境准备完成
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
}
  • 准备IOC容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// 准备上下文环境(IOC 容器),将 environment 保存到 IOC 容器中
        context.setEnvironment(environment);
        // 注册一些组件
        this.postProcessApplicationContext(context);
        // 回调之前保存的所有的 ApplicationContextInitializer 的 initialize 方法
        this.applyInitializers(context);
        // 回调所有的 SpringApplicationRunListener 的 contextPrepared()
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // bean 的加载,逻辑可参考 Spring
        this.load(context, sources.toArray(new Object[0]));
        // prepareContext 运行完成以后回调所有的 SpringApplicationRunListener 的 contextLoaded 方法
        listeners.contextLoaded(context);
}