Springboot源碼——應用程序上下文分析


  前兩篇(Spring MVC源碼——Root WebApplicationContext 和 Spring MVC源碼——Servlet WebApplicationContext)講述了springmvc項目創建上下文的過程,這一篇帶大家了解一下springboot項目創建上下文的過程。

SpringApplication引導類

SpringApplication類用於啟動或者引導springboot項目,直接應用在java main方法中。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //判斷當前web應用程序類型
    this.webApplicationType = deduceWebApplicationType();
    //找到*META-INF/spring.factories*中聲明的所有ApplicationContextInitializer的實現類並將其實例化
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    //找到*META-INF/spring.factories*中聲明的所有ApplicationListener的實現類並將其實例化
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //獲得當前執行main方法的類對象
    this.mainApplicationClass = deduceMainApplicationClass();
}

springboot項目WebApplicationType分為三種:非web類型,web類型(spring-mvc),響應式web類型(spring-webflux)

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.reactive.DispatcherHandler";

private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
        + "web.servlet.DispatcherServlet";

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

下面的run方法是springboot項目啟動的核心代碼。

public ConfigurableApplicationContext run(String... args) {
    //開啟任務執行時間監聽器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    //設置系統屬性『java.awt.headless』,為true則啟用headless模式支持
    configureHeadlessProperty();

    //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,
    //找到聲明的所有SpringApplicationRunListener的實現類並將其實例化,
    //之后逐個調用其started()方法,廣播SpringBoot要開始執行了。
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);

            //創建並配置當前SpringBoot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile),
        //並遍歷調用所有的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment准備完畢。                
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        //是否搜索BeanInfo類
        configureIgnoreBeanInfo(environment);
        //Banner打印
        Banner printedBanner = printBanner(environment);

        //根據WebApplicationType的值來決定創建何種類型的ApplicationContext對象
        context = createApplicationContext();

        //通過*SpringFactoriesLoader*檢索*META-INF/spring.factories*,獲取並實例化異常分析器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        //為ApplicationContext加載environment,之后逐個執行ApplicationContextInitializer的initialize()方法來進一步封裝ApplicationContext,
        //並調用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個空的contextPrepared()方法】,
        //之后初始化IoC容器,並調用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成,
        //這里就包括通過**@EnableAutoConfiguration**導入的各種自動配置類。
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);

        //初始化所有自動配置類,調用ApplicationContext的refresh()方法
        refreshContext(context);
        
        
        //空方法
        afterRefresh(context, applicationArguments);

        /關閉任務執行時間監聽器
        stopWatch.stop();

        //如果開啟日志,則打印執行是時間
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }

        //調用所有的SpringApplicationRunListener的started()方法,廣播SpringBoot已經完成了ApplicationContext初始化的全部過程。
        listeners.started(context);

        //遍歷所有注冊的ApplicationRunner和CommandLineRunner,並執行其run()方法。
        //我們可以實現自己的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啟動過程進行擴展。
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        //調用所有的SpringApplicationRunListener的running()方法,廣播SpringBoot已經可以處理服務請求了。
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

由上文可知,默認WebApplicationType是WebApplicationType.SERVLET,所以默認的上下文是AnnotationConfigServletWebServerApplicationContext。

//應用程序非web環境
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";

//應用程序web環境
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

//應用程序響應式web環境
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";


public enum WebApplicationType {

    //應用程序不需要任何應用服務器
    NONE,

    //應用程序內嵌web服務器
    SERVLET,

    //應用程序內嵌響應式web服務器
    REACTIVE

}

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_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);
}

AnnotationConfigServletWebServerApplicationContext類結構層次如下。

父類ServletWebServerApplicationContext創建內嵌web應用服務器如下。

@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        //創建web應用服務
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        //獲取ServletWebServerFactory類型的web服務器工廠類,比如TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context",
                    ex);
        }
    }
    initPropertySources();
}

Springmvc項目上下文和Springboot項目上下文淺析

Springmvc項目上下文

Springmvc項目的rootcontext的創建時通過 xml中 配置的org.springframework.web.context.ContextLoaderListener,其父類ContextLoader中有一個初始化上下文的方法,如下。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext);

上下文創建好之后調用

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

其中 servletContext的實例是 org.apache.catalina.core.ApplicationContext;

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = org.springframework.web.context.WebApplicationContext.ROOT)

這樣rootcontext就創建好了,並且放入了servletContext中保存。rootcontext獲取方式如下。 

WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

Springmvc項目中的DispatcherServlet是在xml中按照servlet格式配置的,這種方式創建的servlet實例沒有被spring容器管理。

DispatcherServlet實現了ApplicationContextAware接口,有一個成員變量來保存此servlet對應的上下文,如下。
/** WebApplicationContext for this servlet */
private WebApplicationContext webApplicationContext;

這種情況下webApplicationContext變量是無法注入的【DispatcherServlet實例沒有被spring容器管理】。看一下DispatcherServlet的父類FrameworkServlet是如何初始化上下文的,如下。

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    //springmvc項目這塊的判斷為false;springboot項目為ture。
    if (this.webApplicationContext != null) {

    ......

DispatcherServlet所屬上下文的存儲

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
     ...
     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
     ...
}

DispatcherServlet所屬上下文獲取org.springframework.web.servlet.support.RequestContextUtils。

Springboot項目上下文

Springboot項目中,調試代碼時發現DispatcherServlet的父類FrameworkServlet在初始化上下文的時候 rootcontext 和 DispatcherServlet成員變量 webApplicationContext保存的是一個實例,即AnnotationConfigServletWebServerApplicationContext實例。
上面也提到了DispatcherServlet【對應的實例被spring容器管理】實現了ApplicationContextAware接口,webApplicationContext保存的上下文是通過自動注入而來。
RootContext(AnnotationConfigServletWebServerApplicationContext)保存到servletcontext中的操作,如下。
//ServletWebServerApplicationContext
protected
void prepareWebApplicationContext(ServletContext servletContext) { ... servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); ... }

總結

經過三篇文章的分析,相信大家已經明白了Springmvc項目默認是有兩個上下文(Root webapplicationcontext 和 Servlet webapplicationcontext,對應的類型是XmlServletWebServerApplicationContext),而Springboot項目默認是一個上下文,對應的類型是AnnotationConfigServletWebServerApplicationContext。如果有什么疑問,請關注訂閱號,進行私聊。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM