springboot源碼解析-管中窺豹系列之項目類型(二)

一、前言

  • Springboot源碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
  • 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot源碼管中窺豹系列。

 簡介

二、項目類型

這一節我們先討論一下springboot項目的怎麼自動載入applicationcontext實現類的。

  • 以前的spring的項目,都是xml載入bean,常用的都是XmlWebApplicationContext實現類
  • 後來出現註解的形式,基本用AnnotationConfigWebApplicationContext實現類
  • 後來又出現響應式編程reactive

那springboot用的是哪一種呢?

三、源碼解讀

先說結論:關於類型的選擇,springboot是根據class來判斷的。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

    ...

    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    ...

}
  • 我們從SpringApplication的構造函數開始看起
  • 構造函數裡面有段程式碼:是確定類型的

我們先看類型:

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";

static WebApplicationType deduceFromClasspath() {
    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) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}
  • 第一段if,如果存在reactive包的DispatcherServlet,同時不存在jersey和mvc的DispatcherHandler,就是REACTIVE
  • 第二段循環,不存在Servlet或者ConfigurableWebApplicationContext,就是none
  • 剩下的就是我們熟悉的SERVLET

類型確定之後,我們看SpringApplication的run方法:

public ConfigurableApplicationContext run(String... args) {
        
    ...

    try {
        ...

        context = createApplicationContext();
        
        ...
    } catch (Throwable ex) {
        
        ...

    }
    
    ...

    return context;
}

ApplicationContext實現類就是在createApplicationContext()裡面確定的

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根據類型載入不同的class:

  • 如果是SERVLET:AnnotationConfigServletWebServerApplicationContext
  • 如果是REACTIVE:
    AnnotationConfigReactiveWebServerApplicationContext
  • default:AnnotationConfigApplicationContext

至此,一目了然了,想要不同的項目類型,添加對應的jar包,springboot自動幫你選擇對應的ApplicationContext實現類

如果是普通的web項目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

如果是reactive項目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

如果是非web項目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

歡迎關注公眾號:豐極,更多技術學習分享。