我们来详细分析一下视图解析器 (ViewResolver) 的配置以及 Spring Boot 是如何自动配置它们的。
在 Spring MVC 中,当控制器 (Controller) 方法处理完请求并返回一个逻辑视图名 (String) 时,DispatcherServlet
会使用注册的 ViewResolver
来将这个逻辑视图名解析为一个实际的 View
对象。这个 View
对象负责渲染最终的响应(例如,生成 HTML)。
可以手动配置 ViewResolver
,在 Spring 的配置类(使用 @Configuration
注解)中完成。
核心接口是 org.springframework.web.servlet.ViewResolver
。
以下是一些常见的视图解析器及其手动配置示例:
a. InternalResourceViewResolver
(通常用于 JSP)
这是最常用的 JSP 视图解析器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; // 如果使用 JSTL
@Configuration
@EnableWebMvc // 如果不是 Spring Boot,通常需要这个
public class MvcConfig implements WebMvcConfigurer { // 实现 WebMvcConfigurer 以便自定义MVC配置
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class); // 如果使用JSTL标签库
resolver.setPrefix("/WEB-INF/jsp/"); // 视图文件的前缀路径
resolver.setSuffix(".jsp"); // 视图文件的后缀
resolver.setOrder(1); // 如果有多个解析器,设置顺序
return resolver;
}
// 可以配置其他 ViewResolver
// @Bean
// public ViewResolver thymeleafViewResolver() { ... }
}
prefix
: 视图文件在 Web 应用中的路径前缀。suffix
: 视图文件的扩展名。viewClass
: 指定要使用的视图类,例如 JstlView
(用于JSP + JSTL)。order
: 如果有多个视图解析器,order
属性决定了它们的查找顺序,值越小优先级越高。b. ThymeleafViewResolver
(用于 Thymeleaf 模板引擎)
需要先配置 SpringTemplateEngine
和 TemplateResolver
。
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring6.SpringTemplateEngine; // Spring 6, Spring 5 用 spring5
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
@Configuration
@EnableWebMvc
public class ThymeleafConfig implements WebMvcConfigurer {
private ApplicationContext applicationContext;
public ThymeleafConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("classpath:/templates/"); // Thymeleaf 模板通常放在 classpath 下
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false); // 开发时关闭缓存
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true); // 推荐开启SpringEL编译器
// 可以添加额外的 Dialect,例如 SpringSecurityDialect
return templateEngine;
}
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(0); // 优先级高
return resolver;
}
}
c. FreeMarkerViewResolver
(用于 FreeMarker 模板引擎)
需要配置 FreeMarkerConfigurer
。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
@Configuration
@EnableWebMvc
public class FreeMarkerConfig implements WebMvcConfigurer {
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates/freemarker/"); // FreeMarker模板路径
configurer.setDefaultEncoding("UTF-8");
return configurer;
}
@Bean
public ViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(true); // 生产环境建议开启缓存
resolver.setPrefix(""); // 前缀通常在 FreeMarkerConfigurer 中设置
resolver.setSuffix(".ftl");
resolver.setContentType("text/html;charset=UTF-8");
resolver.setOrder(0);
return resolver;
}
}
d. ContentNegotiatingViewResolver
这是一个特殊的视图解析器,它本身不解析视图,而是委托给一个或多个其他的视图解析器。它会根据请求的媒体类型 (Media Type,例如通过 Accept
HTTP头或URL后缀) 来选择合适的 ViewResolver
(进而选择合适的 View
) 来渲染响应。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
// ... 其他 ViewResolver imports
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableWebMvc
public class ContentNegotiationMvcConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorParameter(true) // 是否通过请求参数(默认为format)来确定媒体类型
.parameterName("mediaType") // 请求参数名
.ignoreAcceptHeader(false) // 是否忽略Accept请求头
.defaultContentType(MediaType.TEXT_HTML) // 默认媒体类型
.mediaType("html", MediaType.TEXT_HTML)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
List<ViewResolver> resolvers = new ArrayList<>();
// 添加你想要委托的 ViewResolver
resolvers.add(jsonViewResolver()); // 假设有一个处理 JSON 的 ViewResolver
resolvers.add(jspViewResolver()); // 上面定义的 JSP ViewResolver
// ... 其他
resolver.setViewResolvers(resolvers);
resolver.setOrder(0); // CNVR 通常优先级最高
return resolver;
}
// 示例:一个简单的 JSON ViewResolver (通常会使用 MappingJackson2JsonView)
// @Bean
// public ViewResolver jsonViewResolver() { ... }
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
}
Spring Boot 的核心思想是“约定优于配置” (Convention over Configuration)。它通过自动配置 (Auto-configuration) 机制,根据我们项目中添加的依赖来自动配置应用程序的各个方面,包括视图解析器。
自动配置的触发条件:
@ConditionalOnClass
/ @ConditionalOnMissingBean
:自动配置类通常使用这些注解。
@ConditionalOnClass
: 只有当指定的类存在于类路径上时,配置才会生效。@ConditionalOnMissingBean
: 只有当用户没有自己定义同类型的 Bean 时,Spring Boot 的自动配置 Bean 才会生效。常见视图解析器的自动配置:
Spring Boot 为多种模板引擎提供了自动配置支持。这些配置通常在 spring-boot-autoconfigure.jar
中的 org.springframework.boot.autoconfigure.web.servlet
(针对Servlet Web) 或 org.springframework.boot.autoconfigure.web.reactive
(针对Reactive WebFlux) 包下。
a. Thymeleaf (spring-boot-starter-thymeleaf
)
spring-boot-starter-thymeleaf
依赖时,相关的 Thymeleaf 类会被引入。ThymeleafAutoConfiguration
SpringResourceTemplateResolver
、SpringTemplateEngine
和 ThymeleafViewResolver
。classpath:/templates/
.html
UTF-8
application.properties
或 application.yml
修改这些默认值:spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=true # 生产环境建议 true,开发环境 false
b. FreeMarker (spring-boot-starter-freemarker
)
spring-boot-starter-freemarker
。FreeMarkerAutoConfiguration
FreeMarkerConfigurer
和 FreeMarkerViewResolver
。classpath:/templates/
(注意,FreeMarker 传统上有一个自己的路径,但 Spring Boot 会配置 FreeMarkerConfigurer
的 templateLoaderPath
为 spring.freemarker.template-loader-path
的值,默认为 classpath:/templates/
)。.ftlh
(FreeMarker Template Language HTML,也可以是 .ftl
)application.properties
或 application.yml
修改:spring.freemarker.template-loader-path=classpath:/templates/freemarker/
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.cache=true
# 更多配置...
c. Groovy Templates (spring-boot-starter-groovy-templates
)
spring-boot-starter-groovy-templates
。GroovyTemplateAutoConfiguration
GroovyMarkupConfigurer
和 GroovyMarkupViewResolver
。classpath:/templates/
.tpl
application.properties
或 application.yml
修改:spring.groovy.template.prefix=classpath:/templates/
spring.groovy.template.suffix=.tpl
# 更多配置...
d. JSP (特殊情况)
tomcat-embed-jasper
是必需的,它通常由 spring-boot-starter-web
间接引入。WebMvcAutoConfiguration
内部有一个内部类 WebMvcAutoConfigurationAdapter
,它会尝试配置 InternalResourceViewResolver
。javax.servlet.jsp.JspPage
(或 Jakarta EE 9+ 的 jakarta.servlet.jsp.JspPage
) 在类路径上,并且没有其他更专门的视图解析器(如 Thymeleaf 的)被配置,它可能会配置一个 InternalResourceViewResolver
。application.properties
或 application.yml
修改(如果 InternalResourceViewResolver
被自动配置):spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
但通常,如果使用了像 Thymeleaf 这样的模板引擎,Spring Boot 会优先配置它们的 ViewResolver,而 InternalResourceViewResolver
的自动配置优先级较低。e. ContentNegotiatingViewResolver
(自动配置)
ContentNegotiatingViewResolver
(WebMvcAutoConfiguration
的一部分)。ViewResolver
。Accept
头)返回不同格式的响应(HTML, JSON, XML 等),而控制器代码无需改变。f. BeanNameViewResolver
(自动配置)
BeanNameViewResolver
。View
bean 的名称匹配,则会使用该 View
bean。它的 order
值较高(优先级较低),通常在 InternalResourceViewResolver
之后。g. WelcomePageHandlerMapping
和静态资源
ViewResolver
,但 Spring Boot 也会自动配置对 index.html
作为欢迎页面的支持 (WelcomePageHandlerMapping
) 以及从 classpath:/static/
, classpath:/public/
, classpath:/resources/
, classpath:/META-INF/resources/
等位置提供静态资源的服务。如何覆盖或禁用自动配置?
ThymeleafViewResolver
Bean),那么 Spring Boot 的自动配置版本将不会生效 (因为 @ConditionalOnMissingBean
)。application.properties
:对于大多数自动配置的属性,我们可以通过 application.properties
或 application.yml
来覆盖默认值。@SpringBootApplication
(或 @EnableAutoConfiguration
) 的 exclude
属性:@SpringBootApplication(exclude = ThymeleafAutoConfiguration.class)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
总结: