SpringBoot之SpringApplication Explain

SpringApplication Explain

The SpringApplication class provides a convenient way to bootstrap a Spring application that is started from a main() method. In many situations, you can delegate to the static SpringApplication.run method, as shown in the following example.java

(SpringApplication類提供了一種便利的方式以便引導Spring應用從#main啓動,大多數狀況下,能夠經過靜態方法SpringApplication#run代理啓動)web

How TO Use SpringApplication

@EnableAutoConfiguration
public class MyApplication  {
    // ... Bean definitions
    public static void main(String[] args) throws Exception {
      SpringApplication.run(MyApplication.class, args);
    }
 }

Customize SpringApplication

UseSpringApplicationAPI To Change

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);
    app.run(args);
}

UseSpringApplicationBuilder API To Change

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

SpringApplication Preparing Stage

Configure Spring Boot Bean's Sources

Java 配置類或XML上下文配置集合,用於Spring Boot BeanDefinitionLoader讀取,而且將配置源解析加載爲Spring Bean 定義。spring

  • 數量,一個或者多個

Java Configuration Class

使用Spring 註解驅動中的Java配置類,也就是Spring 模式註解所標註的類,例如@Configuration編程

package com.yi23.backend.springboot.bootstrap;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * {@link SpringApplication} 啓動引導類
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 * @see
 * @since
 */
@SpringBootApplication
public class SpringApplicationBootstrap {

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

XML Configure

用戶Spring 傳統配置驅動的XML文件bootstrap

...
        //設置Annotation配置源
        Set<String> sources = new HashSet<>();
        //A source can be: a class name, package name, or an XML resource location.
        sources.add(Yi23ApplicationConfiguration.class.getName());
        springApplication.setSources(sources);
...

Deduce Web Application Type

根據當前應用CLassPath中是否存在相關實現來推斷Web Application Type,包括:api

  • Web Reactive : WebApplicationType.REACTIVE
  • Web Servlet : WebApplicationType.SERVLET
  • 非Web : WebApplicationType.NONE

參考:org.springframework.boot.SpringApplication#deduceWebApplicationTypespringboot

private WebApplicationType deduceWebApplicationType() {
        if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }
  • 版本意識,下圖爲Spring Boot 2.1.3中的推斷實現
    image-20190306144854302

Deduce Main Class

跟住main線程的執行堆棧判斷當前實際的引導類app

參考:org.springframework.boot.SpringApplication#deduceMainApplicationClass異步

private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

Load ApplicationContextInitializer

ApplicationContextInitializer是應用上下文的加載器,利用Spring 工廠加載機制,實例化ApplicationContextInitializer實現類,並實現對象集合排序ide

  • 代碼
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
  • 實現技術
    • 實現類:org.springframework.core.io.support.SpringFactoriesLoader
    • 配置文件:META-INF/spring.factories
    • 排序:org.springframework.core.annotation.AnnotationAwareOrderComparator#sort
package com.yi23.backend.springboot.context;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

/**
 * 自定義高優先級{@link ApplicationContextInitializer}初始化加載器
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 * @see
 * @since
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloYi23ApplicationContextInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("高優先級初始化加載class : "
                + applicationContext.getId());
    }
}
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.yi23.backend.springboot.context.AfterHelloYi23ApplicationContextInitializer,\
com.yi23.backend.springboot.context.HelloYi23ApplicationContextInitializer

Load ApplicationListener

利用Spring 工廠加載機制,實例化ApplicationLisener實現類,並實現對象集合排序

package com.yi23.backend.springboot.event.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
/**
 * hello yi23 {@link ApplicationListener} 監聽 {@link ContextRefreshedEvent}事件
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 * @see
 * @since
 */
public class HelloYi23ApplicationListener
        implements ApplicationListener<ContextRefreshedEvent>,Ordered {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.printf("上下文內容id:%s,timestamp:%s.\r\n",
                event.getApplicationContext().getId(), event.getTimestamp());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

SpringApplication Running Stage

Load SpringApplication Run Listener

org.springframework.boot.SpringApplicationRunListeners

利用工廠加載機制,讀取SpringApplicationRunListener對象集合,並封裝到組合對象SpringApplicationRunListeners

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

Running SpringApplication Run Listeners

SpringApplicationRunListener 監聽多個運行狀態方法

監聽方法 階段說明 Springboot 起始版本
starting Spring 應用剛剛啓動 1.0
environmentPrepared(ConfigurableEnvironment) ConfigurableEnvironment準確以後,容許將其修改 1.0
contextPrepared(ConfigurableApplicationContext) ConfigurableApplicationContext準備以後,容許將其修改 1.0
contextLoaded(ConfigurableApplicationContext) ConfigurableApplicationContext已加載,但未啓動 1.0
started(ConfigurableApplicationContext) ConfigurableApplicationContext已啓動,當前Spring Bean已初始化完成 2.0.0
running(ConfigurableApplicationContext) Spring Application 正在運行 2.0.0
failed(ConfigurableApplicationContex,Throwable) Spring Application運行失敗 2.0.0

Monitor Spring-Boot Event / Spring Event

SpringBoot 經過SpringApplicationRunListener的實現類EventPublishingRunListener,利用Spring Framework事件API,廣播Spring Boot 事件

Spring Framework Event/Listener Model

  • Spring 應用事件
    • 普通應用事件:ApplicationEvent
    • 應用上下文事件:ApplicationContextEvent
  • Spring 應用監聽器
    • 接口編程模型:ApplicationListener
    • 註解編程模型:@EventListener
  • Spring 應用事件廣播器
    • 接口:ApplicationEventMulticaster
    • 實現類:SimpleApplicationEventMulticaster
      • 執行模式:同步/異步

EventPublishingRunListener relationship of Monitor‘s Method & Spring Boot Events

監聽方法 Spring Boot 事件 Spring boot 起始版本
starting ApplicationStartingEvent 1.5.0
environmentPrepared(ConfigurableEnvironment) ApplicationEnvironmentPreparedEvent 1.0
contextPrepared(ConfigurableApplicationContext)
contextLoaded(ConfigurableApplicationContext) ApplicationPreparedEvent 1.0
started(ConfigurableApplicationContext) ApplicationStartedEvent 2.0.0
running(ConfigurableApplicationContext) ApplicationReadyEvent 1.3.0
failed(ConfigurableApplicationContex,Throwable) ApplicationFailedEvent 1.0

Validate ConfigFileApplicationListener Order

package com.yi23.backend.springboot.event.listener;

import org.springframework.boot.context.config.ConfigFileApplicationListener;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;

/**
 * Before {@link ConfigFileApplicationListener}
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 * @see
 * @since
 */
public class BeforeConfigFileApplicationListener implements SmartApplicationListener, Ordered {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
                || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return true;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            ApplicationEnvironmentPreparedEvent environmentPreparedEvent = (ApplicationEnvironmentPreparedEvent) event;
            Environment environment = environmentPreparedEvent.getEnvironment();
            System.out.println("environment.getProperty(\"name\"): " + environment.getProperty("name"));
        }
        if (event instanceof ApplicationPreparedEvent) {

        }
    }

    @Override
    public int getOrder() {
        //比 ConfigFileApplicationListener 優先級高
        return ConfigFileApplicationListener.DEFAULT_ORDER - 1;
    }
}

Create Spring Application Context(ConfigurableApplicationContext)

根據前面提到的Prepared 階段推斷出的Web 應用類型對應的ConfigurableApplicationContext實例:

  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非Web: AnnotationConfigApplicationContext
/**
     * Strategy method used to create the {@link ApplicationContext}. By default this
     * method will respect any explicitly set application context or application context
     * class before falling back to a suitable default.
     * @return the application context (not yet refreshed)
     * @see #setApplicationContextClass(Class)
     */
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

Create Environment

根據Prepead 階段推斷的Web應用類型建立對應的 ConfigurableEnvironment

  • Web Servlet: StandardServletEnvironment
  • Web Reactive: StandardEnvironment
  • 非Web: StandardEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        if (this.webApplicationType == WebApplicationType.SERVLET) {
            return new StandardServletEnvironment();
        }
        return new StandardEnvironment();
    }
相關文章
相關標籤/搜索