SpringBoot 系列文章简介
SpringBoot 源码阅读辅助篇:
Spring IoC 容器与应用上下文的设计与实现
SpringBoot 启动流程源码分析:
SpringBoot 启动流程分析(一):SpringApplication 类初始化过程
SpringBoot 启动流程分析(二):SpringApplication 的 run 方法
SpringBoot 启动流程分析 (三):SpringApplication 的 run 方法之 prepareContext() 方法
SpringBoot 启动流程分析(四):IoC 容器的初始化过程
SpringBoot 启动流程分析(五):SpringBoot 自动装配原理实现
SpringBoot 启动流程分析(六):IoC 容器依赖注入
笔者注释版 Spring Framework 与 SpringBoot 源码 Git 传送门: 请不要吝啬小星星
SpringBoot-2.0.4.RELEASE https://github.com/hello-shf/spring-boot-build.git
一, SpringApplication 初始化过程
1.1,SpringBoot 项目的 mian 函数
常规的这个主类如下图所示, 我们一般会这样去写.
在这个类中需要关注的是
- @SpringBootApplication
- SpringApplication.run()
关于 @SpringBootApplication 注解, 在后面分析 SpringBoot 自动装配的章节会展开去分析.
本章节中我们需要关注的就是 SpringApplication.run() 方法.
查看 run()方法的实现, 如下面代码所示, 我们发现其实其首先是创建了 SpringApplication 的实例, 然后调用了 SpringApplication 的 run()方法, 那本章我们关注的就是 SpringApplication 创建实例的过程.
- /**
- * Static helper that can be used to run a {@link SpringApplication} from the
- * specified sources using default settings and user supplied arguments.
- *
- * @param primarySources the primary sources to load
- * @param args the application arguments (usually passed from a Java main method)
- * @return the running {@link ApplicationContext}
- */
- public static ConfigurableApplicationContext run(Class<?>[] primarySources,
- String[] args) {
- return new SpringApplication(primarySources).run(args);
- }
1.2, SpringApplication() 构造方法
继续查看源码, SpringApplication 实例化过程, 首先是进入但参数的构造方法, 最终回来到两个参数的构造方法.
- public SpringApplication(Class<?>... primarySources) {
- this(null, primarySources);
- }
- @SuppressWarnings({"unchecked", "rawtypes"})
- public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
- this.resourceLoader = resourceLoader;
- Assert.notNull(primarySources, "PrimarySources must not be null");
- this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
- // 推断应用类型, 后面会根据类型初始化对应的环境. 常用的一般都是 servlet 环境
- this.webApplicationType = deduceWebApplicationType();//2.2.1
- // 初始化 classpath 下 META-INF/spring.factories 中已配置的 ApplicationContextInitializer
- setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//2.2.2
- // 初始化 classpath 下所有已配置的 ApplicationListener
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//2.2.3
- // 根据调用栈, 推断出 main 方法的类名
- this.mainApplicationClass = deduceMainApplicationClass();
- }
1.2.1,deduceWebApplicationType(); 该方法推断应用的类型. SERVLET REACTIVE NONE
- // 常量值
- private static final String[] WEB_ENVIRONMENT_CLASSES = {"javax.servlet.Servlet",
- "org.springframework.web.context.ConfigurableWebApplicationContext"};
- private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.reactive.DispatcherHandler";
- private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
- + "web.servlet.DispatcherServlet";
- private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
- /**
- * 判断 应用的类型
- * NONE: 应用程序不是 Web 应用, 也不应该用 Web 服务器去启动
- * SERVLET: 应用程序应作为基于 servlet 的 Web 应用程序运行, 并应启动嵌入式 servlet Web(tomcat)服务器.
- * REACTIVE: 应用程序应作为 reactive Web 应用程序运行, 并应启动嵌入式 reactive Web 服务器.
- * @return
- */
- private WebApplicationType deduceWebApplicationType() {
- //classpath 下必须存在 org.springframework.Web.reactive.DispatcherHandler
- if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
- && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
- && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
- return WebApplicationType.REACTIVE;
- }
- for (String className : WEB_ENVIRONMENT_CLASSES) {
- if (!ClassUtils.isPresent(className, null)) {
- return WebApplicationType.NONE;
- }
- }
- //classpath 环境下存在 javax.servlet.Servlet 或者 org.springframework.Web.context.ConfigurableWebApplicationContext
- return WebApplicationType.SERVLET;
- }
返回类型是 WebApplicationType 的枚举类型, WebApplicationType 有三个枚举, 三个枚举的解释如其中注释
具体的判断逻辑如下:
WebApplicationType.REACTIVE classpath 下存在 org.springframework.Web.reactive.DispatcherHandler
WebApplicationType.SERVLET classpath 下存在 javax.servlet.Servlet 或者 org.springframework.Web.context.ConfigurableWebApplicationContext
WebApplicationType.NONE 不满足以上条件.
1.2.2, setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
初始化 classpath 下 META-INF/spring.factories 中已配置的 ApplicationContextInitializer.
- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
- return getSpringFactoriesInstances(type, new Class<?>[]{});
- }
- /**
- * 通过指定的 classloader 从 META-INF/spring.factories 获取指定的 Spring 的工厂实例
- * @param type
- * @param parameterTypes
- * @param args
- * @param <T>
- * @return
- */
- 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
- // 通过指定的 classLoader 从 META-INF/spring.factories 的资源文件中,
- // 读取 key 为 type.getName() 的 value
- Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
- // 创建 Spring 工厂实例
- List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
- classLoader, args, names);
- // 对 Spring 工厂实例排序(org.springframework.core.annotation.Order 注解指定的顺序)
- AnnotationAwareOrderComparator.sort(instances);
- return instances;
- }
看看 getSpringFactoriesInstances 都干了什么, 看源码, 有一个方法很重要 loadFactoryNames() 这个方法很重要, 这个方法是 spring-core 中提供的从 META-INF/spring.factories 中获取指定的类 (key) 的同一入口方法.
在这里, 获取的是 key 为 org.springframework.context.ApplicationContextInitializer 的类.
debug 看看都获取到了哪些
上面说了, 是从 classpath 下 META-INF/spring.factories 中获取, 我们验证一下:
发现在上图所示的两个工程中找到了 debug 中看到的 6 条结果. ApplicationContextInitializer 是 Spring 框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用 refresh()方法之前, 回调这个类的 initialize 方法. 通过 ConfigurableApplicationContext 的实例获取容器的环境 Environment, 从而实现对配置文件的修改完善等工作.
关于怎么实现自定义的 ApplicationContextInitializer 请看我的另一篇专门介绍该类的博客.
1.2.3, setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
初始化 classpath 下 META-INF/spring.factories 中已配置的 ApplicationListener.
ApplicationListener 的加载过程和上面的 ApplicationContextInitializer 类的加载过程是一样的. 不多说了, 至于 ApplicationListener 是 spring 的事件监听器, 典型的观察者模式, 通过 ApplicationEvent 类和 ApplicationListener 接口, 可以实现对 spring 容器全生命周期的监听, 当然也可以自定义监听事件. 为了梳理 springboot 的启动流程在这里先不说这个了. 后面有时间的话再介绍.
关于 ApplicationContextInitializer 的详细介绍请看 < SpringBoot 之 ApplicationContextInitializer 的理解和使用>
二, 总结
关于 SpringApplication 类的构造过程, 到这里我们就梳理完了. 纵观 SpringApplication 类的实例化过程, 我们可以看到, 合理的利用该类, 我们能在 spring 容器创建之前做一些预备工作, 和定制化的需求.
比如, 自定义 SpringBoot 的 Banner, 比如自定义事件监听器, 再比如在容器 refresh 之前通过自定义 ApplicationContextInitializer 修改配置一些配置或者获取指定的 bean 都是可以的...
下一节开始分析 SpringBoot 容器的构建过程, 也就是那个大家多少都看过的 run(); 方法.
来源: https://www.cnblogs.com/hello-shf/p/10976646.html