此系列前面章节介绍的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>
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 }
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
可以对其添加自定义的配置。当然配置远远不止这么点,其它的还请自行观看源码。