H__D  

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

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

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

  SpringBoot启动图,参考: 【SpringBoot】SpringBoot 启动原理图

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("\n=== <<< This is new banner >>> ===\n\n");
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可以对其添加自定义的配置。当然配置远远不止这么点,其它的还请自行观看源码。

 

posted on 2020-03-07 14:44  H__D  阅读(622)  评论(1编辑  收藏  举报