SpringBoot启动流程简析(二)

简介: 在这篇文章中,我们接着上一篇的内容接着分析。 public ConfigurableApplicationContext run(String.

在这篇文章中,我们接着上一篇的内容接着分析。

    public ConfigurableApplicationContext run(String... args) {
        //启动应用的检测
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //SpringBoot的上下文
        ConfigurableApplicationContext context = null;
        //失败分析报告
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        //SpringBoot的runlistener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
        //参数解析
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
                    //配置环境变量
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            //输出Banner信息        
            Banner printedBanner = printBanner(environment);
            //创建应用上下文
            context = createApplicationContext();
            analyzers = new FailureAnalyzers(context);
            //refresh上下文之前的准备
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

SpringApplication中的run方法的内容如上所示,上面就是整个SpringBoot应用启动的主要调用方法,run方法中的参数即是我们的应用参数。下面我们来简单的分析一下这个启动过程。
StopWatch主要是监控启动过程,统计启动时间,检测应用是否已经启动或者停止。

SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
        SpringApplicationRunListener.class, types, this, args));
}

通过在上一篇文章中的问题,对于getSpringFactoriesInstances这个方法你应该不陌生来吧。这里也是从META-INF/spring.factories中获取类型为org.springframework.boot.SpringApplicationRunListener的配置值,这个默认的配置值为:org.springframework.boot.context.event.EventPublishingRunListener。我们进入到EventPublishingRunListener这个类看一下它的构造函数

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        //创建一个SimpleApplicationEventMulticaster
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //把之前在SpringApplication中获取到的listener循环放入到SimpleApplicationEventMulticaster中
        for (ApplicationListener<?> listener : application.getListeners()) {
    this.initialMulticaster.addApplicationListener(listener);
        }
    }

通过上面的分析,我们可以看到EventPublishingRunListener把SpringApplication中的监听器,都放到了SimpleApplicationEventMulticaster中,进行了统一的管理。listeners.starting();启动事件监听,这里以后我们单独开章节详细说明.

 //创建应用参数解析器
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);

我们看一下DefaultApplicationArguments的构造函数的内容:

    public DefaultApplicationArguments(String[] args) {
            //首先判断不能为null,这里大家可以想一下可变参数如果不传值的话看看是什么内容
        Assert.notNull(args, "Args must not be null");
        //调用Source对应用参数进行解析
        this.source = new Source(args);
        this.args = args;
    }
Source(String[] args) {
//调用父类的构造函数 Source的继承关系如下图所示
    super(args);
}
public SimpleCommandLinePropertySource(String... args) {
    //对应参数进行解析的工作
    super(new SimpleCommandLineArgsParser().parse(args));
}

Source
大家在配置应用参数的时候,是这样这样配置的 - -key=value,为什么要以- -开头呢?在SimpleCommandLineArgsParser的parse方法中你会找到答案。并且Source这个类还继承类PropertySource这个类。

//准备环境变量
ConfigurableEnvironment environment =           prepareEnvironment(listeners,applicationArguments); 
private ConfigurableEnvironment prepareEnvironment(
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
    //获取环境变量 
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //将应用参数放入到环境变量持有对象中
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //监听器监听环境变量对象的变化
    listeners.environmentPrepared(environment);
    //如果非web环境,则转换为StandardEnvironment对象
    if (!this.webEnvironment) {
        environment = new EnvironmentConverter(getClassLoader())            .convertToStandardEnvironmentIfNecessary(environment);
    }
    return environment;
}
    private ConfigurableEnvironment getOrCreateEnvironment() {
        //如果已经创建过存放环境变量的对象了,则直接返回
        if (this.environment != null) {
            return this.environment;
        }
        //如果是web环境则创建StandardServletEnvironment对象
        if (this.webEnvironment) {
            return new StandardServletEnvironment();
        }
        //非web环境,创建StandardEnvironment
        return new StandardEnvironment();
    }

StandardServletEnvironment的UML图如下所示,StandardServletEnvironment集成了系统变量、环境变量、配置属性信息等内容。这些内容我们以后单开一个篇章来说一下。
StandardServletEnvironment

//这句话是输出SpringBoot的Banner信息,可以从指定的位置加载信息,可以输出为文字形式,也可以输出为图片形式,如我们常见的SpringBoot的logo就是在这里输出的
Banner printedBanner = printBanner(environment);

SpringBootBanner
Banner的UML类图如下所示:
Banner
我们最常见的SpringBoot的logo“图像”就在SpringBootBanner这个类中定义的,这个也是SpringBoot默认的Banner类。
SpringBootBanner

//创建SpringBoot的应用上下文
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
        //DEFAULT_WEB_CONTEXT_CLASS = org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
        /// DEFAULT_CONTEXT_CLASS = org.springframework.context.annotation.AnnotationConfigApplicationContext
        contextClass = Class.forName(this.webEnvironment
                ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

因为我们是web开发环境,所以这里我们的web上下文是AnnotationConfigEmbeddedWebApplicationContext这个对象,一定要记住这个类,要仔细的看看它的UML类图。

相关文章
|
6月前
|
前端开发 Java 应用服务中间件
SpringBoot-Run启动流程
探索Spring Boot启动流程,从加载配置、创建应用上下文、自动配置到启动内嵌服务器。启动类是入口点,`@SpringBootApplication`标记启动,`SpringApplication.run`启动应用。自动配置基于条件注解配置Bean,应用上下文由`SpringApplication`创建并刷新。内嵌服务器如Tomcat随应用启动,简化部署。理解此流程有助于深入掌握Spring Boot。
214 2
|
6月前
|
缓存 Java 程序员
springboot的启动流程总结
springboot的启动流程总结
|
6月前
|
设计模式 Java 容器
SpringBoot2 | SpringBoot启动流程源码分析(二)
SpringBoot2 | SpringBoot启动流程源码分析(二)
79 0
|
6月前
|
设计模式 Java 机器人
SpringBoot3自动配置流程 SPI机制 核心注解 自定义starter
SpringBoot3自动配置流程 SPI机制 核心注解 自定义starter
|
1月前
|
前端开发 Java 数据安全/隐私保护
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
文章通过一个简单的SpringBoot项目,详细介绍了前后端如何实现用户登录功能,包括前端登录页面的创建、后端登录逻辑的处理、使用session验证用户身份以及获取已登录用户信息的方法。
185 2
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
65 3
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
30 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
177 2
|
1月前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
3月前
|
XML Java 应用服务中间件
SpringBoot启动流程解析
SpringBoot启动流程解析
47 0