前情提要:

本次使用2.5.2版本Springboot

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
    </parent>

1.一个普通的Springboot启动类

spring boot 自定义的类加载器 springboot类加载过程_java

 

2.进入SpringApplication.run方法查看

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_02

 

3.

先看SpringApplication 构造方法

 

spring boot 自定义的类加载器 springboot类加载过程_加载_03

 

spring boot 自定义的类加载器 springboot类加载过程_加载_04

 其中


primarySources 就是 第一张图里写的 Example.class


4.

查看上面第一个getBootstrapRegistryInitializersFromSpringFactories

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_05

spring boot 自定义的类加载器 springboot类加载过程_子类_06

 

spring boot 自定义的类加载器 springboot类加载过程_加载_07

5.

查看第2步Spring工程加载器加载类做了哪些操作

 SpringFactoriesLoader.loadFactoryNames

 

spring boot 自定义的类加载器 springboot类加载过程_加载_08

6.查看loadSpringFactories加载了什么类

spring boot 自定义的类加载器 springboot类加载过程_加载_09

 

看一看cache是啥

spring boot 自定义的类加载器 springboot类加载过程_spring_10

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_11

 简单讲一下classLoader

classLoader有三种:

1.Bootstrap loader

 用C++写的,加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定路径%JAVA_HOME%/jre/classes

程序员接触不到。

2.ExtClassLoader 

Bootstrp loader加载。 类名:sun.misc.Launcher$ExtClassLoader

主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。程序员也接触不到。

3.AppClassLoader Launcher$AppClassLoader (程序员使用的这个加载器)

spring boot 自定义的类加载器 springboot类加载过程_spring_12

由1.Bootstrap loader加载,但是它会将父类指定为2.ExtClassLoader 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_13

 由上图可以看到继承关系,Bootstrap不存在,因为它是C++写的,负责加载能使Java运行的关键类。

AppClassLoader就是我们做项目,写代码时,所使用的加载类。

6.1

继续查看loadSpringFactories加载了什么类

此次是第一次加载,所以代码继续运行

spring boot 自定义的类加载器 springboot类加载过程_子类_14

  

spring boot 自定义的类加载器 springboot类加载过程_加载_15

 在这里,要简单测试一下classLoader.getResources的方法

spring boot 自定义的类加载器 springboot类加载过程_spring_16

 准备一个jar包,在META-INF 下 有spring.factories,内容如下

I.am.the.jar=\
jar.test.one,\
jar.test.two,\
jar.test.three

数据格式说一下 :

=\代表这是个根节点,这是一个父类,后面跟着的都是子类了

,\表示后面还有节点

只有类名,后面没有跟符号,说明当前父类已经结束

在项目下也有一个相同目录,相同的文件

spring boot 自定义的类加载器 springboot类加载过程_spring boot_17

 内容如下:

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));
     }
  }

spring boot 自定义的类加载器 springboot类加载过程_spring boot_18

 运行:

spring boot 自定义的类加载器 springboot类加载过程_java_19

 所以,我们知道了加载配置文件的功能。

在SpringBoot中,通过加载配置文件,将它所需的类都给加载进来。

这些只是其中之一。

spring boot 自定义的类加载器 springboot类加载过程_spring boot_20

 

我们也可以实现自己的context类,filter类等等,让Springboot进行管理

spring boot 自定义的类加载器 springboot类加载过程_spring boot_21

 

spring boot 自定义的类加载器 springboot类加载过程_java_22

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_23

 

6.2

继续查看loadSpringFactories加载了什么类

spring boot 自定义的类加载器 springboot类加载过程_java_24


result.computeIfAbsent 用法,如果当前factoryTypeName不存在,则 key -> new ArrayList<>() 生成一个新的ArrayList做value, key 是 factoryTypeName


然后将子类放到list中去

add(factoryImplementationName.trim());

 

spring boot 自定义的类加载器 springboot类加载过程_java_25

最后cache用classLoader,即AppClassLoader作为key,去存放spring.factories里的类信息

 

 7.加载完spring.factories之后做了什么

spring boot 自定义的类加载器 springboot类加载过程_spring boot_26

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_27

 

spring boot 自定义的类加载器 springboot类加载过程_加载_28

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_29

 由此我们可知

spring boot 自定义的类加载器 springboot类加载过程_java_30

getSpringFactoriesInstances,就是获取当前class下,在spring.factories里有没有配置一些子类,如果配置了,则将这些子类实例化,放到springboot中。

8.具体放到springboot的操作

 

 

spring boot 自定义的类加载器 springboot类加载过程_spring boot_31

getBootstrapRegistryInitializersFromSpringFactories 获取到
Bootstrapper.classBootstrapRegistryInitializer.class 两个配置子类实例(如果有的话)。

spring boot 自定义的类加载器 springboot类加载过程_子类_32

bootstrapRegistryInitializers 参数中。

ApplicationContextInitializer.class的配置子类实例放到 initializers 中

spring boot 自定义的类加载器 springboot类加载过程_java_33

ApplicationListener.class 的配置子类实例放到  listeners 中

spring boot 自定义的类加载器 springboot类加载过程_子类_34

 

9.判断入口类(运行主类,Springboot主类,main方法所在的类)

spring boot 自定义的类加载器 springboot类加载过程_spring_35

 

spring boot 自定义的类加载器 springboot类加载过程_子类_36

测试:

spring boot 自定义的类加载器 springboot类加载过程_spring_37

 

spring boot 自定义的类加载器 springboot类加载过程_子类_38

 

spring boot 自定义的类加载器 springboot类加载过程_java_39

运行结果:

spring boot 自定义的类加载器 springboot类加载过程_java_40

 

是谁调用的都可以看到。

而main方法在Springboot只存在一个,所以可以查到这个主入口类。

不过奇怪的是,主入口类里面的参数class,必须是当前类,为什么还要有这个判断。

spring boot 自定义的类加载器 springboot类加载过程_加载_41

 就当留着一个小疑问。

至此,springboot的构造方法已经全部跑完。

下一篇,将讲解构造完之后的run方法。