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依赖即可。
网友评论