SpringApplication到底run了什么上

7次阅读

共计 6419 个字符,预计需要花费 17 分钟才能阅读完成。

SpringApplication 对象实例的创建过程,本篇文章继续看 run 方法的执行逻辑吧

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);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                applicationArguments);
                // 后面还有,本篇文章就解析到这。。。。
}

  • 第一行使用了 StopWatch 来记录开始时间
  • 设置了 java.awt.headless 环境变量,在网上了解了一下这个变量的相关信息

Headless 模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式

个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正

  • 接着遍历所有构造 SpringApplication 实例时加载的 SpringApplicationRunListener,调用它们的 started 方法

这里构造时仅仅加载了一个 EventPublishingRunListener 类,所以咱们就来解析一下这个东东

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

可以看到这里调用了 SimpleApplicationEventMulticaster 类的 multicastEvent 方法并且传入了 ApplicationStartingEvent 对象,看名字就知道了这个是 SpringBoot 启动事件

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

其中获取监听器使用的是 getApplicationListeners 方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合

接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器

  • 把所有的命令行启动参数封装成 ConfigurableEnvironment 对象
  • 准备运行时环境

private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        listeners.environmentPrepared(environment);
        if (!this.webEnvironment) {
                environment = new EnvironmentConverter(getClassLoader())
                                .convertToStandardEnvironmentIfNecessary(environment);
        }
        return environment;
}

获取或创建环境 getOrCreateEnvironment

方法名就很直观,有就直接获取,没有就新建

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
                return this.environment;
        }
        if (this.webApplicationType == WebApplicationType.SERVLET) {
                return new StandardServletEnvironment();
        }
        return new StandardEnvironment();
}

上篇文章中说过了,咱们是 Servlet 环境,所以当前方法是返回一个 StandardServletEnvironment 对象,这个对象的构造过程中调用了 customizePropertySources 方法(它父类的父类调用的)

protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource(“servletConfigInitParams”));
        propertySources.addLast(new StubPropertySource(“servletContextInitParams”));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
                propertySources.addLast(new JndiPropertySource(“jndiProperties”));
        }
        super.customizePropertySources(propertySources);
}
// 这是它父类的
protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource(“systemProperties”, getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource(“systemEnvironment”, getSystemEnvironment()));
}

可以看出 StandardServletEnvironment 往 propertySources 中添加了两个 StubPropertySource 对象,而它的父类添加了一个包含 java 系统属性和一个操作系统环境变量的对象

配置 configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment,
    String[] args) {
    // 配置 PropertySources
    configurePropertySources(environment, args);
    // 配置 Profiles
    configureProfiles(environment, args);
}

分别看一下两个方法

配置 PropertySources

protected void configurePropertySources(ConfigurableEnvironment environment,
        String[] args) {
    MutablePropertySources sources = environment.getPropertySources();
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        // 存在默认配置将其放到最后位置
        sources.addLast(
                new MapPropertySource(“defaultProperties”, this.defaultProperties));
    }
    // 如果存在命令行参数则将原有的替换掉
    if (this.addCommandLineProperties && args.length > 0) {
        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
        if (sources.contains(name)) {
            PropertySource<?> source = sources.get(name);
            CompositePropertySource composite = new CompositePropertySource(name);
            composite.addPropertySource(new SimpleCommandLinePropertySource(
                    “springApplicationCommandLineArgs”, args));
            composite.addPropertySource(source);
            sources.replace(name, composite);
        }
        else {
            // 将其放到第一位置
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}

这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了

配置 Profiles

从 PropertySources 中查找 spring.profiles.active 属性,存在则将其值添加 activeProfiles 集合中

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    environment.getActiveProfiles();
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

发布 EnvirongmentPreparedEvent 事件 绑定环境

protected void bindToSpringApplication(ConfigurableEnvironment environment) {
                try {
                        Binder.get(environment).bind(“spring.main”, Bindable.ofInstance(this));
                }
                catch (Exception ex) {
                        throw new IllegalStateException(“Cannot bind to SpringApplication”, ex);
                }
        }

转换环境

如果 web 环境变更为 NONE 则将 StandardServletEnvironment 转换为 StandardEnvironment

ConfigurationPropertySources.attach(environment)

public static void attach(Environment environment) {
                Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
                MutablePropertySources sources = ((ConfigurableEnvironment) environment)
                                .getPropertySources();
                PropertySource<?> attached = sources.get(“configurationProperties”);
                if (attached != null && attached.getSource() != sources) {
                        sources.remove(“configurationProperties”);
                        attached = null;
                }
                if (attached == null) {
                        sources.addFirst(new ConfigurationPropertySourcesPropertySource(
                                        “configurationProperties”,
                                        new SpringConfigurationPropertySources(sources)));
                }
        }

最终这个 sources 对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘

正文完
 0

SpringApplication到底run了什么上

7次阅读

共计 5788 个字符,预计需要花费 15 分钟才能阅读完成。

在上篇文章:SpringBoot 源码解析:创建 SpringApplication 对象实例中,我们详细描述了 SpringApplication 对象实例的创建过程,本篇文章继续看 run 方法的执行逻辑吧

    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);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            // 后面还有,本篇文章就解析到这。。。。}
  1. 第一行使用了 StopWatch 来记录开始时间
  2. 设置了 java.awt.headless 环境变量,在网上了解了一下这个变量的相关信息

Headless 模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式

个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正

  1. 接着遍历所有构造 SpringApplication 实例时加载的 SpringApplicationRunListener,调用它们的started 方法

这里构造时仅仅加载了一个 EventPublishingRunListener 类,所以咱们就来解析一下这个东东

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

可以看到这里调用了 SimpleApplicationEventMulticaster 类的 multicastEvent 方法并且传入了 ApplicationStartingEvent 对象,看名字就知道了这个是 SpringBoot 启动事件


public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();
            if (executor != null) {executor.execute(() -> invokeListener(listener, event));
            }
            else {invokeListener(listener, event);
            }
        }
    }

其中获取监听器使用的是 getApplicationListeners 方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合

接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器

  1. 把所有的命令行启动参数封装成 ConfigurableEnvironment 对象
  2. 准备运行时环境
    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        listeners.environmentPrepared(environment);
        if (!this.webEnvironment) {environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        return environment;
    }
获取或创建环境getOrCreateEnvironment

方法名就很直观,有就直接获取,没有就新建

    private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}
        if (this.webApplicationType == WebApplicationType.SERVLET) {return new StandardServletEnvironment();
        }
        return new StandardEnvironment();}

上篇文章中说过了,咱们是 Servlet 环境,所以当前方法是返回一个 StandardServletEnvironment 对象,这个对象的构造过程中调用了 customizePropertySources 方法(它父类的父类调用的)

    protected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource("jndiProperties"));
        }
        super.customizePropertySources(propertySources);
    }
  // 这是它父类的
 protected void customizePropertySources(MutablePropertySources propertySources) {propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
    }

可以看出 StandardServletEnvironmentpropertySources中添加了两个 StubPropertySource 对象,而它的父类添加了一个包含 java 系统属性和一个操作系统环境变量的对象

配置 configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment,
    String[] args) {
    // 配置 PropertySources
    configurePropertySources(environment, args);
    // 配置 Profiles
    configureProfiles(environment, args);
}

分别看一下两个方法

配置 PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment,
        String[] args) {MutablePropertySources sources = environment.getPropertySources();
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        // 存在默认配置将其放到最后位置
        sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
    }
    // 如果存在命令行参数则将原有的替换掉
    if (this.addCommandLineProperties && args.length > 0) {
        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
        if (sources.contains(name)) {PropertySource<?> source = sources.get(name);
            CompositePropertySource composite = new CompositePropertySource(name);
            composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
            composite.addPropertySource(source);
            sources.replace(name, composite);
        }
        else {
            // 将其放到第一位置
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}

这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了

配置 Profiles

从 PropertySources 中查找 spring.profiles.active 属性,存在则将其值添加 activeProfiles 集合中

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {environment.getActiveProfiles(); 
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
发布 EnvirongmentPreparedEvent 事件
绑定环境
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
        try {Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
        }
        catch (Exception ex) {throw new IllegalStateException("Cannot bind to SpringApplication", ex);
        }
    }
转换环境

如果 web 环境变更为 NONE 则将 StandardServletEnvironment 转换为StandardEnvironment

ConfigurationPropertySources.attach(environment)
public static void attach(Environment environment) {Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment)
                .getPropertySources();
        PropertySource<?> attached = sources.get("configurationProperties");
        if (attached != null && attached.getSource() != sources) {sources.remove("configurationProperties");
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(
                    "configurationProperties",
                    new SpringConfigurationPropertySources(sources)));
        }
    }

最终这个 sources 对象的第一个位置放的是它自己,循环引用,这个具体的含义还有待挖掘


正文完
 0