Springboot启动流程(新手向)1:初步加载所有类

前情提要:

本次使用2.5.2版本Springboot

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

1.一个普通的Springboot启动类

Springboot启动流程(新手向)1:初步加载所有类

 

2.进入SpringApplication.run方法查看

 Springboot启动流程(新手向)1:初步加载所有类

 

3.

先看SpringApplication 构造方法Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 

Springboot启动流程(新手向)1:初步加载所有类

 其中

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

4.

查看上面第一个getBootstrapRegistryInitializersFromSpringFactories

 Springboot启动流程(新手向)1:初步加载所有类

Springboot启动流程(新手向)1:初步加载所有类 Springboot启动流程(新手向)1:初步加载所有类

5.

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

 SpringFactoriesLoader.loadFactoryNames

 Springboot启动流程(新手向)1:初步加载所有类

6.查看loadSpringFactories加载了什么类

Springboot启动流程(新手向)1:初步加载所有类 

看一看cache是啥

Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 简单讲一下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 (程序员使用的这个加载器)

Springboot启动流程(新手向)1:初步加载所有类由1.Bootstrap loader加载,但是它会将父类指定为2.ExtClassLoader 

Springboot启动流程(新手向)1:初步加载所有类

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

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

6.1

继续查看loadSpringFactories加载了什么类

 Springboot启动流程(新手向)1:初步加载所有类

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

Springboot启动流程(新手向)1:初步加载所有类 

 Springboot启动流程(新手向)1:初步加载所有类

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

Springboot启动流程(新手向)1:初步加载所有类

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

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

数据格式说一下 :

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

,\表示后面还有节点

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

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

Springboot启动流程(新手向)1:初步加载所有类

 内容如下:

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启动流程(新手向)1:初步加载所有类

 运行:

Springboot启动流程(新手向)1:初步加载所有类

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

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

这些只是其中之一。

Springboot启动流程(新手向)1:初步加载所有类

 

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

Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 

6.2

继续查看loadSpringFactories加载了什么类

Springboot启动流程(新手向)1:初步加载所有类

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

然后将子类放到list中去

add(factoryImplementationName.trim());

 Springboot启动流程(新手向)1:初步加载所有类

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

 

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

Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 由此我们可知

Springboot启动流程(新手向)1:初步加载所有类

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

8.具体放到springboot的操作

 

 Springboot启动流程(新手向)1:初步加载所有类

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

Springboot启动流程(新手向)1:初步加载所有类

 并且都放进了springboot的 bootstrapRegistryInitializers 参数中。

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

Springboot启动流程(新手向)1:初步加载所有类

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

Springboot启动流程(新手向)1:初步加载所有类

 

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

Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

测试:

Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

 Springboot启动流程(新手向)1:初步加载所有类

运行结果:

Springboot启动流程(新手向)1:初步加载所有类 

是谁调用的都可以看到。

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

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

Springboot启动流程(新手向)1:初步加载所有类

 就当留着一个小疑问。

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

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

 

 

 

 

 

 

 

 

 

 

 

上一篇:4.再谈类的加载器


下一篇:Java中的类加载器ClassLoader