springboot启动流程分析(二)

现在继续看启动过程的详情,详细描述下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
【未完待续】

猜你喜欢

转载自blog.csdn.net/zhaole524/article/details/80862319