spring boot 项目使用默认配置的思想,极大的简化了 spring 项目的开发。下面的代码就是一个最简单的 spring 项目:

@SpringBootApplication
public class DebugerApplication {public static void main(String[] args) {SpringApplication.run(DebugerApplication.class, args);}
}

代码很简洁,只调用了SpringApplication的 run 方法。接下来,我们就要深入源码,分析下,spring boot的启动过程。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args){return new SpringApplication(primarySources).run(args);
}

在 SpringApplication的静态方法 run里,其实上是构造了SpringApplication的实例,并调用了实例的 run(类方法)。

先看看 SpringApplication 的构造方法干了什么?

ublic 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.1* 此处的webApplicationType 标识当前程序的类型, 判断方式是当前路径下是否有不同类型项目对应的class* NONE: The application should not run as a web application and should not start an embedded web server.* SERVLET: servlet-based web application and should start an embedded servlet web server.例如 spring mvc* REACTIVE: reactive web application and should start an embedded reactive web server. 例如 spring web flux*/this.webApplicationType = WebApplicationType.deduceFromClasspath();/* 1.2* 设置初始化器(Initializer)*/setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));/* 1.2* 设置监听器(Listener)*/setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 1.3this.mainApplicationClass = deduceMainApplicationClass();
}

使用deduceFromClasspath 判断当前程序类型

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."+ "web.servlet.DispatcherServlet";private static final String WEBFLUX_INDICATOR_CLASS = "org."+ "springframework.web.reactive.DispatcherHandler";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}       for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;
}

设置初始化器(Initializer) 和监听器(listener)

可以看到初始化器和监听器的获取方法都是getSpringFactoriesInstances。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {// 获取当前资源的加载器ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);AnnotationAwareOrderComparator.sort(instances); // 根据 @Order和@Priority注解排序return instances;
}

当入参是”ApplicationContextInitializer.class”时,factoryClassName为”org.springframework.context.ApplicationContextInitializer”。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList();
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {/** groupId: org.springframework.boot*  artifactId: spring-boot*  路径 /META-INF/spring.factories*  key 是接口,value是实现类,多个实现类逗号隔开*/Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {              URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +                  FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

文件中的一段内容:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# 在Spring上下文被刷新之前进行初始化的操作。# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener#ApplicationListener 监听ApplicationEvent类型的子类

上述配置文件中的实现类会通过createSpringFactoriesInstances方法获取对应的实例。

private <T> List<T> createSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);// 获得class的构造器Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);// 对于java class 使用 Constructor.newInstance获取实例。T instance = (T) BeanUtils.instantiateClass(constructor, args);instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;
}

至此,对于SpringApplication实例的初始化过程就结束了。

SpringApplication.run方法

public ConfigurableApplicationContext run(String... args) {// 初始化并启动一个计时工具StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();// 2.1 获取SpringApplicationRunListenersSpringApplicationRunListeners listeners = getRunListeners(args);// 发出ApplicationStartingEvent事件listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 注意,此时参数为空// 2.2 根据SpringApplicationRunListeners以及参数来准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体Banner printedBanner = printBanner(environment);// 2.3 创建Spring上下文context = createApplicationContext();// 准备异常报告器exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);// 2.4 Spring上下文前置处理prepareContext(context, environment, listeners, applicationArguments,printedBanner);// 2.5 Spring上下文刷新refreshContext(context);// 2.6 Spring上下文后置处理afterRefresh(context, applicationArguments);// 停止计时器stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 发出ApplicationStartedEvent事件listeners.started(context);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;
}
// 设置java.awt.headless系统属性为true - 没有图形化界面
private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

获取 run listeners

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

这里仍然利用了getSpringFactoriesInstances方法来获取实例.

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener # EventPublishingRunListener主要是负责发布SpringApplicationEvent事件的。通过内置的initialMulticaster 发布事件。

准备环境 prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environment// 使用已有环境(refresh)或新建环境ConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());// 发布ApplicationEnvironmentPreparedEvent 事件listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}switch (this.webApplicationType) {case SERVLET:return new StandardServletEnvironment(); // spring boot webcase REACTIVE:return new StandardReactiveWebEnvironment();default:return new StandardEnvironment();}
}
protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {if (this.addConversionService) {// 初始化属性转换服务ConversionService conversionService = ApplicationConversionService.getSharedInstance();environment.setConversionService((ConfigurableConversionService) conversionService);}//配置Property SourcesconfigurePropertySources(environment, args);//配置ProfilesconfigureProfiles(environment, args);
}

创建Spring上下文

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;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);
}

Spring上下文前置处理

private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 将环境和上下文关联起来context.setEnvironment(environment);// 为上下文配置Bean生成器以及资源加载器(如果它们非空)postProcessApplicationContext(context);// 调用初始化器applyInitializers(context);// 触发Spring Boot启动过程的contextPrepared事件listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beans// 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBannerConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);  }// Load the sources// 加载sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 构造BeanDefinitionLoader并完成Bean定义的加载load(context, sources.toArray(new Object[0]));// 触发Spring Boot启动过程的contextLoaded事件listeners.contextLoaded(context);
}protected void postProcessApplicationContext(ConfigurableApplicationContext context) {// 为上下文配置Bean生成器以及资源加载器(如果它们非空)if (this.beanNameGenerator != null) {context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);}if (this.resourceLoader != null) {if (context instanceof GenericApplicationContext) {((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);}if (context instanceof DefaultResourceLoader) {((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());}}if (this.addConversionService) {context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());}
}
// 调用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) {// 在创建SpringApplication实例时设置的初始化器for (ApplicationContextInitializer initializer : getInitializers()) {Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");initializer.initialize(context);}
}

Spring上下文刷新

private void refreshContext(ConfigurableApplicationContext context) {refresh(context);// shutdown 钩子if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}
}
protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();
}

Spring上下文后置处理

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

callRunners

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);}}
}private void callRunner(ApplicationRunner runner, ApplicationArguments args) {try {(runner).run(args);}catch (Exception ex) {throw new IllegalStateException("Failed to execute ApplicationRunner", ex);}
}private void callRunner(CommandLineRunner runner, ApplicationArguments args) {try {(runner).run(args.getSourceArgs());}catch (Exception ex) {throw new IllegalStateException("Failed to execute CommandLineRunner", ex);}
}

调用注册的runner,下面的接口的实现类,区别是参数不同:

  1. org.springframework.boot.ApplicationRunner
  2. org.springframework.boot.CommandLineRunner
public interface ApplicationRunner {void run(ApplicationArguments args) throws Exception;}public interface CommandLineRunner {void run(String... args) throws Exception;
}

【java】spring-boot源码解析之应用启动相关推荐

  1. spring boot 源码解析23-actuate使用及EndPoint解析

    前言 spring boot 中有个很诱人的组件–actuator,可以对spring boot应用做监控,只需在pom文件中加入如下配置即可: <dependency><group ...

  2. spring boot 源码解析15-spring mvc零配置

    前言 spring boot 是基于spring 4 的基础上的一个框架,spring 4 有一个新特效–>基于java config 实现零配置.而在企业的实际工作中,spring 都是和sp ...

  3. spring boot 源码解析43-JmxMetricWriter详解

    前言 本文我们来介绍JmxMetricWriter, JmxMetricWriter是依赖于spring jmx 来实现的. 因此本文的开头先介绍一下spring boot 与jmx的集成,然后分析J ...

  4. spring boot 源码解析31-AuthenticationAuditListener,AuthorizationAuditListener

    前言 这篇文章我们来分析一下org.springframework.boot.actuate.security,org.springframework.boot.actuate.audit中的代码,这 ...

  5. spring boot 源码解析52-actuate中MVCEndPoint解析

    前言 之前的几篇文章分析了spring boot 中有关endpoint的实现,细心的朋友可以发现,在org.springframework.boot.actuate.endpoint.mvc 包下也 ...

  6. spring boot 源码解析29-LogbackLoggingSystem

    前言 现在我们来分析一下LogbackLoggingSystem,spring boot 中默认生效的,该类也是继承自Slf4JLoggingSystem. 解析 LogbackLoggingSyst ...

  7. spring boot 源码解析8-SpringApplication#run第8步

    前言 这篇文章我们分析SpringApplication#run第8步.执行prepareContext方法.该方法的内容比较多.我们慢慢来. 分析 SpringApplication#run 第8步 ...

  8. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  9. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

最新文章

  1. paramiko的使用
  2. 史上最简单的SpringCloud教程 | 第八篇: 消息总线(Spring Cloud Bus)(Finchley版本)
  3. Git命令集十五——拉取命令
  4. python中exception类的_什么是Python异常?Python异常的种类
  5. python中计算排队等待时间_codewars(python)练习笔记十:计算超市排队时长
  6. JS日期函数getMonth()的值域是0--11
  7. Python --- 卸载
  8. Python DButils
  9. [leetcode] 406. Queue Reconstruction by Height (medium)
  10. 领扣(LeetCode)七进制数 个人题解
  11. 这么狠,私塾在线架构师系列课程全都免费发放
  12. 综合能源系统通用建模及规划方法研究—笔记
  13. QIIME 2教程. 22命令行界面q2cli(2021.2)
  14. 思科交换机配置dhcp
  15. 气象数据的简单数据分析处理——基于Notebook
  16. 制作u盘winpe启动盘_绿色、无捆绑的优启通U盘启动盘制作工具
  17. 计算机系的的毕业感言,计算机班同学的毕业感言
  18. 华为scp快充协议详解_华为SCP快充技术曝光:支持“电荷泵”技术,最高可达20W...
  19. html如何找坐标,如何获取现在的坐标
  20. 苹果无线耳机使用方法_苹果手机11新手使用方法

热门文章

  1. 机器学习实战(MachineLearinginAction) 第二章 k-近邻算法
  2. numpy matrix 矩阵对象
  3. Spring Boot学习总结(19)——使用Redisson实现分布式锁
  4. Myeclipse学习总结(17)——Java主流IDE优缺点分析
  5. Java基础学习总结(47)——JAVA输入输出流再回忆
  6. Oracle学习总结(8)—— 面向程序员的数据库访问性能优化法则
  7. Jsp学习总结(1)——JSP九大内置对象和四种属性范围解读
  8. 使用java理解程序逻辑 第十二章_Java多线程中锁的理解与使用(二)
  9. java生成平滑散点图_【转载】如何在excel制作散点图平滑线散点图
  10. SDNU 1272.SL的秘密