前情提要:
本次使用2.5.2版本Springboot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
</parent>
1.一个普通的Springboot启动类
2.进入SpringApplication.run方法查看
3.
先看SpringApplication 构造方法
其中
primarySources 就是 第一张图里写的 Example.class
4.
查看上面第一个getBootstrapRegistryInitializersFromSpringFactories
5.
查看第2步Spring工程加载器加载类做了哪些操作
SpringFactoriesLoader.loadFactoryNames
6.查看loadSpringFactories加载了什么类
看一看cache是啥
简单讲一下classLoader
classLoader有三种:
1.Bootstrap loader
用C++写的,加载%JAVA_HOME%/jre/lib
,-Xbootclasspath参数指定路径
及%JAVA_HOME%/jre/classes
可以简单的认为,使java运行起来就是靠这个类加载器,程序员接触不到。
2.ExtClassLoader
由Bootstrp loader加载。 类名:sun.misc.Launcher$ExtClassLoader
主要加载%JAVA_HOME%/jre/lib/ext
,此路径下的所有classes目录以及java.ext.dirs
系统变量指定的路径中类库。程序员也接触不到。
3.AppClassLoader Launcher$AppClassLoader (程序员使用的这个加载器)
由1.Bootstrap loader加载,但是它会将父类指定为2.ExtClassLoader
由上图可以看到继承关系,Bootstrap不存在,因为它是C++写的,负责加载能使Java运行的关键类。
AppClassLoader就是我们做项目,写代码时,所使用的加载类。
6.1
继续查看loadSpringFactories加载了什么类
此次是第一次加载,所以代码继续运行
在这里,要简单测试一下classLoader.getResources的方法
准备一个jar包,在META-INF 下 有spring.factories,内容如下
I.am.the.jar=\
jar.test.one,\
jar.test.two,\
jar.test.three
数据格式说一下 :
=\代表这是个根节点,这是一个父类,后面跟着的都是子类了
,\表示后面还有节点
只有类名,后面没有跟符号,说明当前父类已经结束
在项目下也有一个相同目录,相同的文件
内容如下:
we.are.class=\
class.one,\
class.two,\
class.three
有以下代码(结合运行结果看):
ClassLoader classLoader = ClassLoadTest.class.getClassLoader();
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
System.out.println(Arrays.toString(factoryImplementationNames));
}
}
运行:
所以,我们知道了加载配置文件的功能。
在SpringBoot中,通过加载配置文件,将它所需的类都给加载进来。
这些只是其中之一。
我们也可以实现自己的context类,filter类等等,让Springboot进行管理
6.2
继续查看loadSpringFactories加载了什么类
result.computeIfAbsent 用法,如果当前factoryTypeName不存在,则 key -> new ArrayList<>() 生成一个新的ArrayList做value, key 是 factoryTypeName
然后将子类放到list中去
add(factoryImplementationName.trim());
最后cache用classLoader,即AppClassLoader作为key,去存放spring.factories里的类信息
7.加载完spring.factories之后做了什么
由此我们可知
getSpringFactoriesInstances,就是获取当前class下,在spring.factories里有没有配置一些子类,如果配置了,则将这些子类实例化,放到springboot中。
8.具体放到springboot的操作
getBootstrapRegistryInitializersFromSpringFactories 获取到
Bootstrapper.class 和 BootstrapRegistryInitializer.class 两个配置子类实例(如果有的话)。
并且都放进了springboot的 bootstrapRegistryInitializers 参数中。
而 获取 ApplicationContextInitializer.class的配置子类实例放到 initializers 中
获取 ApplicationListener.class 的配置子类实例放到 listeners 中
9.判断入口类(运行主类,Springboot主类,main方法所在的类)
测试:
运行结果:
是谁调用的都可以看到。
而main方法在Springboot只存在一个,所以可以查到这个主入口类。
不过奇怪的是,主入口类里面的参数class,必须是当前类,为什么还要有这个判断。
就当留着一个小疑问。
至此,springboot的构造方法已经全部跑完。
下一篇,将讲解构造完之后的run方法。