目录
推断应⽤类型deduceWebApplicationType()
getSpringFactoriesInstances();
- @SpringBootApplication
- public class SpringBootMytestApplication{
-
- public static void main(String[] args) {
- SpringApplication.run(SpringBootMytestApplication.class, args);
-
- }
- }
点进run⽅法,primarySource就是引导类SpringBootMytestApplication
- public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
- //调用重载方法
- return run(new Class[]{primarySource}, args);
- }
进入重载run方法
- public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
- return (new SpringApplication(primarySources)).run(args);
- }
- public SpringApplication(Class>... primarySources) {
- this((ResourceLoader)null, primarySources);
- }
-
- public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
- this.sources = new LinkedHashSet();
- this.bannerMode = Mode.CONSOLE;
- this.logStartupInfo = true;
- this.addCommandLineProperties = true;
- this.addConversionService = true;
- this.headless = true;
- this.registerShutdownHook = true;
- this.additionalProfiles = Collections.emptySet();
- this.isCustomEnvironment = false;
- this.lazyInitialization = false;
- this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
- this.applicationStartup = ApplicationStartup.DEFAULT;
- //初始化资源加载器为null
- this.resourceLoader = resourceLoader;
- //断⾔,加载资源类不能为null
- Assert.notNull(primarySources, "PrimarySources must not be null");
- //初始化配置类的类名信息(格式转换)将primarySources数组转换为List,最后放到LinkedHashSet集合中
- this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
- //【1.1 推断应⽤类型,后⾯会根据类型初始化对应的环境。常⽤的⼀般都是servlet环境 】
- this.webApplicationType = WebApplicationType.deduceFromClasspath();
- //【1.2 初始化classpath下 META-INF/spring.factories中已配置的BootstrapRegistryInitializer】
- this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
- //【1.2 初始化classpath下META-INF/spring.factories中已配置的ApplicationContextInitializer】
- this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
- //【1.3 初始化classpath下所有已配置的ApplicationListener。ApplicationListener是spring的事件监听器,典型的观察者模式,实现ApplicationListener接⼝,通过ApplicationEvent类,可以实现对spring容器全⽣命周期的监听,当然也可以⾃定义监听事件】
- this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
- //【1.4 根据调⽤栈,推断出 main ⽅法的类名 】
- this.mainApplicationClass = this.deduceMainApplicationClass();
- }
- /**
- * 判断 应⽤的类型
- * NONE: 应⽤程序不是web应⽤,也不应该⽤web服务器去启动
- * SERVLET: 应⽤程序应作为基于servlet的web应⽤程序运⾏,并应启动嵌⼊式servlet web(tomcat)服务器。
- * REACTIVE: 应⽤程序应作为 reactive web应⽤程序运⾏,并应启动嵌⼊式reactive web服务器。
- * @return
- */
- static WebApplicationType deduceFromClasspath() {
- //classpath下必须存在org.springframework.web.reactive.DispatcherHandler
- if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
- return REACTIVE;
- } else {
- String[] var0 = SERVLET_INDICATOR_CLASSES;
- int var1 = var0.length;
-
- for(int var2 = 0; var2 < var1; ++var2) {
- String className = var0[var2];
- if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
- return NONE;
- }
- }
- /**classpath环境下存在javax.servlet.Servlet或者
- *org.springframework.web.context.ConfigurableWebApplicationContext
- */
- return SERVLET;
- }
- }
返回类型是WebApplicationType的枚举类型, WebApplicationType 有三个枚举,三 个枚举的解释如其中注释
具体的判断逻辑如下:
初始化classpath下 META-INF/spring.factories中已配置的类
- private
Collection getSpringFactoriesInstances(Class type) { - return this.getSpringFactoriesInstances(type, new Class[0]);
- }
-
- /**
- * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的
- ⼯⼚实例
- * @param type
- * @param parameterTypes
- * @param args
- * @param
- * @return
- */
- private
Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { - ClassLoader classLoader = this.getClassLoader();
-
- // Use names and ensure unique to protect against duplicates
- //通过指定的classLoader从 META-INF/spring.factories 的资源⽂件中,
- //读取 key 为 type.getName() 的 value
- Set
names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); - //创建Spring⼯⼚实例
- List
instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); - //对Spring⼯⼚实例排序(org.springframework.core.annotation.Order注解指定的顺序)
- AnnotationAwareOrderComparator.sort(instances);
- return instances;
- }
loadFactoryNames() 这个⽅法很重要,这个⽅法是spring-core中提供的从METAINF/spring.factories中获取指定的类(key)的同⼀⼊⼝⽅法
在这⾥,获取的是key为 org.springframework.context.ApplicationContextInitializer 的类。
SpringBoot 启动流程最重要的部分run⽅法,初始化容器
- //初始化容器,得到ApplicationContext对象
- public ConfigurableApplicationContext run(String... args) {
- //记录程序开始运⾏时间
- long startTime = System.nanoTime();
- //将系统配置引导信息再次封装为上下文对象
- DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
- ConfigurableApplicationContext context = null;
- //模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器、鼠标、键盘....),设置了一个java.awt.headless=true的信号,只是为了设备兼容
- this.configureHeadlessProperty();
- //1、获取当前注册的可运行的监听器
- SpringApplicationRunListeners listeners = this.getRunListeners(args);
- //监听器执行starting
- listeners.starting(bootstrapContext, this.mainApplicationClass);
-
- try {
- //获取参数
- ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
- //2、将前期读取的数据加载成了一个环境对象,用来描述信息,构造应⽤上下⽂环境
- ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
- //处理需要忽略的Bean
- this.configureIgnoreBeanInfo(environment);
- //打印banner
- Banner printedBanner = this.printBanner(environment);
- //3、初始化应⽤上下⽂,创建容器对象,根据前期配置的容器类型进行判定并创建
- context = this.createApplicationContext();
- //设置启动模式
- context.setApplicationStartup(this.applicationStartup);
- //4、刷新应⽤上下⽂(容器)前的准备阶段,对容器进行设置,参数来源于前期的设定
- this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
- //5、刷新应⽤上下⽂(容器)
- this.refreshContext(context);
- //刷新应⽤上下⽂(容器)后处理
- this.afterRefresh(context, applicationArguments);
- //记录程序运行时间
- Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
- //判断是否记录启动时间的日志
- if (this.logStartupInfo) {
- //创建日志对象,输出日志信息,包含启动时间
- (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
- }
- //监听器执行started
- listeners.started(context, timeTakenToStartup);
- this.callRunners(context, applicationArguments);
- } catch (Throwable var12) {
- this.handleRunFailure(context, var12, listeners);
- throw new IllegalStateException(var12);
- }
-
- try {
- Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
- //监听器执行ready
- listeners.ready(context, timeTakenToReady);
- return context;
- } catch (Throwable var11) {
- this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
- throw new IllegalStateException(var11);
- }
- }
在以上的代码中,启动过程中的重要步骤共分为六步
获取监听器
- private SpringApplicationRunListeners getRunListeners(String[] args) {
- Class>[] types = new Class[]{SpringApplication.class, String[].class};
- return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
- }
启动监听器
- void starting(ConfigurableBootstrapContext bootstrapContext, Class> mainApplicationClass) {
- this.doWithListeners("spring.boot.application.starting", (listener) -> {
- listener.starting(bootstrapContext);
- }, (step) -> {
- if (mainApplicationClass != null) {
- step.tag("mainApplicationClass", mainApplicationClass.getName());
- }
-
- });
- }
应⽤上下⽂环境包括计算机的环境,Java环境,Spring的运⾏环 境,Spring项⽬的配置(在SpringBoot中就是那个熟悉的 application.properties/yml)等等。
- private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
- //创建并配置相应的环境
- ConfigurableEnvironment environment = this.getOrCreateEnvironment();
- //根据⽤户配置,配置 environment系统环境
- this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
- ConfigurationPropertySources.attach((Environment)environment);
- // 启动相应的监听器,其中⼀个重要的监听器ConfigFileApplicationListener 就是加载项⽬配置⽂件的监听器。
- listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
- DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
- Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
- this.bindToSpringApplication((ConfigurableEnvironment)environment);
- if (!this.isCustomEnvironment) {
- EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
- environment = environmentConverter.convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
- }
-
- ConfigurationPropertySources.attach((Environment)environment);
- return (ConfigurableEnvironment)environment;
- }
⽅法中主要完成的⼯作,⾸先是创建并按照相应的应⽤类型配 置相应的环境,然后根据⽤户的配置,配置系统环境,然后启动监听器,并加载系 统配置⽂件。
- private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
- //设置容器环境
- context.setEnvironment(environment);
- //执⾏容器后置处理
- this.postProcessApplicationContext(context);
- //执⾏容器中的 ApplicationContextInitializer 包括spring.factories和通过三种⽅式⾃定义的
- this.applyInitializers(context);
- //向各个监听器发送容器已经准备好的事件
- listeners.contextPrepared(context);
- bootstrapContext.close(context);
- if (this.logStartupInfo) {
- this.logStartupInfo(context.getParent() == null);
- this.logStartupProfileInfo(context);
- }
-
- ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
- // Add boot specific singleton beans
- //将main函数中的args参数封装成单例Bean,注册进容器
- beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
- //将 printedBanner 也封装成单例,注册进容器
- if (printedBanner != null) {
- beanFactory.registerSingleton("springBootBanner", printedBanner);
- }
-
- if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
- ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
- if (beanFactory instanceof DefaultListableBeanFactory) {
- ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
- }
- }
-
- if (this.lazyInitialization) {
- context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
- }
-
- context.addBeanFactoryPostProcessor(new SpringApplication.PropertySourceOrderingBeanFactoryPostProcessor(context));
- Set
- Assert.notEmpty(sources, "Sources must not be empty");
- //加载我们的启动类,将启动类注⼊容器
- this.load(context, sources.toArray(new Object[0]));
- //发布容器已加载事件
- listeners.contextLoaded(context);
- }
到IoC容器的初始化过程,主要分下⾯三步:
- @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.
- //这⾥是在⼦类中启动 refreshBeanFactory() 的地⽅
- @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.
- //这⾥是在⼦类中启动 refreshBeanFactory() 的地⽅
- //发布容器事件,结束Refresh过程
- finishRefresh();
- } catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during
- context initialization - " +
- "cancelling refresh attempt: " + ex);
- }
- // Destroy already created singletons to avoid
- dangling resources.
- destroyBeans();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- } finally {
- // Reset common introspection caches in Spring's
- core, since we
- // might not ever need metadata for singleton
- beans anymore...
- resetCommonCaches();
- }
- }
- }