spring是如何載入可用的ViewResolver的?

執著的慢行者發表於2017-10-28

Spring Web MVC 檢視解析

Spring web MVC 框架同其它web MVC 框架一樣,是請求驅動的(request driven),圍繞中心Servlet設計的。中心Servlet會分配請求到各個Controllers,以及提供其它功能。Spring的DispatcherServlet就是這種中心Servlet,但做的更多。所有MVC框架都提供定位檢視(address views)的機制,Spring提供view resolvers,讓你能夠在瀏覽器上渲染models,而不會把你束縛在某一特定的檢視技術上。有2個介面(Interface)對於Spring處理檢視來說是很重要的,一個是ViewResolver,另一個是View。 ViewResolver提供檢視名稱與實際檢視的對映關係,View介面定位請求準備和請求處理到檢視技術上。Spring規定,Controller中的所有handler方法,必須解析到一個邏輯檢視,可以是顯式的(通過返回String、View或ModelView)或者是隱式的(基於協商 based on conventions)。在Spring中,檢視由邏輯檢視名稱定位,然後由一個view resolver解析。

Spring自帶的檢視解析器

Spring自身帶有若干種view resolver,比如:AbstractCachingViewResolver、XmlViewResolver、ResourceBundleViewResolver、UrlBasedViewResolver、InternalResourceViewResolver、VelocityViewResolver、FreeMarkerViewResolver
、ContentNegotiatingViewResolver,你可以使用1種,或鏈式使用多種。使用方法就是在你的*-Servlet.xml配置檔案種加入相關的bean。舉例如下:

<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="order" value="1"/>
    <property name="location" value="/WEB-INF/views.xml"/>
</bean>

J2EE環境下檢視解析器的載入

那麼,問題是,Spring是如何從容器中感知到我們想要使用哪些ViewResoler的呢? 答案就在於DispatcherServlet類中的initViewResolvers方法。

DispatcherServlet.java中有一個私有方法,名為initViewResolvers,程式碼片段如下,就是用來初始化所有ViewResolvers的。

    /**
     * Initialize the ViewResolvers used by this class.
     * <p>If no ViewResolver beans are defined in the BeanFactory for this
     * namespace, we default to InternalResourceViewResolver.
     */
    private void initViewResolvers(ApplicationContext context) {
        this.viewResolvers = null;

        if (this.detectAllViewResolvers) {
            // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
            Map<String, ViewResolver> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
                // We keep ViewResolvers in sorted order.
                OrderComparator.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.
            }
        }

        // Ensure we have at least one ViewResolver, by registering
        // a default ViewResolver if no other resolvers are found.
        if (this.viewResolvers == null) {
            this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No ViewResolvers found in servlet `" + getServletName() + "`: using default");
            }
        }
    }

如果detectAllViewResolvers屬性被設定為true的話,就會觸發DispatcherServlet從ApplicationContext中載入所有基類為ViewResolver的Beans,然後存入viewResolvers列表。
(initViewResolvers會被確保在WebApplicationContext被初始化好了以後才會被呼叫的)

相關文章