还是从SpringBoot的启动类说起,这篇文章主要分析启动类中的SpringApplication
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 复制代码
可以看出main函数中重要的就是 SpringApplication.run()
,这可以分为两部分来探讨:
首先进入SpringApplication的构造函数,先是单个参数的构造方法,后进入两个参数的构造方法,ResourceLoader是Spring的资源加载器,这里没有自定义的ResourceLoader传入,所以为NULL,而primarySources参数就是我们传入的Application.class启动类
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //1. 推断应用类型 this.webApplicationType = deduceWebApplicationType(); //2. initializer初始化模块,加载ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //3. 加载监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //4. 配置应用main方法所在的类 this.mainApplicationClass = deduceMainApplicationClass(); } 复制代码
SpringApplication的初始化主要包括以下4个步骤:
this.webEnvironment=deduceWebApplicationType();判断应用的类型,是否是servlet应用还是reactive应用或者是none, webEnvironment中定义了这三种类型
setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class)):通过SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer
进入loadFactoryNames方法,然后 进入loadSpringFactories方法,获取当前ClassLoader下的所有META-INF/spring.factories文件的配置信息而后通过 loadSpringFactories(classloader).getOrDefault(factoryClassName,Collections.emptyList()) 从所有META-INF/spring.factories文件的配置信息的map中获取指定的factory的值
默认情况下,从 spring.factories 文件找出的 key 为 ApplicationContextInitializer 的类有如上图中所示4种
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); } 复制代码
setListeners()方法与setInitializers()方法类似,只不过它是使用SpringFactoriesLoader在应用的classpath的META-INT/spring.factories中查找并加载所有可用的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 复制代码
ApplicationListener,应用程序事件(ApplicationEvent)监听器:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); } 复制代码
更详细的分析可以参阅我之前的文章: springboot系列文章之启动时初始化数据
在SpringApplication构造函数的最后一步,根据调用栈推断并设置main方法的定义类
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; } 复制代码
SpringApplication实例初始化完成并且完成设置后,就可以开始run方法的逻辑了,对于这个run方法我将分为以下几点进行逐步剖析,而StopWatch是一个工具类,主要是方便记录程序运行时间,这里就不仔细介绍了。
public ConfigurableApplicationContext run(String... args) { //构造一个任务执行观察期 StopWatch stopWatch = new StopWatch(); //开始执行,记录开始时间 stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //1 configureHeadlessProperty(); //2 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { //3 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //4 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //5 configureIgnoreBeanInfo(environment); //6 Banner printedBanner = printBanner(environment); //7 context = createApplicationContext(); //8 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //9 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //10 refreshContext(context); //2.0版本中是空实现 //11 afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); //12 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } 复制代码
SpringApplication的run方法主要分为以下几步:
configureHeadlessProperty()
设置 headless 模式,即设置系统属性java.awt.headless,它是J2SE的一种模式,用于在缺少显示屏,键盘,或者鼠标时的系统配置,该属性会被设置为true,更多的信息可以参考这里
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; ... private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); } 复制代码
SpringApplicationRunListeners listeners = getRunListeners(args); 复制代码
getRunListeners(args)
也是通过 SpringFactoriesLoader
从 META-INF/spring.factories
查找到并加载的 SpringApplicationRunListener
。该类实际上是监听SpringApplication的run方法的执行
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); } ..... private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates //通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListner Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
这里的SpringApplicationRunListener监听器与SpringApplication时加载的ApplicationListener监听器不同,SpringApplicationRunListener是SpringBoot新增的类,SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener。虽然说是新增的, 但是它们之间是有联系的,它们之间的的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的
更详细的分析请参阅 : SpringBoot源码分析之SpringBoot的启动过程
将args参数封装成 ApplicationArguments
对象
public DefaultApplicationArguments(String[] args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; } 复制代码
官网对 ApplicationArguments
的解释如下
根据 listeners
和 applicationArguments
创建并配置当前SpringBoot应用将要使用的Enviroment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; } 复制代码
遍历调用所有SpringApplicationRunListener的 enviromentPrepared()
方法就是宣告当前SpringBoot应用使用的Enviroment准备好了
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { if (System.getProperty( CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE); System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString()); } } 复制代码
打印banner标志,就是启动SpringBoot项目时出现的 Spring
字样,当然我们也可以自定义banner,这里就不多说了
private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter( resourceLoader, this.banner); if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } return bannerPrinter.print(environment, this.mainApplicationClass, System.out); } 复制代码
createApplicationContext()
根据用户是否明确设置了applicationContextClass类型以及SpringApplication初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成。
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; protected ConfigurableApplicationContext createApplicationContext() { //用户是否明确设置了applicationContextClass,在SpringApplication中有对应的setter方法 Class<?> contextClass = this.applicationContextClass; //如果没有主动设置 if (contextClass == null) { try { //判断当前应用的类型,也就是之前SpringApplication初始化阶段的推断结果 switch (this.webApplicationType) { //servlet应用程序 case SERVLET: contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); break; //reactive响应式程序 case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; //默认类型 default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } 复制代码
在SpringBoot官网对ApplicationContext的类型是如下定义的:
exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); 复制代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
这里也是通过SpringFactoriesLoader加载META-INF/spring.factories中key为SpringBootExceptionReporter的全类名的value值
SpringBootExceptionReporter
是一个回调接口,用于支持对 SpringApplication
启动错误的自定义报告。里面就一个报告启动失败的方法 org.springframework.boot.diagnostics.FailureAnalyzers
FailureAnalyzer
和 FailureAnalysisReporter
实例 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //设置应用的环境 context.setEnvironment(environment); //对 context 进行了预设置 postProcessApplicationContext(context); applyInitializers(context); 遍历调用SpringApplicationRunListener的contextPrepared()方法,通告SpringBoot应用使用的ApplicationContext准备好了 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); //遍历调用SpringApplicationRunListener的contextLoaded()方法,通告ApplicationContext装填完毕 listeners.contextLoaded(context); } 复制代码
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } } 复制代码
遍历调用这些ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理
protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug( "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); } 复制代码
设置资源加载器,加载各种beans到ApplicationContext对象中
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } 复制代码
进入内部的refresh()方法,准备环境所需的bean工厂,通过工厂产生环境所需的bean,重点就是产生bean
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } 复制代码
上下文刷新后调用该方法,目前没有操作
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { } 复制代码
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } 复制代码
查找当前的ApplicationContext中是否注册有CommandLineRunner或者ApplicationRunner,如果有,就遍历执行他们。
上面从SpringApplication的初始化到SpringApplication.run()方法执行,基本上按照其内部函数调用的顺序一步一步分析下来,内容非常多,很容易把人搞晕。在网上发现一张图,图出自 SpringBoot启动流程解析 ,画的比较清楚明白,把SpringBoot启动整个流程都包含进来了
再总结下run方法中最关键的几步:
上面的分析都是基于SpringBoot2.0版本,在之前的版本,内容上可能有些偏差,大体思路是差不多的。在阅读源码的过程中,看了很多前人分析的博客文章,也借鉴了他们的分析流程,有点「前人栽树,后人乘凉」的感觉,现在「取之网络,再回馈之网络」