zoukankan      html  css  js  c++  java
  • 【SpringBoot】SpringBoot 启动原理(十五)

      此系列前面章节介绍的SpringBoot的使用,本章结束SpringBoot启动原理

      通过搭建一个SpringBoot Web工程,然后采用Debug模式运行程序,一步一步参考程序究竟做了哪些任务

      本篇文章所用到的 Spring Boot版本是 2.1.8.RELEASE

    SpringApplication 准备阶段

      1、搭建SpringBoot Web工程,参考【SpringBoot】SpringBoot Web开发(八)

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0"
     3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     5     <modelVersion>4.0.0</modelVersion>
     6 
     7     <groupId>com.test</groupId>
     8     <artifactId>test-springboot-web2</artifactId>
     9     <version>1.0-SNAPSHOT</version>
    10 
    11     <parent>
    12         <groupId>org.springframework.boot</groupId>
    13         <artifactId>spring-boot-starter-parent</artifactId>
    14         <version>2.1.8.RELEASE</version>
    15     </parent>
    16 
    17     <properties>
    18 
    19         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    20         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    21         <java.version>1.8</java.version>
    22     </properties>
    23 
    24     <dependencies>
    25 
    26         <dependency>
    27             <groupId>org.springframework.boot</groupId>
    28             <artifactId>spring-boot-starter-web</artifactId>
    29         </dependency>
    30 
    31     </dependencies>
    32 
    33 
    34     <!-- SpringBoot打包插件,可以将代码打包成一个可执行的jar包 -->
    35     <build>
    36         <plugins>
    37             <plugin>
    38                 <groupId>org.springframework.boot</groupId>
    39                 <artifactId>spring-boot-maven-plugin</artifactId>
    40             </plugin>
    41         </plugins>
    42     </build>
    43 
    44 </project>
    View Code

      2、使用Debug模式运行程序、

     1 @SpringBootApplication
     2 public class Application {
     3 
     4     public static void main(String[] args){
     5         // 调用SpringApplication的静态run方法
     6         // 参数是主类 和 运行参数args
     7         SpringApplication.run(Application.class, args);
     8 
     9     }
    10 
    11 }

      3、查看SpringApplication的run方法,通过入参 primarySources 构造 SpringApplication 类,然后在调用 run 方法,其中,准备阶段的工作皆在 SpringApplication 的构造器中处理:

    1 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    2     return run(new Class<?>[] { primarySource }, args);
    3 }
    4 
    5 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    6     return new SpringApplication(primarySources).run(args);
    7 }

    SpringApplication构造方法

      查看SpringApplication的构造方法,构造流程如下:

     1 public class SpringApplication {
     2 
     3     ...
     4 
     5     private Set<Class<?>> primarySources;
     6 
     7     private Set<String> sources = new LinkedHashSet<>();
     8 
     9     private Class<?> mainApplicationClass;
    10 
    11     private WebApplicationType webApplicationType;
    12 
    13     private List<ApplicationContextInitializer<?>> initializers;
    14 
    15     private List<ApplicationListener<?>> listeners;
    16 
    17     public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    18 
    19         // resourceLoader 主要用来获取 Resource 及 ClassLoader。这里值为 null
    20         this.resourceLoader = resourceLoader;
    21 
    22         // 断言主要加载资源类不能为 null,否则报错
    23         Assert.notNull(primarySources, "PrimarySources must not be null");
    24 
    25         // primarySources是SpringApplication.run的参数,存放的是主配置类
    26         this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    27 
    28         // 进行Web应用的类型推断
    29         this.webApplicationType = WebApplicationType.deduceFromClasspath();
    30 
    31         // 加载应用上下文初始化器 initializer
    32         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    33         
    34         // 加载应用事件监听器 listener
    35         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    36         
    37         // 推断引导类,也就是找到入口类
    38         this.mainApplicationClass = deduceMainApplicationClass();
    39     }
    40 
    41     ...
    42 
    43 }

      SpringApplication构造方法中主要做了4件事

      1、推断 Web 应用类型

      SpringApplication允许指定应用的类型,大体上包括Web应用和非Web应用。从 Spring Boot 2.0开始,Web应用又可分为Servlet Web和Reactive Web。而在准备阶段,是通过检查当前ClassPath下某些Class是否存在,从而推导应用的类型。我们进入 WebApplicationType.deduceFromClasspath() 方法查看:

     1 public enum WebApplicationType {
     2 
     3     /**
     4      * 非 web 项目
     5      */
     6     NONE,
     7 
     8     /**
     9      * servlet web 项目
    10      */
    11     SERVLET,
    12 
    13     /**
    14      * reactive web 项目
    15      */
    16     REACTIVE;
    17 
    18     private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
    19             "org.springframework.web.context.ConfigurableWebApplicationContext" };
    20 
    21     private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";
    22 
    23     private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";
    24 
    25     private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    26 
    27     private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    28 
    29     private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    30 
    31     static WebApplicationType deduceFromClasspath() {
    32         // 根据是否存在某些类,判断是否是reactive web 项目
    33         if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
    34                 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
    35             return WebApplicationType.REACTIVE;
    36         }
    37         // 根据是否存在某些类,判断是否是非 web 项目
    38         for (String className : SERVLET_INDICATOR_CLASSES) {
    39             if (!ClassUtils.isPresent(className, null)) {
    40                 return WebApplicationType.NONE;
    41             }
    42         }
    43         // 默认为 servlet web 项目
    44         return WebApplicationType.SERVLET;
    45     }
    46 
    47     ...
    48 
    49 }

      可以看到,在方法中利用 ClassUtils.isPresent 进行判断, 当DispatcherHandler存在,而DispatcherServlet和ServletContainer不存在时,则当前应用推导为 Reactive web 类型;当 Servlet 和 ConfigurableWebApplicationContext 不存在时,当前应用为非 Web 类型;其他的则为 Servlet Web 类型。

      2、加载应用上下文初始化器 initializer

        a、进入加载Spring应用上下文初始器的过程  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)):

     1 public class SpringApplication {
     2 
     3     ...
     4 
     5     /**
     6      * 加载应用上下文初始化器 initializer
     7      */
     8     public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
     9         this.initializers = new ArrayList<>();
    10         this.initializers.addAll(initializers);
    11     }
    12 
    13     public void addInitializers(ApplicationContextInitializer<?>... initializers) {
    14         this.initializers.addAll(Arrays.asList(initializers));
    15     }
    16 
    17     /**
    18      * 通过 Spring 工厂加载机制获取 ApplicationContextInitializer 初始器  
    19      */
    20     private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    21         return getSpringFactoriesInstances(type, new Class<?>[] {});
    22     }
    23 
    24     private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    25         ClassLoader classLoader = getClassLoader();
    26         // Use names and ensure unique to protect against duplicates
    27         Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    28         List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    29         AnnotationAwareOrderComparator.sort(instances);
    30         return instances;
    31     }
    32 
    33     ...
    34     
    35 }
        b、可以看到,通过SpringFactoriesLoader.loadFactoryNames(type, classLoader),获取资源地址
     1 public final class SpringFactoriesLoader {
     2 
     3     /**
     4      * 默认工厂资源地址
     5      */
     6     public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
     7 
     8     private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
     9 
    10     private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
    11 
    12     ...
    13 
    14     public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    15         String factoryClassName = factoryClass.getName();
    16         return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    17     }
    18 
    19     private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    20         MultiValueMap<String, String> result = cache.get(classLoader);
    21         if (result != null) {
    22             return result;
    23         }
    24 
    25         try {
    26             // META-INF/spring.factories 资源中获取url
    27             Enumeration<URL> urls = (classLoader != null ?
    28                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    29                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    30             result = new LinkedMultiValueMap<>();
    31             while (urls.hasMoreElements()) {
    32                 URL url = urls.nextElement();
    33                 UrlResource resource = new UrlResource(url);
    34                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    35                 for (Map.Entry<?, ?> entry : properties.entrySet()) {
    36                     String factoryClassName = ((String) entry.getKey()).trim();
    37                     for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    38                         result.add(factoryClassName, factoryName.trim());
    39                     }
    40                 }
    41             }
    42             cache.put(classLoader, result);
    43             return result;
    44         }
    45         catch (IOException ex) {
    46             throw new IllegalArgumentException("Unable to load factories from location [" +
    47                     FACTORIES_RESOURCE_LOCATION + "]", ex);
    48         }
    49     }
    50 
    51     ...
    52 
    53 }

         c、可以看到,这里是通过 Spring 工厂加载机制 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法获取。该方法是从所有的 META-INF/spring.factories 资源中获取key为 ApplicationContextInitializer 的实现类集合,如下是 spring-boot-autoconfigure 包下的 spring.factories 文件:

    1 # Initializers
    2 org.springframework.context.ApplicationContextInitializer=
    3 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
    4 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

        d、这里获取的就是 SharedMetadataReaderFactoryContextInitializer 和 ConditionEvaluationReportLoggingListener 上下文初始化器,接下来通过 createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names) 方法初始化这些实现类:

     1 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
     2         ClassLoader classLoader, Object[] args, Set<String> names) {
     3     List<T> instances = new ArrayList<>(names.size());
     4     for (String name : names) {
     5         try {
     6             Class<?> instanceClass = ClassUtils.forName(name, classLoader);
     7             Assert.isAssignable(type, instanceClass);
     8             Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
     9             T instance = (T) BeanUtils.instantiateClass(constructor, args);
    10             instances.add(instance);
    11         }
    12         catch (Throwable ex) {
    13             throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
    14         }
    15     }
    16     return instances;
    17 }

        e、这里先通过 BeanUtils.instantiate 初始化这些类,然后将初始化的类保存至List进行返回,并进行排序操作,最后添加到SpringApplication的initializers集合变量中。至此,该流程结束。

        查看SharedMetadataReaderFactoryContextInitializer类

     1 class SharedMetadataReaderFactoryContextInitializer
     2         implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
     3 
     4     @Override
     5     public void initialize(ConfigurableApplicationContext applicationContext) {
     6         applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
     7     }
     8 
     9     ...
    10 
    11 }

        可以看到该类实现了 Spring 的 ApplicationContextInitializer 接口,并重写了initialize()方法。同理,其他的 Initializer 接口也是类似实现。 而在这里则是在上下文中加入了 CachingMetadataReaderFactoryPostProcessor bean工厂后置处理器。

        注:

    ApplicationContextInitializer 接口的主要作用是在 ConfigurableApplicationContext#refresh() 方法调用之前做一些初始化工作。

      3、加载应用事件监听器 listener

        a、接着加载应用事件监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)),过程与“加载应用上下文初始器”基本一致,同样是调用 getSpringFactoriesInstances 方法,不过这里获取的是 key 为 ApplicationListener 的对象集合,如下是 spring-boot-autoconfigure 包下的 spring.factories 文件::

    1 # Application Listeners
    2 org.springframework.context.ApplicationListener=
    3 org.springframework.boot.autoconfigure.BackgroundPreinitializer

        b、最后,将获取的 BackgroundPreinitializer 对象通过 setListeners 方法放入 listeners 属性变量中:

    1 public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    2     this.listeners = new ArrayList<>();
    3     this.listeners.addAll(listeners);
    4 }

        c、查看监听器中的内容,如BackgroundPreinitializer

     1 public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {
     2 
     3     @Override
     4     public void onApplicationEvent(SpringApplicationEvent event) {
     5         if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
     6                 && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
     7             performPreinitialization();
     8         }
     9         if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
    10                 && preinitializationStarted.get()) {
    11             try {
    12                 preinitializationComplete.await();
    13             }
    14             catch (InterruptedException ex) {
    15                 Thread.currentThread().interrupt();
    16             }
    17         }
    18     }
    19 
    20     ...
    21 
    22 }

         可以看到,该类实现了 Spring 的 ApplicationListener 接口,在重写的 onApplicationEvent 方法中触发相应的事件进行操作。同理,其他 Listener 也是类似实现。而该接口的主要功能是另起一个后台线程触发那些耗时的初始化,包括验证器、消息转换器等等。

        注:

    目前spring boot中支持的事件类型如下:
    
    ApplicationFailedEvent:该事件为spring boot启动失败时的操作
    ApplicationPreparedEvent:上下文context准备时触发
    ApplicationReadyEvent:上下文已经准备完毕的时候触发
    ApplicationStartedEvent:spring boot 启动监听类
    SpringApplicationEvent:获取SpringApplication
    ApplicationEnvironmentPreparedEvent:环境事先准备

      4、推断引导类

        准备阶段的最后一步是推断应用的引导类,也就是获取启动 main 方法的类,执行的是 deduceMainApplicationClass() 方法:

     1 private Class<?> deduceMainApplicationClass() {
     2     try {
     3         StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
     4         for (StackTraceElement stackTraceElement : stackTrace) {
     5             if ("main".equals(stackTraceElement.getMethodName())) {
     6                 return Class.forName(stackTraceElement.getClassName());
     7             }
     8         }
     9     }
    10     catch (ClassNotFoundException ex) {
    11         // Swallow and continue
    12     }
    13     return null;
    14 }

        可以看到,通过 getStackTrace()方法获取当前线程的执行栈,再通过 getMethodName()获取方法名,判断是否是 main 方法,最后返回 main 方法的所在类。

    SpringApplication run方法

      查看SpringApplication的run方法,流程如下:

     1 public class SpringApplication {
     2 
     3     ...
     4 
     5     public ConfigurableApplicationContext run(String... args) {
     6         // 开关
     7         StopWatch stopWatch = new StopWatch();
     8 
     9         // 开始
    10         stopWatch.start();
    11 
    12         ConfigurableApplicationContext context = null;
    13         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    14         configureHeadlessProperty();
    15 
    16         // 获取SpringApplicationRunListeners;从类路径下META‐INF/spring.factories
    17         SpringApplicationRunListeners listeners = getRunListeners(args);
    18 
    19         // 回调所有的获取SpringApplicationRunListener.starting()方法
    20         listeners.starting();
    21 
    22         try {
    23             // 封装命令行参数
    24             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    25 
    26             // 准备环境
    27             // 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();
    28             ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    29             configureIgnoreBeanInfo(environment);
    30  
    31             // 打印 Banner
    32             Banner printedBanner = printBanner(environment);
    33 
    34             // 通过 createApplicationContext 方法创建上下文,
    35             // 根据 Web 环境不同创建的上下文也不同
    36             context = createApplicationContext();
    37             
    38             // 异常报告
    39             exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    40                     new Class[] { ConfigurableApplicationContext.class }, context);
    41             
    42             // 准备上下文环境;将environment保存到ioc中;而且applyInitializers(); 
    43             // applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法 
    44             // 回调所有的SpringApplicationRunListener的contextPrepared();
    45             prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    46 
    47             // 刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
    48             // 扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
    49             refreshContext(context);
    50 
    51             // 刷新之后回调方法
    52             afterRefresh(context, applicationArguments);
    53 
    54             // 结束
    55             stopWatch.stop();
    56             if (this.logStartupInfo) {
    57                 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    58             }
    59 
    60             // 回调所有的获取SpringApplicationRunListener.started()方法
    61             listeners.started(context);
    62 
    63             // 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
    64             // ApplicationRunner先回调,CommandLineRunner再回调
    65             callRunners(context, applicationArguments);
    66         }
    67         catch (Throwable ex) {
    68             handleRunFailure(context, ex, exceptionReporters, listeners);
    69             throw new IllegalStateException(ex);
    70         }
    71 
    72         try {
    73             listeners.running(context);
    74         }
    75         catch (Throwable ex) {
    76             handleRunFailure(context, ex, exceptionReporters, null);
    77             throw new IllegalStateException(ex);
    78         }
    79         return context;
    80     }
    81 
    82     ...
    83 
    84 } 

      1、加载SpringApplicationRunListener

        查看代码getRunListeners(args);

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

        可以看出获取SpringApplicationRunListeners;也是从类路径下META‐INF/spring.factories

        获取完成之后回到SpringApplicationRunListeners的start()方法,listeners.starting();

      2、准备环境

        调用方法prepareEnvironment(listeners, applicationArguments);

     1 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
     2         ApplicationArguments applicationArguments) {
     3     // 创建并配置环境
     4     ConfigurableEnvironment environment = getOrCreateEnvironment();
     5     configureEnvironment(environment, applicationArguments.getSourceArgs());
     6     ConfigurationPropertySources.attach(environment);
     7 
     8     // 回调listeners的环境准备完成方法
     9     listeners.environmentPrepared(environment);
    10     bindToSpringApplication(environment);
    11     if (!this.isCustomEnvironment) {
    12         environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
    13                 deduceEnvironmentClass());
    14     }
    15     ConfigurationPropertySources.attach(environment);
    16     return environment;
    17 }
    18 
    19 
    20 
    21 private ConfigurableEnvironment getOrCreateEnvironment() {
    22     if (this.environment != null) {
    23         return this.environment;
    24     }
    25     // 根据webApplicationType类型判断,创建相应环境
    26     // webApplicationType类型在准备阶段已赋值
    27     switch (this.webApplicationType) {
    28     case SERVLET:
    29         return new StandardServletEnvironment();
    30     case REACTIVE:
    31         return new StandardReactiveWebEnvironment();
    32     default:
    33         return new StandardEnvironment();
    34     }
    35 }
       可以看到,通过getOrCreateEnvironment();获取环境对象,而获取的依据又是webApplicationType,webApplicationType类型在准备阶段已赋值,然后又回调了listeners的环境准备完成方法 

      3、创建上下文环境

        调用方法createApplicationContext();来创建上下文

     1 protected ConfigurableApplicationContext createApplicationContext() {
     2     Class<?> contextClass = this.applicationContextClass;
     3     if (contextClass == null) {
     4         try {
     5             switch (this.webApplicationType) {
     6             case SERVLET:
     7                 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
     8                 break;
     9             case REACTIVE:
    10                 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
    11                 break;
    12             default:
    13                 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    14             }
    15         }
    16         catch (ClassNotFoundException ex) {
    17             throw new IllegalStateException(
    18                     "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
    19                     ex);
    20         }
    21     }
    22     return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    23 }

        可以看到,根据 Web 环境不同创建的上下文也不同

      4、准备上下文环境

        调用方法:prepareContext(context, environment, listeners, applicationArguments, printedBanner);准备上下文

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
    
        // 调用listeners上下文准备完成方法
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // 获取Bean工厂
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册一些组件
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 加载资源
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
    
        // 调用listeners上下文加载完成方法
        listeners.contextLoaded(context);
    }

      5、刷新容器

        通过调试refresh方法,进入 refreshContext 方法,里面调用了 refresh 方法

     1 private void refreshContext(ConfigurableApplicationContext context) {
     2     refresh(context);
     3     if (this.registerShutdownHook) {
     4         try {
     5             context.registerShutdownHook();
     6         }
     7         catch (AccessControlException ex) {
     8             // Not allowed in some environments.
     9         }
    10     }
    11 }

        这里,最终也是调用 ApplicationContext 的 refresh 方法来启动上下文

    1 protected void refresh(ApplicationContext applicationContext) {
    2     Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    3     ((AbstractApplicationContext) applicationContext).refresh();
    4 }

        最终都是调用上下文中的 refresh 方法来启动。该方法是 ApplicationContext 的核心,如 Bean 注册、注入、解析 XML 、解析注解等是从该方法开始,其内部实现大致如下:

     1 public void refresh() throws BeansException, IllegalStateException {
     2     synchronized (this.startupShutdownMonitor) {
     3         // 1. 初始化 refresh 的上下文环境,就是记录下容器的启动时间、标记已启动状态、处理配置文件中的占位符
     4         prepareRefresh();
     5 
     6         // 2. 初始化 BeanFactory,加载并解析配置
     7         ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
     8 
     9         /* ---至此,已经完成了简单容器的所有功能,下面开始对简单容器进行增强--- */
    10 
    11         // 3. 对 BeanFactory 进行功能增强,如设置BeanFactory的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
    12         prepareBeanFactory(beanFactory);
    13 
    14         try {
    15             // 4. 后置处理 beanFactory,交由子类实现
    16             postProcessBeanFactory(beanFactory);
    17 
    18             // 5. 调用已注册的 BeanFactoryPostProcessor
    19             invokeBeanFactoryPostProcessors(beanFactory);
    20 
    21             // 6. 注册 BeanPostProcessor,仅仅是注册,调用在getBean的时候
    22             registerBeanPostProcessors(beanFactory);
    23 
    24             // 7. 初始化国际化资源
    25             initMessageSource();
    26 
    27             // 8. 初始化事件广播器
    28             initApplicationEventMulticaster();
    29 
    30             // 9. 留给子类实现的模板方法
    31             onRefresh();
    32 
    33             // 10. 注册事件监听器
    34             registerListeners();
    35 
    36             // 11. 实例化所有非延迟加载的单例
    37             finishBeanFactoryInitialization(beanFactory);
    38 
    39             // 12. 完成刷新过程,发布应用事件
    40             finishRefresh();
    41             
    42         } catch (BeansException ex) {
    43             if (logger.isWarnEnabled()) {
    44                 logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
    45             }
    46             
    47             // 13.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
    48             this.destroyBeans();
    49             
    50             // Reset 'active' flag.
    51             this.cancelRefresh(ex);
    52             // Propagate exception to caller.
    53             throw ex;
    54         } finally {
    55             // Reset common introspection caches in Spring's core, since we
    56             // might not ever need metadata for singleton beans anymore...
    57             this.resetCommonCaches();
    58         }
    59     }
    60 }

      6、刷新之后回调方法

        查看afterRefresh(context, applicationArguments);

    1 protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    2 }        

        可以看到里面是空方法,如有需要可以自定义一些内容

      7、回调Runners

        查看callRunners(context, applicationArguments);

     1 private void callRunners(ApplicationContext context, ApplicationArguments args) {
     2     List<Object> runners = new ArrayList<>();
     3     runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
     4     runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
     5     AnnotationAwareOrderComparator.sort(runners);
     6     for (Object runner : new LinkedHashSet<>(runners)) {
     7         if (runner instanceof ApplicationRunner) {
     8             callRunner((ApplicationRunner) runner, args);
     9         }
    10         if (runner instanceof CommandLineRunner) {
    11             callRunner((CommandLineRunner) runner, args);
    12         }
    13     }
    14 }

         可以看到,从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调 ,ApplicationRunner先回调,CommandLineRunner再回调

        最后返回上下文context  

    SpringApplication 配置

      SpringApplication 准备阶段结束后,按道理应该进入运行阶段,但运行阶段之前还有一个操作,就是可以修改 SpringApplication 默认配置。开头的代码示例可以看到,应用程序主类中的main方法中写的都是SpringApplication.run(xx.class),可能这种写法不满足我们的需求,我们可以对SpringApplication进行一些配置,例如关闭Banner,设置一些默认的属性等。下面则是利用 SpringApplicationBuilder 的方式来添加配置:  

     1 @SpringBootApplication
     2 public class Application {
     3 
     4     public static void main(String[] args) {
     5         // 普通
     6 //        SpringApplication.run(Application.class, args);
     7 
     8         // 定制
     9         Banner banner = new Banner(){
    10             @Override
    11             public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
    12                 System.out.println(environment);
    13                 System.out.println(sourceClass);
    14                 out.print("
    === <<< This is new banner >>> ===
    
    ");
    15             }
    16         };
    17 
    18         new SpringApplicationBuilder(Application.class)
    19 
    20                 // 设置当前应用类型
    21                 .web(WebApplicationType.SERVLET)
    22 
    23                 // 设置 banner 横幅打印方式、有关闭、日志、控制台
    24                 .bannerMode(Banner.Mode.CONSOLE)
    25 
    26                 // 设置自定义的 banner
    27                 .banner(banner)
    28 
    29                 // 追加自定义的 initializer 到集合中
    30                 .initializers()
    31 
    32                 // 追加自定义的 listeners 到集合中
    33                 .listeners()
    34 
    35                 .run(args);
    36     }
    37 
    38 }

      可以看到,使用该方式实现的SpringApplication可以对其添加自定义的配置。当然配置远远不止这么点,其它的还请自行观看源码。

  • 相关阅读:
    二次离线莫队
    串串题-各种算法的应用
    插头dp 笔记
    ST03模拟赛#1 比赛记录
    AtCoder Regular Contest 123 比赛记录(vp)
    冷门trick:线性区间单调求和
    dp优化瞎吹
    概率期望
    NOI挑战赛#2 (vp) 记录
    动态规划-线性dp-序列组成-5833. 统计特殊子序列的数目
  • 原文地址:https://www.cnblogs.com/h--d/p/12434206.html
Copyright © 2011-2022 走看看