(1)SpringBoot源码: 启动类型推断

2020-05-04  本文已影响0人  逆风踏雷

1.启动类的调用链。

典型的SpringBoot启动代码如下:

...
 public static void main(String[] args) throws InterruptedException {
        SpringApplication.run(ApplicationMain.class, args);
        //保证当前线程不会结束
        Thread.currentThread().join();
    }
...

其实看SpringApplication 的类注释,为了在启动之前定制化SpringBoot的配置,也可以有如下写法:

  public static void main(String[] args) {
    SpringApplication application = new SpringApplication(MyApplication.class);
    // ... customize application settings here
    application.run(args)
 }

那么问题来了,一种是静态方法,一种是构造方法,两种启动方式在原理上有啥区别吗?答案是: 没有,静态方法底层调用了构造方法,之所以常用静态方法,我推测是因为代码简洁。代码如下:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
                //看到了吧,就是这里用构造方法创建的,那构造方法都干了啥呢?
        return new SpringApplication(primarySources).run(args);
    }

2.SpringApplication的构造

先上代码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
                 //设置类加载器,默认为null,使用系统加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
                //LinkedHashSet 可以保证元素唯一性以及插入顺序,这里是为了缓存我们自己指定的启动主类。
                //TODO:为啥非要保证插入顺序?
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//重点来了,这里可以推断出启动类型。
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

deduce意思是推断,进到这个方法里面:WebApplicationType.deduceFromClasspath();

static WebApplicationType deduceFromClasspath() {
//这里在尝试加载:DispatcherHandler,DispatcherServlet,ServletContainer。如果加载到了handler,但是没有加载到DispatcherServlet和jersey容器,就会判断是一个REACTIVE web工程(响应式)。
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
              //这里试图加载:Servlet,ConfigurableWebApplicationContext,如果都没加载到那么判定为非web工程。
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
  //否则就是一个web工程
        return WebApplicationType.SERVLET;
    }

综上,SpringBoot通过判断一些经典web类是否被加载来推断工程类型,因此如果不想使用SpringBoot的web功能,只需要不引入springboot-web依赖即可。

上一篇 下一篇

猜你喜欢

热点阅读