现在继续看启动过程的详情,详细描述下SpringApplication构造函数:
1.加载过程中的SpringApplication初始化如下:
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、Reactive、或者是web环境;
this.webApplicationType = deduceWebApplicationType();
// 初始化器initializers:这些初始化器(initializers)是Spring Boot通过读取每个jar包下的/META-INF/spring.factories文件中的配置获取的。每一个initailizer都是一个实现了ApplicationContextInitializer接口的实例。ApplicationContextInitializer是Spring IOC(控制反转)容器中提供的一个接口:
//【特别要注意的是,这块的扫描结果,是为了run方法中的prepareContext和refreshContext函数初始化的,参见:https://www.cnblogs.com/hzhuxin/p/7742365.html】
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 监听器listeners:从spring.factories文件中找出key为ApplicationListener的类并实例化后设置到SpringApplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器
// 【参见:https://blog.csdn.net/Bob_666/article/details/79715156】
setListeners((Collection)
// 该方法获取执行类型子类实例集合,不太明白????????????????
getSpringFactoriesInstances(ApplicationListener.class));
// 获取当前调用栈,找到入口方法main所在的类,并将其复制给SpringApplication对象的成员变量mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
2.首先来看resourceLoader,传入空值初始化,什么是resourceLoader?
ResouceLoader是spring-core中的一个接口类
public interface ResourceLoader {
/** 首先,什么是classpath:描述了Java虚拟机在运行一个Class时在哪些路径中加载要运行的类以及运行的类要用到的类(http://blog.sina.com.cn/s/blog_605f5b4f01010i5u.html)
比如:用intellij idea执行springboot入口main时,控制台打印的第一条就是带有-classpath的java执行命令(由于太长,这里不列出命令详情),其中classpath参数中的path中,有一条:E:\springdemo\target\classes,这条就是当前springboot应用编译生成的目录,即在Maven项目中,所有的resources/src文件都将被复制到classes目录下,并且,classpath参数列表的先后顺序(也是实际的加载顺序,因为classpath会按照从前向后加载,如果有两个类,那么在classpath中靠前的会先被加载)为:安装的JRE类库、当前应用的target\classes目录、maven下载的依赖
https://www.javaworld.com/article/2077468/core-java/java-tip-105--mastering-the-classpath-with-jwhich.html */
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/** 通过提供的资源location参数获取Resource实例,该实例可以是ClasPathResource、FileSystemResource、UrlResource等实现类
资源可以为:
1. URL位置资源,如”file:C:/test.dat”
2. ClassPath位置资源,如”classpath:test.dat”
3.相对路径资源,如”WEB-INF/test.dat
*/
Resource getResource(String location);
/** 通过getClassLoader获取ClassLoader实例
http://www.blogjava.net/DLevin/archive/2012/12/01/392337.html
*/
@Nullable
ClassLoader getClassLoader();
关于ResourceLoader的使用,待后面分析
3.Assert判断确实有入口类传入
4.将入口类转化为有向set(是否是为了确保不被乱序加载????,待确认):
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
5.推断应用框架类型:
/** 判断:Servlet还是Reactive应用
Servlet:spring原始框架就是为Servlet API和Servlet容器创建的;
Reactive:是spring 5之后支持的一个web应用架构,集成Reactive的是:spring webflux 具体参见:https://springcloud.cc/web-reactive.html
Servlet和Reactive的选择:https://www.infoq.com/news/2017/12/servlet-reactive-stack
*/
this.webApplicationType = deduceWebApplicationType();
deduceWebApplicationType实现:
/** WebApplicationType 是个枚举值:NONE、SERVLET、REACTIVE */
private WebApplicationType deduceWebApplicationType() {
/** 其中:
REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.reactive.DispatcherHandler";
MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
即判断:存在REACTIVE的事件处理器,并且不是servlet的事件处理器,则返回REACTIVE
*/
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
/** String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
遍历该环境CLASS,如果都不存在,那么就是有问题,即也不是servlet应用
*/
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
/** 最终返回SERVLET类型 */
return WebApplicationType.SERVLET;
}
ClassUtils.isPresent函数:
/** @Nullable表示classLoader可以为空,实际上这里在调用的时候,就是传入了null,表示使用默认的classloader
即:查找对应的class是否存在
*/
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
6.初始化器:
这个很重要,是spring初始化的重要过程
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
这里的 ApplicationContextInitializer 是一个回调接口类, 是在ConfigurableApplicationContext刷新之前初始化Spring ConfigurableApplicationContext的回调接口。当执行:
ConfigurableApplicationContext.refresh()或SpringApplication.run()时生效。
在本例中,也就是run方法中的refresh时候会回调这些
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
那如何配置ApplicationContextInitializer?有多种方法:
(1)通过继承实现:
(2)在application.properties中以context.initializer.classes为key配置
(3)通过在spring.factories中配置
比如:https://www.jianshu.com/p/e4a0b900872b
【未完待续】