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 接口的标准,即将逻辑视图称号解析为具体的视图完成目标。