SpringBoot启动配置原理

前言

  关于SpringBoot自动装配的概念:

  • 1)SpringBoot启动会加载大量的自动配置类
  • 2)xxxAutoCOnfiguration:自动配置类
  • 3)自动配置类中会配置相应的组件,这些组件不需要我们手动配置
  • 4)在给容器自动配置类添加组件的时候,会从xxxProperties类中获取某些属性,我们可以在yml、properties配置文件中修改这些属性值
一. 启动原理
1.主配置类入口
@SpringBootApplication
public class DevServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(DevServiceApplication.class,args);
    }
}

SpringApplication这个类主要做了4件事情:
1. 推 断 应 用 的 类 型 是 普 通 项 目 还 是 W e b 项 目 \color{blue}{1. 推断应用的类型是普通项目还是Web项目} 1.Web
2. 查 找 并 加 载 所 有 的 可 用 初 始 化 器 , 设 置 到 i n i t i a l i z e r s 属 性 中 \color{blue}{2. 查找并加载所有的可用初始化器,设置到initializers属性中} 2.initializers
3. 找 出 所 有 的 应 用 程 序 监 听 器 , 用 于 处 理 上 下 文 获 取 B e a n , 设 置 到 l i s t e n e r s 属 性 中 \color{blue}{3. 找出所有的应用程序监听器,用于处理上下文获取Bean,设置到listeners属性中} 3.Beanlisteners
4. 推 断 并 设 置 m a i n 方 法 的 定 义 类 , 并 找 到 运 行 的 方 法 \color{blue}{4.推断并设置main方法的定义类,并找到运行的方法} 4.main

2.进入run方法
(new SpringApplication(primarySources)).run(args);
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}
3.创建SpringApplication对象:

包括了初始化和赋值操作,配置Initializers和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));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
二. 运行流程

调用SpringApplication对象的run()方法:

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

该方法中实现了如下几个关键步骤:

1.创建了应用的监听器SpringApplicationRunListeners并开始监听

2.加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment

3.配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)

4.创建run()返回对象:ConfigurableApplicationContext(应用配置上下文),根据环境决定创建Web的ioc还是普通的ioc

5.prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联

6.接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作

配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成,接下来我们来探讨自动化配置是如何实现。

三. 自动配置原理

SpringBoot自动配置模块,主要使用到了SpringFactoriesLoader,即Spring工厂加载器
在这里插入图片描述
下图有助于我们形象理解自动配置流程:
在这里插入图片描述
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。

此部分主要参考博文:
https://blog.csdn.net/hfmbook/article/details/100507083

这是在我查看SpringBoot注解源码后绘制的流程图:
在这里插入图片描述
得出的结论是:
  SpringBoot所有自动配置都会在启动类中自动被扫描和加载,存放在SpringBoot_autoconfigure_META-INF下的spring.factories里,但需要我们手动导入对应的starter才会启动。

1.SpringBoot在启动时,会从类路径/META-INF里的spring.factories里获取指定的配置类:
在这里插入图片描述
2.将这些自动配置的类导入容器,自动配置类就会生效,由Spring容器帮我们自动配置

3.以前我们要自己配置的东西,现在都由SpringBoot帮我们自动完成了

4.整个JavaEE的解决方案和自动配置的资源都在spring-boot-autoconfigure-2.2.5.RELEASE.jar这个核心包下

5.核心包里的spring.factories包含了所有的配置类,以XXXAutoConfiguration的全类名文件存在,这些文件会向容器导入所有这个场景所需的组件,并自动配置@Configuration

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值