• Spring Boot 2.1.1.RELEASE Main方法启动详解二


    二、SpringApplication.run(String... args)方法解析

    public ConfigurableApplicationContext run(String... args) {
            // 1.创建一个计时器,并启动计时器
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            // 2.配置java.awt.headless=true
            configureHeadlessProperty();
            // 3.创建发布事件的监听器,用于向ApplicationListener监听器发布不同消息
            SpringApplicationRunListeners listeners = getRunListeners(args);
            // 4.发布启动事件
            listeners.starting();
            try {
                // 5.封装args参数
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 6.(*)加载环境参数
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                // 7.
                configureIgnoreBeanInfo(environment);
                // 8.创建banner,并打印banner
                Banner printedBanner = printBanner(environment);
                // 9.创建spring容器
                context = createApplicationContext();
                // 10.创建异常报告类
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
                // 11.spring容器刷新前处理
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 12.刷新spring容器
                refreshContext(context);
                // 13.spring容器刷新后处理
                afterRefresh(context, applicationArguments);
                // 14.停止计时器
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                // 15.发布spring容器启动完成事件
                listeners.started(context);
                // 16.
                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;
    }

    1.1 stopWatch.start()分析

    public void start(String taskName) throws IllegalStateException {
            if (this.currentTaskName != null) {
                throw new IllegalStateException("Can't start StopWatch: it's already running");
            }
            this.currentTaskName = taskName;
            // 记录当前时间毫秒值
            this.startTimeMillis = System.currentTimeMillis();
    }

    分析:

      记录当前时间的毫秒值,用于计算启动spring容器时耗时

    1.2 配置headless系统变量参数

    private void configureHeadlessProperty() {
            System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }

    1.3 创建发布事件的监听器

    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
            // 1. 加载SpringApplicationRunListener对象
            // 2. 创建SpringApplicationRunListeners对象,统一管理SpringApplicationRunListener对象
            return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

    分析:

      也是通过加载/META-INF/spring.factories中的配置类,创建EventPublishingRunListener时,将所有ApplicationListener交给其管理,并创建了SimpleApplicationEventMulticaster(*)类,它是真正发布事件的类

    • EventPublishingRunListener

    1.4 发布启动事件(SimpleApplicationEventMulticaster)

    public void starting() {
            this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

    1.5 封装args参数

    public CommandLineArgs parse(String... args) {
            CommandLineArgs commandLineArgs = new CommandLineArgs();
            // 解析命令行中的参数
            for (String arg : args) {
                // 解析格式为:
                //   --spring.application.name=student
                //   --spring.application.name student
                //   --name
                if (arg.startsWith("--")) {
                    String optionText = arg.substring(2, arg.length());
                    String optionName;
                    String optionValue = null;
                    if (optionText.contains("=")) {
                        optionName = optionText.substring(0, optionText.indexOf('='));
                        optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
                    } else {
                        optionName = optionText;
                    }
                    if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
                        throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                    }
                    // 将解析出来的键值对存放到commandLineArgs中的map中
                    commandLineArgs.addOptionArg(optionName, optionValue);
                } 
                // 解析格式为:
                //  aaa
                else {
                    // 解析非--前缀的命令,添加到commandLineArgs中list中
                    commandLineArgs.addNonOptionArg(arg);
                }
            }
            return commandLineArgs;
    }

    分析:解析命令行的参数,并封装到CommandLineArgs对象中

    public PropertySource(String name, T source) {
            Assert.hasText(name, "Property source name must contain at least one character");
            Assert.notNull(source, "Property source must not be null");
            // 默认传的名称为:commandLineArgs
            this.name = name;
            // 上一步解析出来的CommandLineArgs对象
            this.source = source;
    }

    分析:封装解析出来的命令行参数CommandLineArgs到PropertySource(父类)->Source(子类)

    public DefaultApplicationArguments(String[] args) {
            Assert.notNull(args, "Args must not be null");
            this.source = new Source(args);
            this.args = args;
    }

    分析:再一次将Source和未解析的args封装到DefaultApplicationArguments中

    1.6 加载环境变量

    private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // 获取配置环境,根据spring容器类型创建相应的StandardServletEnvironment
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            // 给environment.propertyResolver设置ConversionService
            // 添加命令行参数到environment.propertySource
            // 配置environment.activeProfiles
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            // 发布ApplicationEnvironmentPreparedEvent事件
            listeners.environmentPrepared(environment);
            bindToSpringApplication(environment);
            if (!this.isCustomEnvironment) {
                environment = new EnvironmentConverter(getClassLoader())
                        .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
    }
  • 相关阅读:
    HDU 1505 & POJ 1964 City Game (递推+扫描法)
    web页面内容优化管理与性能技巧
    POJ2406简单KMP
    poj2418map或者字典树
    poj2418map或者字典树
    POJ2296二分2sat
    POJ2296二分2sat
    poj2186强联通(牛仰慕)
    poj2186强联通(牛仰慕)
    poj2175费用流消圈算法
  • 原文地址:https://www.cnblogs.com/pascall/p/11364960.html
Copyright © 2020-2023  润新知