SpirngBoot | Startup Principle 01

  Source code analysis, springboot

SpirngBoot

A reader and a good friend of mine contributed an article on the SpringBoot startup principle. I was only a sophomore and was so outstanding that I can expect it in the future.


I’ve always wanted to know how SpirngBoot is started. I’d like to write an analysis on SpirngBoot startup. For the first time writing such a profound technical topic, please forgive me if you don’t understand it properly.

Source version

SpinrgBoot 2.0.2

It is well known that SpringBoot’s startup class is called in a main methodSpringApplication.run()Method, such as:

@SpringBootApplication
public class DiveInSpringBootApplication {

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

}

Start-up sequence analysis is as follows:

Initialization phase-> run phase

Initialization phase:

Enter the run method,SpringApplication.run()A SpringApplication object will be created for it first:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //加载应用资源(URL资源、File资源、ClassPath资源)
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // primarySources 为 run 方法传入的引导类
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //推断Web应用类型
        this.webApplicationType = deduceWebApplicationType();
        //加载应用上下文(初始化Initializers)
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        //加载应用事件监听器(初始化ApplicationListener)
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //推断引导类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

Step1. PassdeduceWebApplicationType()To infer our Web-type applications

private WebApplicationType deduceWebApplicationType() {
    
    //根据当前应用的ClassPath中是否存在相关实现类来推断Web类型
    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;
}

Look at the 3 constant values used:

Constant value Application type
REACTIVE_WEB_ENVIRONMENT_CLASS org.springframework.web.reactive.DispatcherHandler
MVC_WEB_ENVIRONMENT_CLASS org.springframework.web.servlet.DispatcherServlet
WEB_ENVIRONMENT_CLASSES {“javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext” }

In other words, there are three situations:

  1. If the class org.spring framework.web.reactive.dispatcher handler exists in the application, it means that it is a responsive web application, and the embedded responsive web server needs to be loaded when the project is started.
  2. If there is neither javax.servlet.Servlet class nor org.springframework.web.context.configurableapplicationcontext class in the application, then

Indicates that the current application is not a web application and the embedded web server does not need to be loaded when starting.

  1. In addition to the above two cases, it means that the current application is a servlet web application, and the web server (such as Tomcat) that starts the embedded servlet needs to be loaded at startup.

Step2. How to Load Application Context Initiators (Initializers)

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;
}

Using Spirng’s factory loading mechanism, we instantiate the ApplicationContextInitializer implementation class and sort the collection. The specific implementation method is as follows:

  1. viaSpringFactoriesLoader.loadFactoryNamesTo scanMETA-INF/spring.factoriesBelow is the name of the resource that matches the ApplicationContextInitializer type.
  2. Instantiate allMETA-INF/spring.factoriesInformation on resources found under
  3. Prioritize the instantiated resource information, or by@orderComments andOrderedInterface to sort
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
#Spring容器的常见的错误配置警告
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
#设置Spring应用上下文ID
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

Step3. Load ApplicationListener

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;
}

The Spring factory loading mechanism is used to instantiate the ApplicationListene r implementation class and sort the object set. the specific method is as followsInitializes InitializersSimilar, not repeat.

# Application Listeners
org.springframework.context.ApplicationListener=\
#Spring应用上下文加载完成之后清除缓存
org.springframework.boot.ClearCachesApplicationListener,\
#父容器关闭时通知各个子容器关闭,
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
#文件编码
org.springframework.boot.context.FileEncodingApplicationListener,\
#控制台彩色输出
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
#外部化配置 管理factories或者YMAL文件
org.springframework.boot.context.config.ConfigFileApplicationListener,\
#将指定事件广播给指定的监听器
org.springframework.boot.context.config.DelegatingApplicationListener,\
#将需要输出的日志打印到指定的级别 DEBUG INFO ERROR
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
#初始化日志系统
org.springframework.boot.context.logging.LoggingApplicationListener,\
#控制可执行Spirng文件版本
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

Step4. Inferring the boot class

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
                //根据 Main 线程执行堆栈来判断实际的引导类。
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
            // Swallow and continue
    }
    return null;
}

run phase

The entire SpringApplication revolves around run and is divided into two small phases:

  • LoadSpringApplicationRun the listener and listenSpring BootEvents
  • Create Spring Application Context and Create Environment
public ConfigurableApplicationContext run(String... args) {
    //记录运行时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
        //Spring 应用的上下文
    ConfigurableApplicationContext context = null;
        //记录启动期间的错误
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //配置文件加载及优先级判断
    configureHeadlessProperty();
        //获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
        //加载运行监听器
    listeners.starting();
    try {
            //创建ApplicationArguments对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
            //加载属性配置
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
        configureIgnoreBeanInfo(environment);
            //打印Banner
        Banner printedBanner = printBanner(environment);
            //创建应用上下文
        context = createApplicationContext();
            //实例化SpringBootExceptionReporter用于报告启动过程错误。
        exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
            //初始化应用上下文
        prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
            //刷新应用上下文(IOC容器的准备,初始化Bean)
        refreshContext(context);
            //应用上下刷新完成之后
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
            //启动日志记录器
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
        }
            //启动运行监听器
        listeners.started(context);
            //启动后需要的操作
        callRunners(context, applicationArguments);
        ....
    }
}

Step1. loadSpringApplicationRun listener

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

Using Spirng’s factory loading mechanism, the implementation class of SpringApplicationRunListeners is instantiated and the collection is sorted. The specific implementation method is as follows:

  1. viaSpringFactoriesLoader.loadFactoryNamesTo scanMETA-INF/spring.factoriesBelow is the name of the resource that matches the SpringApplicationRunListeners type.
  2. Instantiate allMETA-INF/spring.factoriesInformation on resources found under
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListenr

This shows that onlyEventPublishingRunListenrAn Implementation Class

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        //实例化SimpleApplicationEventMulticaster事件发布者
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        //以迭代的方法逐一进行ApplicationListener的监听
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }

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

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                this.application, this.args, environment));
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationStartedEvent(this.application, this.args, context));
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationReadyEvent(this.application, this.args, context));
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
                this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }

    private static class LoggingErrorHandler implements ErrorHandler {

        private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

        @Override
        public void handleError(Throwable throwable) {
            logger.warn("Error calling ApplicationEventListener", throwable);
        }

    }

}

InEventPublishingRunListenerWhen instantiating, one will be instantiatedSimpleApplicationEventMulticasterThe event publisher (its function is to monitor the events published in the container, and whenever an event occurs, it triggers the callback of the listener to complete event-driven development), and then calls thelisteners.starting()The method passes through its internalinitialMulticasterAttribute publishingApplicationStartingEventEvents.

Step2. Create Spirng Application Context

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                //初始化阶段的推断Web类型
                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);
    }

According to the inferred Web application type in the initialization phase

If the inferred SERVLETWeb type instantiates this object

web.servlet.context.AnnotationConfigServletWebServerApplicationContext

Step3. CreateEnvironment

    private ConfigurableEnvironment prepareEnvironment(
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments) {
        // Create and configure the environment
        //创建 ConfigurableEnvironment 对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
        //配置 ConfigurableEnvironment
    configureEnvironment(environment, applicationArguments.getSourceArgs());
        //发布 ApplicationEnvironmentPreparedEvent 事件
    listeners.environmentPrepared(environment);
        //将 ConfigurableEnvironment 绑定到 SpringApplication 中
    bindToSpringApplication(environment);
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
        .convertToStandardEnvironmentIfNecessary(environment);
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

The corresponding ConfigurableEnvironment instance is created according to the inferred Web application type in the initialization phase.

So far the whole SpringBoot process has been analyzed: let’s summarize:

  1. First initialize the SpringApplication class and infer the WEB startup type, then initialize and implement the application event listener, and then infer the boot class.
  2. Call their started methods through the SpringFactoriesLoader loaded SpringApplicationRunListener.
  3. According to the Web service type, different Spring ApplicationContexts are created, and the previously prepared Environment is set to the Spring application context for use.
  4. Create and configure the Environment to be used by the current Spring Boot application, such as applocation.properties file and external configuration.
  5. SpirngBoot starts.

Recommended reading

Java | What is Dynamic Proxy?

How does SpringBoot | implement logging?

How does SpringBoot | implement automatic configuration?

Postscript

If this article is of any help to you, please help me look good. Your good looks are my motivation to persist in writing.
In addition, after the attention is sent1024Free study materials are available.

For details, please refer to this old article:Python, C++, Java, Linux, Go, Front End, Algorithm Data Sharing

一个优秀的废人