精华内容
下载资源
问答
  • springboot启动流程

    2020-07-31 22:08:37
    springboot启动流程

    springboot启动类

    package com.example.springbootstart;
    
    import org.springframework.boot.Banner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringBootStartApplication {
    
        public static void main(String[] args) {
        /**
        下方注释的代码与创建实例对象后的操作差不多,只是那里拆分了。
        */
    //        SpringApplication.run(SpringBootStartApplication.class, args);
            SpringApplication app = new SpringApplication(SpringBootStartApplication.class);
            app.setBannerMode(Banner.Mode.OFF);// 不显示banner
    //        app.setBannerMode(Banner.Mode.OFF);// 关闭启动Banner
    //        app.setBannerMode(Banner.Mode.CONSOLE);// 输出Banner到控制台
    //        app.setBannerMode(Banner.Mode.LOG);// 输出Banner到日志中
    
            app.run(args);
        }
    
    }
    
    

    从整体出发观察,我们发现除去@SpringBootApplication注解,和普通的功能测试代码没有很大区别,于是,我们先来看下@SpringBootApplication到底是啥

    @SpringBootApplication注解

    在这里插入图片描述

    我们点进去后,不难发现 @SpringBootApplication注解是依赖上图框框里面的几个注解,那我们再来看下框框里面的几个注解是干啥的。

    • @Target

    @Target表明注释的作用目标

    @Target(ElementType.TYPE)——接口、类、枚举、注解
    @Target(ElementType.FIELD)——字段、枚举的常量
    @Target(ElementType.METHOD)——方法
    @Target(ElementType.PARAMETER)——方法参数
    @Target(ElementType.CONSTRUCTOR) ——构造函数
    @Target(ElementType.LOCAL_VARIABLE)——局部变量
    @Target(ElementType.ANNOTATION_TYPE)——注解
    @Target(ElementType.PACKAGE)——包
    
    
    • @Retention

    注解的保留位置

    @Retention(RetentionPolicy.Resource):这种类型的Annotations只在源代码级别保留,编译时就会被忽略,class字节码文件中不包含。
    @Retention(RetentionPolicy.CLASS):这种类型的Annotations编译时被保留,默认的保留策略,class文件中存在,但JVM将会忽略,运行时无法获得。
    @Retention(RetentionPolicy.RUNTIME):这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
    
    
    • @Documented

    说明该注解将被包含在javadoc中

    • @Inherited

    说明子类可以继承父类的该注解

    • @SpringBootConfiguration

    @SpringBootConfiguration是继承与@Configuration,表明当前类是一个配置类,通过@Configuration与@Bean结合,将Bean注册到spring的IOC容器中。

    • @EnableAutoConfiguration

    开启自动配置

    • @ComponentScan

    根据定义的扫描路径,把符合扫描规则的类装配到spring容器中

    由上面的注解可知,此类是可以扫描到配置文件加载配置文件,那么,我们就继续往下看SpringApplication.run(SpringBootStartApplication.class, args);

    SpringApplication构造方法

    通过构造方法,我们可以发现SpringApplication可以设置一下的属性

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.sources = new LinkedHashSet();
            this.bannerMode = Mode.CONSOLE;
            this.logStartupInfo = true;
            this.addCommandLineProperties = true;
            this.addConversionService = true;
            this.headless = true;
            this.registerShutdownHook = true;
            this.additionalProfiles = new HashSet();
            this.isCustomEnvironment = false;
            this.lazyInitialization = false;
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
            this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = this.deduceMainApplicationClass();
        }
    
    

    deduceWebEnvironment方法

    在这里插入图片描述

    deduceWebEnvironment方法是用来判断当前应用的环境,该方法通过获取这两个类来判断当前环境是否是web环境,如果能获得这两个类说明是web环境,否则不是。

    getSpringFactoriesInstances()方法

    getSpringFactoriesInstances方法主要用来从spring.factories文件中找出key为ApplicationContextInitializer的类并实例化,然后调用setInitializers方法设置到SpringApplication的initializers属性中。这个过程就是找出所有的应用程序初始化器。

    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    
    
    
    
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
    
            try {
               //从spring.factories文件中找出key为ApplicationContextInitializer的类
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                ArrayList result = new ArrayList();
    
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                    String factoryClassNames = properties.getProperty(factoryClassName);
                    result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
                }
    
                return result;
            } catch (IOException var8) {
                throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
            }
        }
    
    

    设置监听器

    同理调用getSpringFactoriesInstances从spring.factories文件中找出key为ApplicationListener的类并实例化,然后调用setListeners方法设置到SpringApplication的listeners属性中

    在这里插入图片描述

    调用main,开始运行

    调用deduceMainApplicationClass方法找出main类,就是这里的ApplicationMain类。
    在这里插入图片描述

    上面的操作顺利后,main方法里面的东西就能运行了,然后我们看run()方法

    run()方法

    
    public ConfigurableApplicationContext run(String... args) {
            //计时器,统计任务的执行时间
            StopWatch stopWatch = new StopWatch();
            //开始执行
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            FailureAnalyzers analyzers = null;
            this.configureHeadlessProperty();
            // 获取SpringApplicationRunListeners启动事件监听器,这里只有一个EventPublishingRunListener
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            // 封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的listeners所监听
            listeners.starting();
    
            try {
                // 构造一个应用程序参数持有类
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 准备并配置环境
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
                 // 打印banner图形
                Banner printedBanner = this.printBanner(environment);
                // 创建Spring容器
                context = this.createApplicationContext();
                new FailureAnalyzers(context);
                // 配置Spring容器
                this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 容器上下文刷新
                this.refreshContext(context);
                // 容器创建完成之后调用afterRefresh方法
                this.afterRefresh(context, applicationArguments);
                // 调用监听器,广播Spring启动结束的事件
                listeners.finished(context, (Throwable)null);
                // 停止计时器
                stopWatch.stop();
                if (this.logStartupInfo) {
                    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
                }
    
                return context;
            } catch (Throwable var9) {
                this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
                throw new IllegalStateException(var9);
            }
        }
    
    
    

    SpringApplicationRunListeners

    获取启动事件监听器,同样的通过调用getSpringFactoriesInstances方法去META-INF/spring.factories文件中拿到SpringApplicationRunListener监听器,当前的SpringApplicationRunListener事件监听器只有一个EventPublishingRunListener广播事件监听器:

    启动事件监听器

    通过listeners.starting()可以启动事件监听器SpringApplicationRunListener ,SpringApplicationRunListener 是一个启动事件监听器接口

    public interface SpringApplicationRunListener {
        void starting();
    
        void environmentPrepared(ConfigurableEnvironment var1);
    
        void contextPrepared(ConfigurableApplicationContext var1);
    
        void contextLoaded(ConfigurableApplicationContext var1);
    
        void finished(ConfigurableApplicationContext var1, Throwable var2);
    }
    
    public void starting() {
            this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
        }
    

    准备启动环境

    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 创建应用程序的环境信息。如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 配置环境信息。比如profile,命令行参数
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
        listeners.environmentPrepared(environment);
        // 环境信息的校对
        if (!this.webEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        return environment;
    }
    
    
    判断启动的项目类型

    判断环境,如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment。

    
    private ConfigurableEnvironment getOrCreateEnvironment() {
            if (this.environment != null) {
                return this.environment;
            } else {
                return (ConfigurableEnvironment)(this.webEnvironment ? new StandardServletEnvironment() : new StandardEnvironment());
            }
        }
    
    

    创建Spring容器上下文

    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                // 判断是否是web应用,
                // 如果是则创建AnnotationConfigEmbeddedWebApplicationContext,否则创建AnnotationConfigApplicationContext
                contextClass = Class.forName(this.webEnvironment
                        ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }
    
    

    配置Spring容器上下文

    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        // 设置Spring容器上下文的环境信息
        context.setEnvironment(environment);
        // Spring容器创建之后做一些额外的事
        postProcessApplicationContext(context);
        // SpringApplication的初始化器开始工作
        applyInitializers(context);
        // 遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
    
        // 把应用程序参数持有类注册到Spring容器中,并且是一个单例
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }
    
        // 加载sources,sources是main方法所在的类
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 将sources加载到应用上下文中。最终调用的是AnnotatedBeanDefinitionReader.registerBean方法
        load(context, sources.toArray(new Object[sources.size()]));
        // 广播出ApplicationPreparedEvent事件给相应的监听器执行
        // 执行EventPublishingRunListener.contextLoaded方法
        listeners.contextLoaded(context);
    }
    
    

    Spring容器创建之后回调方法postProcessApplicationContext

    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        // 如果SpringApplication设置了实例命名生成器,则注册到Spring容器中
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton(
                    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        // 如果SpringApplication设置了资源加载器,设置到Spring容器中
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context)
                        .setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context)
                        .setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
    }
    
    

    初始化器开始工作

    
    protected void applyInitializers(ConfigurableApplicationContext context) {
        // 遍历每个初始化器,调用对应的initialize方法
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                    initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
    
    

    Spring容器创建完成之后会调用afterRefresh方法

    ApplicationRunner、CommandLineRunner类都是在在afterRefresh方法中调用的,也就是说在Spring容器创建之后执行的。

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
        callRunners(context, args);
    }
    
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        // 找出Spring容器中ApplicationRunner接口的实现类
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        // 找出Spring容器中CommandLineRunner接口的实现类
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        // 对runners进行排序
        AnnotationAwareOrderComparator.sort(runners);
        // 遍历runners依次执行
        for (Object runner : new LinkedHashSet<Object>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    
    

    相关资料参考地址

    1. @traget注解的作用
    2. 浅谈SpringBoot源码 — @SpringBootConfiguration的作用
    3. springboot@EnableAutoConfiguration 注解的作用以及加载流程
    4. @ComponentScan 详解
    5. SpringBoot启动过程
    6. Spring Boot的扩展机制之Spring Factories
    7. 备用参考地址

    贡献最大的

    https://blog.wangqi.love/articles/Spring/SpringBoot%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B.html

    展开全文
  • Springboot启动流程

    2020-09-02 17:51:34
    Springboot启动流程开始目录构造ConfigurableApplicationContext 对象实例推断当前应用类型 现在大部分公司都在使用springboot开方项目,由于springboot的开发步骤简单方便,大部人都会使用,但是对于springboot的...

    Springboot启动流程


    现在大部分公司都在使用springboot开方项目,由于springboot的开发步骤简单方便,大部人都会使用,但是对于springboot的启动流程大部分初级开发人员并没有了解多少,下面就会以这篇文章稍微细致的分析下springboot的启动流程。

    开始

    下面一个非常简单常用的例子开始:

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

    为了不让文章显得没有头绪而且篇幅过长,下面会分功能点进行讲解。

    目录

    1. 构造SpringApplication实例对象
    2. SpringApplication run方法核心逻辑
    3. 创建Environment
    展开全文
  • SpringBoot启动流程

    2021-01-31 15:48:20
    1 SpringBoot启动流程 该图描述了SpringBoot启动过程中所需要的大致流程,链接地址:https://upload-images.jianshu.io/upload_images/6912735-51aa162747fcdc3d.png?imageMogr2/auto-orient/strip 1.1 ...

    1 SpringBoot启动流程 返回目录

    (本文基于SpringBoot 2.1.x分析。)

    该图描述了SpringBoot启动过程中所需要的大致流程,链接地址:https://upload-images.jianshu.io/upload_images/6912735-51aa162747fcdc3d.png?imageMogr2/auto-orient/strip
    在这里插入图片描述

    1.1 SpringApplication构造方法初始化

    1.1.1 SpringBoot应用类型推断

    SpringBoot2.0 支持响应式编程,因此为了能够区分不同的应用,SpringBoot通过枚举WebApplicationType来区分应用类型,便于在启动Spring时选用合适的ConfigurableApplicationContext实现。

    1. REACTIVE: 类路径存在org.springframework.web.reactive.DispatcherHandler但不存在servlet.DispatcherServletorg.glassfish.jersey.servlet.ServletContainer
    2. SERVLET: 当javax.servlet.ServletConfigurableWebApplicationContext同时在则认为时web应用类型
    3. NONE: 表示普通应用类型

    1.1.2 从spring.factories文件加载初始化器和监听器

    Spring通过工具类SpringFactoriesLoader从classpath扫描所有jar包中的META-INF/spring.factories配置文件,读取spring.factoies中的配置类信息,保存在MultiValueMap<String, String>中,key为接口全类名,value为实现类全类名.一个key可以对应多个实现类.该工具类主要有两个方法:

    1. List<T> loadFactories(factoryClass, classLoader)返回排序后的对象集合
    2. List<String> loadFactoryNames(factoryClass, classLoader) 在SpringApplication构造方法中会分别读取org.springframework.context.ApplicationContextInitializerorg.springframework.context.ApplicationListener两个接口实现类对象集合。 ApplicationContextInitializer用于在上下文ApplicationContext创建后回调。而ApplicationListener则用于监听SpringBoot在整个启动过程中监听相应的事件.

    默认情况下ApplicationContextInitializer实现类有如下:

    1. org...boot.context.config.DelegatingApplicationContextInitializer
      用于解析通过属性context.initializer.classes指定的ApplicationContextInitializer实现类,并调用其实现的接口方法
    1. org...boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
      通过applicationContext.addBeanFactoryPostProcessor(…)注册CachingMetadataReaderFactoryPostProcessorBeanFactoryPostProcessor中文翻译后置处理器,这是Spring生命周期中最重要的回调接口,工厂模式获取MetadataReader用于读取class文件信息.相关类: ClassMetadata, SimpleMetadataReader
    2. org...boot.context.ContextIdApplicationContextInitializer
      读取spring.application.name属性值,默认为application,做为ConfigurableApplicationContextid
    3. org...boot.context.ConfigurationWarningsApplicationContextInitializer
      用于验证设置的包名是否包含org.springframeworkorg,如果包含则输出警告日志,不过只是验证了@ComponentScan注解(包含@SpringBootApplication继承的),但是没有验证@ComponentScans注解
    4. org...boot.web.context.ServerPortInfoApplicationContextInitializer
      用于web环境下读取设置服务端端口
    5. org...boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
      当Spring中条件注解验证不通过时打印日志,仅当debug级别打印,SpringBoot默认为info级别.

    ApplicationListener实现类如下:

    1. org...boot.context.config.ConfigFileApplicationListener
      用于读取application.yml, application.properties配置文件,并解析spring.profiles.active指定的配置文件.
    2. org...boot.context.config.AnsiOutputApplicationListener
      用于控制是否在控制台打印彩色日志
    3. org...boot.context.logging.LoggingApplicationListener
      用于日志初始化,在LoggingApplicationListener监听的ApplicationEnvironmentPreparedEvent事件执行之前日志不可用
    4. org...boot.context.logging.ClasspathLoggingApplicationListener
      打印当前项目启动的类路径信息,所加载的jar信息
    5. org.springframework.boot.autoconfigure.BackgroundPreinitializer
      SpringBoot启动过程唯一用到多线程的地方,用于触发一些类静态块的执行,减少启动耗时
    6. org...boot.context.config.DelegatingApplicationListener
      DelegatingApplicationListener监听到ApplicationEnvironmentPreparedEvent事件时,解析属性context.listener.classes配置的监听器并发布事件,因此context.listener.classes配置的事件监听器只能监听 ApplicationEnvironmentPreparedEvent以及之后的事件,无法监听ApplicationStartedEvent事件。
    7. org...boot.ClearCachesApplicationListener
      监听ContextRefreshedEvent事件,清除ClassLoader缓存
    8. org...boot.context.FileEncodingApplicationListener
      验证开发者配置的属性spring.mandatory-file-encoding值和System.getProperty("file.encoding")获取的值是否一致,如果不一致则打印错误日志,如果没有设置则不验证.

    1.1.3 解析main方法所在类

    通过当前堆栈信息,获取当前运行方法名为main的所在类. 解析main方法所在类主要目的就是为了输出日志信息使用,并不是为了解析启动类. 启动类是通过SpringApplication.run(…)方法的参数传递. 也就是说SpringBoot中启动类并不要求就是main()方法所在类.

    1.1.4 SpringBoot中组件排序规则

    SpringBoot组件的排序大多是使用
    org.springframework.core.annotation.AnnotationAwareOrderComparator实现.主要分为如下几种情况;

    1. 实现PriorityOrdered接口的组件单独排序比较,数值越小优先级越高
    2. 未实现PriorityOrdered接口的组件,会按照Ordered,@Order,@ javax.annotation.Priority顺序查找排序值进行排序,数值越小优先级越高
    3. 如果一个实现PriorityOrdered接口,一个实现Ordered接口,那么无论数值大小,始终是实现PriorityOrdered接口的组件优先级最高.

    1.2 springApplication.run(String… arg)方法执行

    SpringBoot启动的整个流程主要是在该方法中完成.主要包含:

    1. ConfigurableEnvironment实现类类型的推断与创建,不同类型配置属性的添加,例如: 程序参数, 系统环境变量, application.properties中的配置属性都将分别做为PropertySource保存在ConfigurableEnvironment中.
    2. ConfigurableApplicationContext的类型推断与创建,和其一些基本属性的添加操作,例如向上下文中添加Environment实例,添加数据类型转换其等.
    3. 控制台SpringBoot logo的打印
    4. SpringBoot启动流程不同阶段的事件发布等.

    1.2.1 StopWatch start 开启应用启动计时器

    StopWatch该类主要用于统计SpringBoot在整个启动过程中所耗费时间

    1.2.2 配置java.awt.headless模式

    该属性值默认被设置为true, 用于在缺失显示屏、鼠标或者键盘时的系统配置。对于后端服务来讲,很多都是需要将这个属性设置为true的。

    1.2.3 创建SpringApplicationRunListeners

    通过SpringFactoriesLoaderspring.factories中加载org.springframework.boot.SpringApplicationRunListener实现类并封装在SpringApplicationRunListeners对象的成员变量listeners集合中,默认实现只有一个org.springframework.boot.context.event.EventPublishingRunListener,主要用于在SpringBoot启动过程发布不同阶段产生的事件,这是一个典型的监听者模式,SpringApplicationRunListener接口中定义的方法即对应着不同的事件类型。如下:
    starting() 对应 ApplicationStartingEvent
    environmentPrepared(environment) 对应 ApplicationEnvironmentPreparedEvent
    contextPrepared(context) 对应 ApplicationContextInitializedEvent (SpringBoot2.1 中新增)
    contextLoaded(context) 对应 ApplicationPreparedEvent
    started(context) 对应 ApplicationStartedEvent
    running(context) 对应 ApplicationReadyEvent
    failed(context, throwable) 对应 ApplicationFailedEvent SpringBoot启动过程中出现异常都会发布该事件

    1.2.4 listeners.starting()发布ApplicationStartingEvent

    • LoggingApplicationListener 监听ApplicationStartingEvent事件,创建日志对象,但是未设置日志配置信息,此时日志不可用
    • BackgroundPreinitializer 监听ApplicationStartingEvent事件,创建线程异步执行一些类的静态初始化,例如DefaultFormattingConversionService中的sattic代码块

    1.2.5 创建ConfigurableEnvironment对象并添加属性值

    知识点: ConfigurableEnvironment继承Environment, Environment继承PropertyResolver,主要提供了对属性获取方法, AbstractEnvironment做为抽象类实现了ConfigurableEnvironment接口方法,其内部是通过List<PropertySource<?>>集合来保存不同类型的属性资源。
    例如: 通过System.getProperties()得到的系统属性就是一种类型的PropertySource,通过application.yml配置的属性是另一种属性资源. 当调用env.getProperty()获取属性值时,会遍历PropertySource集合,只要有一个PropertySource中有对应属性值则不再继续遍历查找,所以在集合中越靠前的属性优先级越高.
    在这里插入图片描述

    1. 根据应用类型推断创建ConfigurableEnvironment实例,非web环境使用StandardEnvironment,将应用程序参数封装到StandardEnvironment

    2. 发布ApplicationEnvironmentPreparedEvent事件,事件的发布是同步的,主要有两个重要的监听器监听该事件。

      1. ConfigFileApplicationListener实现了Order接口,设置的优先级最高,因此最先执行。
        通过读取spring.factoriesEnvironmentPostProcessor接口实现类,遍历调用接口方法,ConfigFileApplicationListener同时实现了接口EnvironmentPostProcessor,用于读取application.ymlapplication.properties等配置文件中的信息封装在Environment中。
        相关类:

            org.springframework.boot.env.EnvironmentPostProcessor
            org.springframework.boot.env.PropertySourceLoader
            org.springframework.boot.env.PropertiesPropertySourceLoader
            org.springframework.boot.env.YamlPropertySourceLoader
        

        配置中心接入的实现可以通过接口EnvironmentPostProcessorPropertySourceLoader实现

      2. DelegatingApplicationListener 从Environment中读取属性context.listener.classes指定的监听器,实例化之后排序,将其添加到成员变量事件广播器multicaster,并发布ApplicationEnvironmentPreparedEvent事件

    3. 调用ConfigurationPropertySources.attach(environment)将所有的PropertySource又包装成PropertySource放在environment。
      这里有个问题,是否会造成死循环 ? 不会,springBoot重写了Iterable.Iterable() 方法,看源码分析.
      但是SpringBoot使用attach方法导致的一个缺陷: 如果要获取的属性值不存在,则会造成多余的循环遍历. 看源码分析.

    4. environment中PropertySource的优先级如下:
      configurationProperties
      commandLineArgs
      systemProperties
      systemEnvironment
      random
      applicationConfig: [classpath:/application-dev.properties]
      applicationConfig: [classpath:/application.properties]

    1.2.6 创建Banner,打印SpringBoot logo

    源码见SpringApplication printBanner(environment)

    1.2.7 创建ConfigurableApplicationContext

    根据应用类型推断,创建相应的ConfigurableApplicationContext实现类对象,非web应用使用AnnotationConfigApplicationContext,通过反射创建其实例对象,而其构造函数中做了重要的工作.。即创建AnnotatedBeanDefinitionReader 对象,而AnnotatedBeanDefinitionReader在构造方法中通过AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);注册一系列后置处理器:

    1. 添加组件排序比较器,AnnotationAwareOrderComparator.INSTANCE
    2. 添加ContextAnnotationAutowireCandidateResolver处理@Lazy@Qualifier
    3. 注入ConfigurationClassPostProcessor BeanDefinition
      该类实现BeanDefinitionRegistryPostProcessor接口有两个非常重要的作用,
      • postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法中对classpath下类文件扫描
      • postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法中解析@Configuration注解和@Bean方法,对@Configuration进行cglib增强,防止同类中调用this方法创建多个不同的对象. 不能增强BeanDefinitionRegistryPostProcessor实现类.
    4. 注入AutowiredAnnotationBeanPostProcessor BeanDefinition, 用于针对 @value@Autowired标记的字段赋值等.
    5. 注入CommonAnnotationBeanPostProcessor BeanDefinition,用于处理@PostConstruct@PreDestroy注解
    6. 注入EventListenerMethodProcessor BeanDefinition其通过实现SmartInitializingSingleton接口,实现对方法@EventListener方法的收集,
    7. 注入DefaultEventListenerFactory BeanDefinition,使用适配器模式,将@EventListener标记的方法转为监听器

    1.2.8 从spring.factories加载SpringBootExceptionReporter

    从spring.factories中加载SpringBootExceptionReporter接口实现类,主要用在SpringBoot启动过程回调异常信息给开发者处理.

    1.2.9 prepareContext为ApplicationContext执行refresh做准备

    1. environment添加到ConfigurableApplicationContext
    2. ConfigurableBeanFactory添加数据类型转换器ApplicationConversionService
    3. 回调ApplicationContextInitializer接口方法,传入ConfigurableApplicationContext参数
    4. 发布ApplicationContextInitializedEvent事件
    5. 通过beanFactory.registerSingleton(…)applicationArguments添加到Spirng容器
    6. 将启动类注册为Spring BeanDefinition
    7. 发布ApplicationPreparedEvent事件,在EventPublishingRunListener发布事件之前会遍历所有的事件监听器,如果有监听器实现了ApplicationContextAware接口,则回调接口方法.并将SpringBootspring.factories中加载的监听器添加到ConfigurableApplicationContext中,之后的事件发布则是通过applicationContext中的事件广播器发布.

    1.2.10 refreshContext(context) 开始spring容器的生命周期

    该过程主要工作:

    1. 设置beanFactory的类加载器, SpringEL表达解析器, Aware接口的后置处理器,
    2. 重点是执行BeanFactoryPostProcessors,中文翻译后置处理器,详情见Spring容器的生命周期
    3. 注册BeanPostProcessors,BeanPostProcessor的使用见Spring中Bean的生命周期
    4. 注册事件广播器,SimpleApplicationEventMulticaster
    5. 添加事件监听器到广播器, 解析实现ApplicationListener接口的Bean名称保存在广播器
    6. 通过getBean()完成所有非懒加载的bean的初始化,详情见Spring中Bean的生命周期

    1.2.11 stopWatch.stop() 停止应用启动计时

    控制台打印应用启动时间

    1.2.12 listeners发布ApplicationStartedEvent事件

    发布该事件时整个应用服务基本可用,但是优先级高于ApplicationRunner和CommandLineRunner

    1.2.13 callRunners调用Runner回调接口方法

    org.springframework.boot.ApplicationRunner
    org.springframework.boot.CommandLineRunner
    这两个Runner没有继承关系,但是作用相同,都能够在应用启动后执行回调,并且可以得到应用启动时传入的参数,两者唯一区别就是方法参数类型不同

    1.2.14 listeners发布ApplicationReadyEvent事件

    自此整个SpringBoot应用启动完成

    展开全文
  • SpringBoot 启动流程

    2020-12-01 14:08:57
    1、编写一个SpringBoot启动类 运行一个SpringBoot项目,引入相关Starters和相关依赖后,再编写一个启动类,然后在这个启动类标上@SpringBootApplication注解,然后就可以启动运行项目了,我们在MyApplication启动类...

    1、编写一个SpringBoot启动类

    运行一个SpringBoot项目,引入相关Starters和相关依赖后,再编写一个启动类,然后在这个启动类标上@SpringBootApplication注解,就可以启动运行项目了。我们在MyApplication启动类上标注了@SpringBootApplication注解,然后在main函数中调用SpringApplication.run(MyApplication.class, args),这句代码就完成了SpringBoot的启动流程,非常简单。

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

    2、@SpringBootApplication

    我们来分析下标注在启动类上的@SpringBootApplication注解,@SpringBootApplication注解是一个组合注解,主要由@SpringBootConfiguration、@EnableAutoConfiguration@ComponentScan这三个注解组合而成。因此@SpringBootApplication注解主要作为一个配置类,能够触发包扫描和自动配置的逻辑,从而使得SpringBoot的相关bean被注册进Spring容器。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { // 两个排除过滤器TypeExcludeFilter和AutoConfigurationExcludeFilter
    		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
        // 等同于EnableAutoConfiguration注解的exclude属性
    	@AliasFor(annotation = EnableAutoConfiguration.class)
    	Class<?>[] exclude() default {};
         // 等同于EnableAutoConfiguration注解的excludeName属性
    	@AliasFor(annotation = EnableAutoConfiguration.class)
    	String[] excludeName() default {};
        // 等同于ComponentScan注解的basePackages属性
    	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    	String[] scanBasePackages() default {};
        // 等同于ComponentScan注解的basePackageClasses属性
    	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    	Class<?>[] scanBasePackageClasses() default {};
    }

    3、SpringBoot的启动流程

    main函数里的SpringApplication.run(MainApplication.class, args);这句代码中SpringApplication类是干嘛的呢?SpringApplication类是用来启动SpringBoot项目的,可以在java的main方法中启动,目前我们知道这些就足够了。下面看下SpringApplication.run(MainApplication.class, args);这句代码的源码:

    // run方法是一个静态方法,用于启动SpringBoot
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    	// 继续调用静态的run方法
    	return run(new Class<?>[] { primarySource }, args);
    }

    在上面的静态run方法里又继续调用另一个静态run方法:

    // run方法是一个静态方法,用于启动SpringBoot
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    	// 构建一个SpringApplication对象,并调用其run方法来启动
    	return new SpringApplication(primarySources).run(args);
    }

    如上代码,可以看到构建了一个SpringApplication对象,然后再调用其run方法来启动SpringBoot项目。关于SpringApplication对象是如何构建的,我们后面再分析,现在直接来看下启动流程的源码:

    public ConfigurableApplicationContext run(String... args) {
    	// 创建一个StopWatch用于统计启动过程花了多少时间
    	StopWatch stopWatch = new StopWatch();
    	stopWatch.start();
    	ConfigurableApplicationContext context = null;
    	// exceptionReporters集合用来存储异常报告器,用来报告SpringBoot启动过程的异常
    	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    	// 配置headless属性,即java.awt.headless属性,默认为ture
    	// 其实是想设置该应用程序,即使没有检测到显示器,也允许其启动.对于服务器来说,是不需要显示器的,所以要这样设置。
    	configureHeadlessProperty();
    	// 【1】从spring.factories配置文件中加载到EventPublishingRunListener对象并赋值给SpringApplicationRunListeners
    	// EventPublishingRunListener对象主要用来发射SpringBoot启动过程中内置的一些生命周期事件,标志每个不同启动阶段
    	SpringApplicationRunListeners listeners = getRunListeners(args);
    	// 启动SpringApplicationRunListener的监听,表示SpringApplication开始启动。
    	// 发布【ApplicationStartingEvent】事件
    	listeners.starting();
    	try {
    		// 创建ApplicationArguments对象,封装了args参数
    		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    		// 【2】准备环境变量,包括系统变量,环境变量,命令行参数,默认变量,servlet相关配置变量,随机值,
    		// JNDI属性值,以及配置文件(比如application.properties)等,注意这些环境变量是有优先级的
    		// 发布【ApplicationEnvironmentPreparedEvent】事件
    		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    		// 配置spring.beaninfo.ignore属性,默认为true,即跳过搜索BeanInfo classes。
    		configureIgnoreBeanInfo(environment);
    		// 【3】控制台打印SpringBoot的bannner标志
    		Banner printedBanner = printBanner(environment);
    		// 【4】根据不同类型创建不同类型的spring applicationcontext容器
    		//一般是servlet环境,创建的是AnnotationConfigServletWebServerApplicationContext容器对象
    		context = createApplicationContext();
    		// 【5】从spring.factories配置文件中加载异常报告期实例,这里加载的是FailureAnalyzers
    		// 注意FailureAnalyzers的构造器要传入ConfigurableApplicationContext,因为要从context中获取beanFactory和environment
    		// ConfigurableApplicationContext是AnnotationConfigServletWebServerApplicationContext的父接口
    		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
    		// 【6】为刚创建的AnnotationConfigServletWebServerApplicationContext容器对象做一些初始化工作,准备一些容器属性值等
    		// 1)为AnnotationConfigServletWebServerApplicationContext的属性AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner设置environgment属性
    		// 2)根据情况对ApplicationContext应用一些相关的后置处理,比如设置resourceLoader属性等
    		// 3)在容器刷新前调用各个ApplicationContextInitializer的初始化方法,ApplicationContextInitializer是在构建SpringApplication对象时从spring.factories中加载的
    		// 4)发布【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
    		// 5)从context容器中获取beanFactory,并向beanFactory中注册一些单例bean,比如applicationArguments,printedBanner
    		// 6)加载bean到application context,注意这里只是加载了部分bean比如mainApplication这个bean,大部分bean如单例bean应该是在AbstractApplicationContext.refresh方法中被加载
    		// 7)发布【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
    		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    		// 【7】刷新容器,这一步至关重要,以后会在分析Spring源码时详细分析,主要做了以下工作:
    		// 1)在context刷新前做一些准备工作,比如初始化一些属性设置,属性合法性校验和保存容器中的一些早期事件等;
    		// 2)让子类刷新其内部bean factory,注意SpringBoot和Spring启动的情况执行逻辑不一样
    		// 3)对bean factory进行配置,比如配置bean factory的类加载器,后置处理器等
    		// 4)完成bean factory的准备工作后,此时执行一些后置处理逻辑,子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
    		// 在这一步,所有的bean definitions将会被加载,但此时bean还不会被实例化
    		// 5)执行BeanFactoryPostProcessor的方法即调用bean factory的后置处理器:
    		// BeanDefinitionRegistryPostProcessor(触发时机:bean定义注册之前)和BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)
    		// 6)注册bean的后置处理器BeanPostProcessor,注意不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的
    		// 7)初始化国际化MessageSource相关的组件,比如消息绑定,消息解析等
    		// 8)初始化事件广播器,如果bean factory没有包含事件广播器,那么new一个SimpleApplicationEventMulticaster广播器对象并注册到bean factory中
    		// 9)AbstractApplicationContext定义了一个模板方法onRefresh,留给子类覆写,比如ServletWebServerApplicationContext覆写了该方法来创建内嵌的tomcat容器
    		// 10)注册实现了ApplicationListener接口的监听器,之前已经有了事件广播器,此时就可以派发一些early application events
    		// 11)完成容器bean factory的初始化,并初始化所有剩余的单例bean。这一步非常重要,一些bean postprocessor会在这里调用。
    		// 12)完成容器的刷新工作,并且调用生命周期处理器的onRefresh()方法,并且发布ContextRefreshedEvent事件。
    		refreshContext(context);
    		// 【8】执行刷新容器后的后置处理逻辑,注意这里为空方法
    		afterRefresh(context, applicationArguments);
    		// 停止stopWatch计时
    		stopWatch.stop();
    		// 打印日志
    		if (this.logStartupInfo) {
    			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    		}
    		// 发布【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
    		listeners.started(context);
    		// 【9】调用ApplicationRunner和CommandLineRunner的run方法,实现spring容器启动后需要做的一些东西比如加载一些业务数据等
    		callRunners(context, applicationArguments);
    	}
    	// 【10】若启动过程中抛出异常,此时用FailureAnalyzers来报告异常
    	// 发布【ApplicationFailedEvent】事件,标志SpringBoot启动失败
    	catch (Throwable ex) {
    		handleRunFailure(context, ex, exceptionReporters, listeners);
    		throw new IllegalStateException(ex);
    	}
    
    	try {
    		// 发布【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
    		listeners.running(context);
    	}
    	// 若出现异常,此时仅仅报告异常,而不会发射任何事件
    	catch (Throwable ex) {
    		handleRunFailure(context, ex, exceptionReporters, null);
    		throw new IllegalStateException(ex);
    	}
    	// 【11】最终返回容器
    	return context;
    }

    如上代码就是SpringBoot的启动流程了,其中注释也非常详细,主要步骤也已经标注【x】,现将主要步骤总结如下:

    1. spring.factories配置文件中加载EventPublishingRunListener对象,该对象拥有SimpleApplicationEventMulticaster属性,即在SpringBoot启动过程的不同阶段用来发射内置的生命周期事件;
    2. 准备环境变量,包括系统变量,环境变量,命令行参数,默认变量,servlet相关配置变量,随机值以及配置文件(比如application.properties)等;
    3. 控制台打印SpringBoot的bannner标志
    4. 根据不同类型环境创建不同类型的applicationcontext容器,一般是servlet环境,创建的是AnnotationConfigServletWebServerApplicationContext容器对象;
    5. spring.factories配置文件中加载FailureAnalyzers对象,用来报告SpringBoot启动过程中的异常;
    6. 为刚创建的容器对象做一些初始化工作,准备一些容器属性值等,对ApplicationContext应用一些相关的后置处理和调用各个ApplicationContextInitializer的初始化方法来执行一些初始化逻辑等;
    7. 刷新容器,这一步至关重要。比如调用bean factory的后置处理器,注册BeanPostProcessor后置处理器,初始化事件广播器且广播事件,初始化剩下的单例bean和SpringBoot创建内嵌的Tomcat服务器等等重要且复杂的逻辑都在这里实现,主要步骤可见代码的注释,关于这里的逻辑会在以后的spring源码分析专题详细分析;
    8. 执行刷新容器后的后置处理逻辑,注意这里为空方法;
    9. 调用ApplicationRunnerCommandLineRunner的run方法,我们实现这两个接口可以在spring容器启动后需要的一些东西比如加载一些业务数据等;
    10. 报告启动异常,即若启动过程中抛出异常,此时用FailureAnalyzers来报告异常;
    11. 最终返回容器对象,这里调用方法没有声明对象来接收。

    当然在SpringBoot启动过程中,每个不同的启动阶段会分别发射不同的内置生命周期事件,比如在准备environment前会发射ApplicationStartingEvent事件,在environment准备好后会发射ApplicationEnvironmentPreparedEvent事件,在刷新容器前会发射ApplicationPreparedEvent事件等,总之SpringBoot总共内置了7个生命周期事件,除了标志SpringBoot的不同启动阶段外,同时一些监听器也会监听相应的生命周期事件从而执行一些启动初始化逻辑。

    展开全文
  • SpringBoot启动流程

    2021-01-08 14:26:52
    SpringBoot启动流程
  • springBoot启动流程

    2020-09-27 09:57:59
    1.前言 出差闲的蛋疼,遂学习一波。一边看源码,一边做...2.springBoot启动时序概览: 2.1时序图 2.2注释 3.springBoot准备上下文: 3.1时序图 3.2注释 4.springBoot刷新上下文: 4.1时序图: ...
  • springboot 启动流程

    2020-04-30 16:19:40
    springboot 启动源码解析 注: 以下源码是基于: 2.2.6.RELEASE 版本做的摘录分析 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starters</...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,799
精华内容 1,119
关键字:

springboot启动流程

spring 订阅