SampleWebServicesApplication
最近在研究Spring,先看一個簡單的例子。
@SpringBootApplication
public class SampleWebServicesApplication {
public static void main(String[] args) {
SpringApplication.run(SampleWebServicesApplication.class, args);
}
}
SpringApplication源碼
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);
}
啓動程序首先初始化了一個SpringApplication對象。來看一看在它的構造器了發生了什麼。
private ResourceLoader resourceLoader;
private Set<Class<?>> primarySources;
private WebApplicationType webApplicationType;
private Class<?> mainApplicationClass;
private List<ApplicationContextInitializer<?>> initializers;
private List<ApplicationListener<?>> listeners;
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
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英文翻譯爲:推斷。
web環境檢測:WebApplicationType分析
先來看SpringBoot支持那些Web類型:
ClassUtils.isPresent(String className, ClassLoader classLoader)
方法並不是簡簡單單看看classLoader之中有沒有出現過傳參的class。它的底層調用的是java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)。這個方法會加載你指定的className到jvm,如果給回你要的class給你,常常在反射中使用。所以
public enum WebApplicationType {
/**
* 不啓動內嵌的WebServer,不是運行web application
*/
NONE,
/**
* 啓動內嵌的基於servlet的web server
*/
SERVLET,
/**
* 啓動內嵌的reactive web server,這個application是一個reactive web application
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
// 嘗試加載org.springframework.web.reactive.DispatcherHandler,如果成功並且加載org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer失敗,則這個application是WebApplicationType.REACTIVE類型。
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
// 如果ClassLoader裏面同時加載這兩個 javax.servlet.Servlet和 org.springframework.web.context.ConfigurableWebApplicationContext成功。則application是WebApplicationType.NONE 類型。
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// application是 WebApplicationType.SERVLET 類型。
return WebApplicationType.SERVLET;
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
//省略其他代碼
這個例子中,加載org.springframework.web.reactive.DispatcherHandler拋出異常。
異常如下:
而org.springframework.util.ClassUtils#isPresent(String className, @Nullable ClassLoader classLoader) 的實現如下:
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}
加載失敗則return false,那麼怎麼加載失敗呢?
這個deduceFromClasspath()
方法中最重要的功能 是根據 java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader) 的加載class功能,而class加載是依賴jar包是否引起而判斷的,所以如果引入了javax.servlet.Servlet的jar,則會啓動Servlet模式,如果引入的jar是spring-boot-starter-webflux,而且沒引入servlet相關的jar,則會啓動Reactive模式。
這也是Spring Boot的設計思想:拔插而決定application的啓動方式。
更多精彩內容歡迎關注我的微信公衆號: