springboot mvc自動配置(三)初始化mvc的組件

全部文章

http://www.javashuo.com/article/p-mbkpimnl-cg.htmlhtml

 

正文

springboot mvc自動配置的時候,得到了DispatcherServlet和DispatcherServletRegistrationBean。DispatcherServletRegistrationBean將DispatcherServlet給註冊到了ServletContext當中。web

註冊到ServletContext中的Servlet將會觸發其init方法,從而進行Servlet的初始化。本文將從Servlet的init方法開始,看看觸發MVC各個組件初始化的代碼spring

 

DispatcherServlet類圖

咱們先看看DispatcherServlet的類圖springboot

根據類圖能夠看到兩部分的設計,第一部分是Servlet到HttpServlet,也就是Servlet容器相關的內部設計。第二部分是Spring在HttpServlet的基礎上擴展了框架相關的內容,而最終DispatcherServlet將擴展springMVC的內容。mvc

 

GenericServlet

咱們跟進GenericServlet的init方法,看看它的實現app

@Override
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

繼續跟進init方法框架

public void init() throws ServletException {
    // NOOP by default
}

沒有實現邏輯,供子類去選擇實現ide

 

HttpServletBean

httpServlet麼有實現init方法,由HttpServletBean這個spring實現的類來擴展。跟進HttpServletBean的init方法ui

@Override
public final void init() throws ServletException {

    // Set bean properties from init parameters.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // Let subclasses do whatever initialization they like.
    initServletBean();
}

HttpServletBean也是擴展了一個initServletBean接口來供子類實現this

 

FrameworkServlet

跟進FrameworkServlet的initServletBean的方法

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
    if (logger.isInfoEnabled()) {
        logger.info("Initializing Servlet '" + getServletName() + "'");
    }
    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        throw ex;
    }

    // ...
}

initFrameworkServlet是一個空實現,核心邏輯落到了initWebApplicationContext中,跟進它

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) {
        //...
    }
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        synchronized (this.onRefreshMonitor) {
            onRefresh(wac);
        }
    }

    if (this.publishContext) {
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

Springboot在啓動過程當中建立了ApplicationContext,這裏將公用同一個ApplicationContext。

onRefresh方法提供了一個空實現,供子類去作初始化實現

protected void onRefresh(ApplicationContext context) {
    // For subclasses: do nothing by default.
}

 

DispatcherServlet

跟進DispatcherServlet的onRefresh方法

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

繼續跟進initStrategies

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

咱們看到,initStrategies包含了各類MVC組件的初始化方法

 

組件初始化

咱們打開initMultipartResolver看看

private void initMultipartResolver(ApplicationContext context) {
    try {
        this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
        // ...
    catch (NoSuchBeanDefinitionException ex) {
        // Default is no multipart resolver.
        this.multipartResolver = null;
        // ...
    }
}

其實就是從Bean工廠當中獲取對應的Bean對象。multipartResolver默承認能爲空

 

再打開initViewResolvers看看

private void initViewResolvers(ApplicationContext context) {
    this.viewResolvers = null;

    if (this.detectAllViewResolvers) {
        Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.viewResolvers = new ArrayList<>(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.viewResolvers);
        }
    } else {
        try {
            ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
            this.viewResolvers = Collections.singletonList(vr);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default ViewResolver later.
        }
    }

    if (this.viewResolvers == null) {
        this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
    }
}

同樣的是從Bean工廠當中獲取Bean,只不過這裏是獲取到一個集合,且若是沒有將會獲取默認的策略。

 

其它的MVC組件初始化相似,這裏不每一個打開看了。

 

總結

DispatcherServlet做爲一個Servlet的實現,在Servlet被調用init方法之後最終將會調用DispatcherServlet的initStrategies方法,該方法將會初始化各個組件。

初始化組件基本就是把各個Bean對象從BeanFactory中拿出來組合到DispatcherServlet中,供後續使用。

相關文章
相關標籤/搜索