天天看點

Spring MVC 之 ViewResolver

Spring MVC 之 ViewResolver

Spring MVC的視圖解析器 ViewResolver 是架構中一個重要的元件,用于将控制器傳回的邏輯視圖名稱解析為具體的視圖實作對象,最終呈現給使用者的是具體的視圖實作,例如 JSP、FreeMarker 模闆、Thymeleaf 模闆、JSON 等等。本文将介紹 Spring MVC 的視圖解析器的作用、類型、以及源碼實作。

作用

在 Spring MVC 中,控制器處理完請求之後需要将生成的模型資料和視圖名稱傳回給 DispatcherServlet,DispatcherServlet 會将模型資料和視圖名稱交給 ViewResolver 進行解析,ViewResolver 将對應的視圖解析出來,并傳回給 DispatcherServlet,由 DispatcherServlet 進行渲染,最終将渲染後的結果傳回給用戶端。是以,ViewResolver 的作用是将控制器傳回的邏輯視圖名稱解析為具體的視圖實作對象。

類型

Spring MVC 中提供了多種 ViewResolver 類型,不同類型的 ViewResolver 會使用不同的解析政策和算法,下面介紹幾種常見的 ViewResolver 類型。

InternalResourceViewResolver

InternalResourceViewResolver 是 Spring MVC 中最常用的視圖解析器,它用于解析 JS P或 HTML 等資源檔案。該解析器會将邏輯視圖名稱加上字首和字尾,例如将邏輯視圖名稱 “hello” 解析為 “/WEB-INF/views/hello.jsp”。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig {

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}
複制代碼           

FreeMarkerViewResolver

FreeMarkerViewResolver 用于解析FreeMarker 模闆,它會将邏輯視圖名稱加上字首和字尾,例如将邏輯視圖名稱 “hello” 解析為 “/WEB-INF/views/hello.ftl”。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig {

    @Bean
    public FreeMarkerViewResolver viewResolver() {
        FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".ftl");
        return viewResolver;
    }
}
複制代碼           

TilesViewResolver

TilesViewResolver 用于解析 Tiles 布局,它會将邏輯視圖名稱解析為 Tiles 布局,并傳回給 DispatcherServlet 進行渲染。Tiles 是一個基于模闆的布局架構,可以将頁面分成多個部分,每個部分都是一個獨立的模闆,最終組合成一個完整的頁面。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig {

    @Bean
    public TilesConfigurer tilesConfigurer() {
        TilesConfigurer tilesConfigurer = new TilesConfigurer();
        tilesConfigurer.setDefinitions(new String[] { "/WEB-INF/tiles.xml" });
        return tilesConfigurer;
    }

    @Bean
    public TilesViewResolver viewResolver() {
        TilesViewResolver viewResolver = new TilesViewResolver();
        return viewResolver;
    }
}
複制代碼           

ContentNegotiatingViewResolver

ContentNegotiatingViewResolver 是一個複合視圖解析器,它可以根據請求的 Accept 頭資訊來判斷用戶端需要的資料類型,并選擇對應的視圖解析器進行解析。例如用戶端請求的 Accept 頭資訊為 “application/json”,則選擇使用 MappingJackson2JsonView 解析器将模型資料渲染成 JSON 格式傳回給用戶端。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.controller")
public class AppConfig {

    @Bean
    public ContentNegotiatingViewResolver viewResolver() {
        ContentNegotiatingViewResolver viewResolver = new ContentNegotiatingViewResolver();
        List<ViewResolver> viewResolvers = new ArrayList<>();
        viewResolvers.add(jsonViewResolver());
        viewResolver.setViewResolvers(viewResolvers);
        return viewResolver;
    }

    @Bean
    public MappingJackson2JsonViewResolver jsonViewResolver() {
        MappingJackson2JsonViewResolver jsonViewResolver = new MappingJackson2JsonViewResolver();
        return jsonViewResolver;
    }
}

複制代碼           

源碼實作

Spring MVC 中的視圖解析器是通過 ViewResolver 接口來實作的,該接口定義了兩個方法:

public interface ViewResolver {
    View resolveViewName(String viewName, Locale locale) throws Exception;
    String REDIRECT_URL_PREFIX = "redirect:";
    String FORWARD_URL_PREFIX = "forward:";
}
複制代碼           

其中,resolveViewName 方法接收一個邏輯視圖名稱和一個 Locale 對象作為參數,傳回一個 View 對象。如果找不到對應的 View 對象,則傳回 null。

對于 InternalResourceViewResolver 而言,它的 resolveViewName 方法實作如下:

@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
    String prefix = getPrefix();
    String suffix = getSuffix();
    if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
        String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
        RedirectView redirectView = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
        String[] hosts = StringUtils.toStringArray(getRedirectHosts());
        if (hosts.length > 0) {
            redirectView.setHosts(hosts);
        }
        return redirectView;
    }
    if (viewName.startsWith(FORWARD_URL_PREFIX)) {
        String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
        InternalResourceView forwardView = new InternalResourceView(forwardUrl);
        forwardView.setApplicationContext(getApplicationContext());
        forwardView.setServletContext(getServletContext());
        forwardView.setAttributesMap(getAttributesMap());
        return forwardView;
    }
    if (!viewName.startsWith(prefix) && !viewName.endsWith(suffix)) {
        viewName = prefix + viewName + suffix;
    }
    return buildView(viewName);
}
複制代碼           

在上面的代碼中 首先判斷邏輯視圖名稱是否以 redirect: 或 forward: 開頭,如果是就傳回 RedirectView 或 InternalResourceView 對象。

如果不是,則根據 prefix 和 suffix 屬性将邏輯視圖名稱轉換為實體視圖名稱。

總結

Spring MVC 的視圖解析器 ViewResolver 是一個重要的元件,它将控制器傳回的邏輯視圖名稱解析為具體的視圖實作對象,最終呈現給使用者的是具體的視圖實作。

Spring MVC提供了多種 ViewResolver 類型,不同類型的ViewResolver會使用不同的解析政策和算法,例如 InternalResourceViewResolver 用于解析JSP或HTML等資源檔案,FreeMarkerViewResolver 用于解析 FreeMarker 模闆,TilesViewResolver 用于解析 Tiles 布局,ContentNegotiatingViewResolver 則是一個複合視圖解析器,可以根據請求的 Accept 頭資訊來選擇對應的視圖解析器進行解析。

ViewResolver 的源碼實作遵循了 ViewResolver 接口的規範,即将邏輯視圖名稱解析為具體的視圖實作對象。

繼續閱讀