SpirngBoot | 启动原理 01

简介: 我一直想了解一下 SpirngBoot 的是如何启动的,我想就来写一篇关于 SpirngBoot 启动分析吧。第一次写那么高深的技术话题理解不到位的话也请多多包涵。

众所周知 SpringBoot 的启动类是在一个 main 方法中调用SpringApplication.run()方法启动的,如:


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


启动顺序分析如下:


初始化阶段 -> 运行阶段


1. 初始化阶段


进入run方法中,SpringApplication.run()会先为其创建一个 SpringApplication 对象:


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. 通过deduceWebApplicationType()来推断我们Web类型应用
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;
}


看看使用到的 3 个常量值:


常量值


应用类型

REACTIVE_WEB_ENVIRONMENT_CLASS
org.springframework.web.reactive.DispatcherHandler
MVC_WEB_ENVIRONMENT_CLASS
org.springframework.web.servlet.DispatcherServlet
EB_ENVIRONMENT_CLASSES
{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }


也就是说,有如下三种情况:


1. 如果应用程序中存在org.springframework.web.reactive.DispatcherHandler 这个类,则表示是一个响应式 web 应用,项目在启动时,需要去加载启动内嵌的响应式 web 服务器。


2. 如果应用程序中既不存在 javax.servlet.Servlet 类,也不存在

org.springframework.web.context.ConfigurableWebApplicationContext 这个类,则表示当前应用不是一个web应用,启动时无需加载启动内嵌的 web 服务器。


3. 除上述两种情况外,则表示当前应用是一个 servlet 的 web 应用,启动时需要加载启动内嵌的 servlet 的 web 服务器(比如 Tomcat )。


Step2. 如何加载应用上下文初始器(初始化 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;
}


利用Spirng的工厂加载机制,实例化ApplicationContextInitializer实现类,并排序集合。具体实现方法如下:


1.通过SpringFactoriesLoader.loadFactoryNames来扫描META-INF/spring.factories下符合 ApplicationContextInitializer 类型的资源名称。


2. 实例化所有在META-INF/spring.factories下找到的资源信息


3. 对实例化的资源信息进行优先级顺序排序,或通过@order注解和Ordered接口进行排序


# 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. 加载应用事件监听器( 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;
}


利用 Spring 工厂加载机制,实例化 ApplicationListene r实现类,并排序对象集合,具体方法跟上面初始化Initializers类似,不赘述。


# 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. 推断引导类
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;
}


2. 运行阶段


整个 SpringApplication 围绕着 run 这个方法并分为两个小阶段:


1. 加载SpringApplication运行监听器,并监听Spring Boot事件


2. 创建 Spring 应用上下文和创建 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. 加载SpringApplication运行监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
        SpringApplicationRunListener.class, types, this, args));
}


利用 Spirng 的工厂加载机制,实例化 SpringApplicationRunListeners 实现类,并排序集合。具体实现方法如下:


1.通过SpringFactoriesLoader.loadFactoryNames来扫描META-INF/spring.factories下符合 SpringApplicationRunListeners 类型的资源名称。


2. 实例化所有在META-INF/spring.factories下找到的资源信息


# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListenr
由此可见就只有EventPublishingRunListenr一个实现类
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);
        }
    }
}


在EventPublishingRunListener实例化的时候,会实例化一个

SimpleApplicationEventMulticaster事件发布者(它的作用就是监听容器中发布的事件,只要事件发生,就触发监听器的回调,来完成事件驱动开发),于是接下来调用listeners.starting()方法就会通过其内部的initialMulticaster属性发布

ApplicationStartingEvent事件。


Step2. 创建Spirng应用上下文


 

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


根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableApplicationContext 实例如果推断的为 SERVLETWeb 类型就实例化这个对象


web.servlet.context.AnnotationConfigServletWebServerApplicationContext


Step3. 创建Environment

 

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


根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableEnvironment 实例。


至此整一个的 SpringBoot 过程已经分析完毕:我们来总结一下:


1. 首先初始化 SpringApplication 类,并推断 WEB 启动类型,再初始化和实现应用事件监听器,然后推断引导类。


2. 通过 SpringFactoriesLoader 加载的 SpringApplicationRunListener,调用它们的 started 方法。


3. 根据 Web 服务类型创建不同的 Spring 应用上下文,并将之前准备好的 Environment 设置给 Spring 应用上下文 ApplicationContext 使用。


4. 创建并配置当前 Spring Boot 应用将要使用的 Environment,如

applocation.properties 文件和外部配置。


5. SpirngBoot 开始启动。

相关文章
Servlet启动原理和原始运行方式
Servlet启动原理和原始运行方式
87 0
Servlet启动原理和原始运行方式
|
2月前
|
Java 调度
Java实现定时启动,且只执行一次,如何实现?
【10月更文挑战第18天】Java实现定时启动,且只执行一次,如何实现?
278 3
|
7月前
|
XML Java 开发者
springboot 启动原理、启动过程、启动机制的介绍
【5月更文挑战第13天】Spring Boot 是一种基于 Java 的框架,用于创建独立的、生产级别的 Spring 应用程序。它的主要目标是简化 Spring 应用的初始搭建和开发过程,同时提供一系列大型项目常见的非功能性特征(如嵌入式服务器、安全性、度量、健康检查和外部化配置)。
501 3
|
6月前
|
网络协议 算法 数据库
IS-IS原理与配置
IS-IS原理与配置
|
Java 应用服务中间件 容器
Tomcat原理系列之三:对请求的过程详细分析
Tomcat原理系列之三:对请求的过程详细分析
Tomcat原理系列之三:对请求的过程详细分析
|
Java 数据库连接 开发者
探讨spring的启动类run方法启动的时候底层会执行什么
Spring是一个开源的Java应用程序框架,它为开发者提供了一种简化企业级应用开发的方式。Spring框架的核心是一个轻量级的容器,它能够管理和协调应用程序中的各个组件。在Spring框架中,Spring Boot是一个用于快速构建独立的、生产级的Spring应用程序的工具。Spring Boot简化了Spring应用程序的配置和部署,使得开发者可以更专注于业务逻辑的实现。
94 0
|
缓存 数据库
项目启动时执行指定任务如何实现?
项目启动时执行指定任务如何实现?
项目启动时执行指定任务如何实现?
( ఠൠఠ )ノ 一键启动要启动的程序
( ఠൠఠ )ノ 一键启动要启动的程序
169 0
( ఠൠఠ )ノ 一键启动要启动的程序
|
SQL 关系型数据库 MySQL
mysql主从复制原理及配置步骤
mysql主从复制原理及配置步骤
183 1
mysql主从复制原理及配置步骤

相关实验场景

更多