《Spring 5 官方文档》18. Web MVC 框架

原文链接 译者:dan

18.1  Spring Web MVC 框架的介绍

Spring Web模型视图控制器(MVC)框架是围绕一个DispatcherServlet设计的,它将请求分派给处理程序,具有可配置的处理程序映射,视图解析,区域设置,本地化和主题解析,并且支持上传文件。默认的处理是基于注解@Controller@RequestMapping,提供一系列灵活的处理方法。随着Spring 3.0的推出,通过@PathVariable或者其他注解,@Controller 机制开始允许你去创建 Rest风格的web站点和应用。

在Spring Web MVC 和 Spring中一条关键的准则是“对扩展开放,对修改关闭”

在Spring Web MVC中一些核心类的方法被标注为final,由于开发者不能用自已的方法去覆盖这些方法,这并不是任意的,而是特别考虑到这个原则。

对于这个准则的解释,请参考Seth Ladd的Expert Spring Web MVC和Web Flow; 具体参见第一版第117页的“A Look At Design”一节。 或者参见

Bob Martin, The Open-Closed Principle (PDF)

当你使用Spring MVC时,你不能在final方法增加切面。例如,你不能在AbstractController.setSynchronizeOnSession()增加切面,有关AOP 代理的更多信息以及为什么不能再Final方法增加切面,查看第7.6.1节“了解AOP代理”

在Spring Web MVC中,您可以使用任何对象作为命令或表单支持对象;你不需要实现一个特别架构接口或者基类。Spring数据绑定非常灵活:例如,你可以使用程序将类型不匹配当作验证错误而不是系统错误。 因此,您不需要将您的业务对象的属性复制为简单的无格式的字符串,仅用于处理无效提交,或者正确转换字符串。 相反,通常最好直接绑定到您的业务对象。

Spring 的视图处理也是相当灵活,控制器通常负责准备具有数据和选择视图名称的模型映射,但它也可以直接写入响应流并完成请求。视图名称解析可通过文件扩展或Accept标头内容类型协商进行高度配置,通过bean名称,属性文件或甚至自定义的ViewResolver实现。模型(MVC中的M)是一个Map接口,可以完全提取视图技术,你可以直接与基于模板的渲染技术(如JSP和FreeMarker)集成,或直接生成XML,JSON,Atom和许多其他类型的内容。 模型Map可以简单地转换成适当的格式,如JSP请求属性或FreeMarker模板模型。

18.1.1 Spring Web MVC的特点

 

Spring Web 流程

Spring Web 流程 (SWF)的目的是成为最好的Web页面应用流程管理方案,SWF与Servlet 和Portlet 环境中的Spring MVC和JSF等现有框架集成。如果你有一个这样的业务流程,使用会话模型比纯粹的请求要优,那么SWF可能是一个选择。

SWF允许您将逻辑页面流作为在不同情况下可重用的自包含模块捕获,因此非常适合构建引导用户通过驱动业务流程的受控导航的Web应用程序模块。

更多关于SWF的信息,请点击Spring Web Flow website.

Spring 的Web模块包含许多独特的web支持特性:

  • 明确并分离的角色.每个角色-控制器,验证器,命令对象,构建对象,模型对象,分发器,映射处理器,视图解析等等都是完全的一个特定对象
  • 框架和应用程序类作为JavaBeans的强大而直接的配置。 此配置功能包括跨上下文的简单引用,例如从Web控制器到业务对象和验证器。
  • 可适配,无入侵,灵活,定义您需要的任何控制器方法签名,可能使用给定方案的参数注释之一(例如@RequestParam,@RequestHeader,@PathVariable等)。
  • 可重用的业务代码,不需要重复,使用现有的业务对象作为命令或表单对象,而不是仿照它们来扩展特定的框架基类。
  • 自定义绑定和验证,类型不匹配作为应用程序级验证错误,保持违规值,本地化日期和数字绑定等,而不是只使用仅包含字符串的表单对象进行手动解析和转换为业务对象。
  • 自定义的处理程序映射和视图解析,从简单的URL配置策略到复杂的,特制的策略,Spring比Web MVC框架更灵活,这些框架需要特定的技术。
  • 灵活的模型转换,具有名称/值的模型传输Map支持与任何视图技术的轻松集成。
  • 本地,时区,主题自定义,支持具有或不具有Spring标签库的JSP,支持JSTL,支持FreeMarker而不需要额外的网桥等等。
  • 一个简单而强大的JSP标签库,被称为Spring标签库,为数据绑定和主题等功能提供支持。 自定义标签允许在标记代码方面具有最大的灵活性。 有关标签库描述符的信息,请参见附录Chapter 40, spring JSP Tag Library
  • 在Spring 2.0中引入的JSP表单标签库,使得在JSP页面中的写入表单更容易。 有关标签库描述符的信息,请参见附录Chapter 41, spring-form JSP Tag Library
  • Bean的生命周期范围限定在当前的HTTP请求或HTTP Session中。 这不是Spring MVC本身的一个特定功能,而是Spring MVC使用的WebApplicationContext容器。 这些bean范围在Section 3.5.4, “Request, session, application, and WebSocket scopes”

 

18.1.2 其他MVC实现的可插拔性

对于某些项目,非Spring MVC实现更为可取。许多团队希望利用他们现有的技能和工具投资,例如使用JSF。

如果您不想使用Spring的Web MVC,但打算利用Spring提供的其他解决方案,您可以轻松地将您选择的Web MVC框架与Spring集成。通过其ContextLoaderListener简单地启动一个Spring根应用程序上下文,并通过任何动作对象中的ServletContext属性(或Spring的各自的帮助方法)访问它。没有涉及“插件”,因此不需要专门的集成。从Web层的角度来看,您只需使用Spring作为库,将根应用程序上下文实例作为入口点。

即使没有Spring的Web MVC,您的注册bean和Spring的服务也可以在您的指尖。在这种情况下,Spring不会与其他Web框架竞争。它简单地解决了纯Web MVC框架从bean配置到数据访问和事务处理的许多方面。所以您可以使用Spring中间层和/或数据访问层来丰富您的应用程序,即使您只想使用JDBC或Hibernate的事务抽象。

18.2 分发

Spring的Web MVC框架与许多其他Web MVC框架一样,以请求为驱动,围绕一个中央Servlet设计,将请求发送给控制器,并提供了其他促进Web应用程序开发的功能。然而, Spring 的DispatcherServlet 做得更多.它和 Spring IoC 容器整合一起,它允许你使用Spring 每个特性.

Spring Web MVC DispatcherServlet的请求处理工作流程如下图所示。 对设计模式熟悉的读者将会认识到,DispatcherServlet是“前端控制器”设计模式的表达(这是Spring Web MVC与许多其他领先的Web框架共享的模式)。

下图,在Spring Web MVC 中请求处理流程

mvc

DispatcherServlet是一个实际的Servlet(它继承自HttpServlet基类),因此在Web应用程序中被声明。 您需要使用URL映射来映射要DispatcherServlet处理的请求。 以下是Servlet 3.0+环境中的标准Java EE Servlet配置:


 public class MyWebApplicationInitializer implements WebApplicationInitializer {
 @Override
 public void onStartup(ServletContext container) {
 ServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet());
 registration.setLoadOnStartup(1);
 registration.addMapping("/example/*");
 }
 }
 

 

在前面的示例中,以/ example开头的所有请求都将由名为Example的DispatcherServlet实例处理。

WebApplicationInitializer是由Spring MVC提供的接口,可确保您的基于代码的配置被检测并自动用于初始化任何Servlet 3容器。这个名为AbstractAnnotationConfigDispatcherServletInitializer的接口的抽象基类实现通过简单地指定其servlet映射和列出配置类来更容易地注册DispatcherServlet,甚至建议您设置Spring MVC应用程序。有关更多详细信息,请参阅基于代码的Servlet容器初始化。

DispatcherServlet是一个实际的Servlet(它继承自HttpServlet基类),因此在Web应用程序的web.xml中声明。您需要通过使用同一web.xml文件中的URL映射来映射要DispatcherServlet处理的请求。这是标准的Java EE Servlet配置;以下示例显示了这样的DispatcherServlet声明和映射:

<web-app>
	<servlet>
		<servlet-name>example</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>example</servlet-name>
		<url-pattern>/example/*</url-pattern>
	</servlet-mapping>

</web-app>


如第3.15节“ApplicationContext的附加功能”中所述,Spring中的ApplicationContext实例可以被限定。 在Web MVC框架中,每个DispatcherServlet都有自己的WebApplicationContext,它继承了已经在根WebApplicationContext中定义的所有bean。 根WebApplicationContext应该包含应该在其他上下文和Servlet实例之间共享的所有基础架构bean。 这些继承的bean可以在特定于servlet的范围内被覆盖,您可以在给定的Servlet实例本地定义新的范围特定的bean。

18.2. Spring Web MVC中的典型上下文层次结构

mvc context hierarchy

在初始化DispatcherServlet时,Spring MVC将在Web应用程序的WEB-INF目录中查找名为[servlet-name] -servlet.xml的文件,并创建在那里定义的bean,覆盖使用相同名称定义的任何bean的定义 在全球范围内。 请考虑以下DispatcherServlet Servlet配置(在web.xml文件中):

<web-app>
	<servlet>
		<servlet-name>golfing</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>golfing</servlet-name>
		<url-pattern>/golfing/*</url-pattern>
	</servlet-mapping>
</web-app>

使用上述Servlet配置,您将需要在应用程序中有一个名为/WEB-INF/golfing-servlet.xml的文件; 该文件将包含您所有的Spring Web MVC特定组件(bean)。 您可以通过Servlet初始化参数更改此配置文件的确切位置(有关详细信息,请参阅下文)。 单个DispatcherServlet方案也可能只有一个根上下文。

mvc root context

这可以通过设置一个空的ContextConfigLocation servlet init参数进行配置,如下所示:

<web-app>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/root-context.xml</param-value>
	</context-param>
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value></param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

WebApplicationContext是普通ApplicationContext的扩展,它具有Web应用程序所需的一些额外功能。 它与正常的ApplicationContext不同之处在于它能够解析主题(参见第18.9节“使用主题”),并且知道它与哪个Servlet相关联(通过连接到ServletContext)。 WebApplicationContext绑定在ServletContext中,并且通过在RequestContextUtils类上使用静态方法,您可以随时查找WebApplicationContext,如果您需要访问它。 请注意,我们可以通过基于Java的配置实现相同的方式:


 public class GolfingWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
 protected Class<?>[] getRootConfigClasses() {
 // GolfingAppConfig defines beans that would be in root-context.xml
 return new Class[] { GolfingAppConfig.class };
 }
@Override
 protected Class<?>[] getServletConfigClasses() {
 // GolfingWebConfig defines beans that would be in golfing-servlet.xml
 return new Class[] { GolfingWebConfig.class };
 }
@Override
 protected String[] getServletMappings() {
 return new String[] { "/golfing/*" };
 }
}

 

18.2.1 WebApplicationContext中的特殊Bean类型

Spring DispatcherServlet使用特殊的bean来处理请求并呈现适当的视图。 这些bean是Spring MVC的一部分。 您可以通过在WebApplicationContext中简单配置一个或多个选择要使用的特殊bean。 但是,您最初不需要这样做,因为Spring MVC维护一个默认bean列表,如果您没有配置任何内容。 更多的在下一节。 首先看下表列出DispatcherServlet依赖的特殊bean类型。

表18.1. 在 WebApplicationContext中的特殊bean类型

Bean type Explanation
HandlerMapping 根据一些标准将传入的请求映射到处理程序和前处理程序和后处理程序列表(处理程序拦截器),其细节由HandlerMapping实现而异。 最流行的实现支持注释控制器,但其他实现也存在。
HandlerAdapter 帮助DispatcherServlet调用映射到请求的处理程序,而不管实际调用哪个处理程序。 例如,调用带注释的控制器需要解析各种注释。 因此,HandlerAdapter的主要目的是屏蔽DispatcherServlet和这些细节
HandlerExceptionResolver 映射视图的异常,也允许更复杂的异常处理代码。
ViewResolver 将基于逻辑字符串的视图名称解析为实际的View类型。
LocaleResolver & LocaleContextResolver 解决客户端正在使用的区域设置以及可能的时区,以便能够提供国际化的视图
ThemeResolver 解决您的Web应用程序可以使用的主题,例如,提供个性化的布局
MultipartResolver 解析multi-part请求,以支持从HTML表单处理文件上传。
FlashMapManager 存储并检索可以用于将属性从一个请求传递到另一个请求的“输入”和“输出”FlashMap,通常是通过重定向。

18.2.2 默认DispatcherServlet 配置

如上一节中针对每个特殊bean所述,DispatcherServlet会维护默认使用的实现列表。此信息保存在包org.springframework.web.servlet中的文件DispatcherServlet.properties中。

所有特殊豆都有一些合理的默认值。不久之后,您将需要自定义这些bean提供的一个或多个属性。例如,将InternalResourceViewResolver设置的前缀属性配置为视图文件的父位置是很常见的。

无论细节如何,在这里了解的重要概念是,一旦您在WebApplicationContext中配置了一个特殊的bean(如InternalResourceViewResolver),您可以有效地覆盖该特殊bean类型所使用的默认实现列表。例如,如果配置了InternalResourceViewResolver,则会忽略ViewResolver实现的默认列表。

在第18.16节“配置Spring MVC”中,您将了解配置Spring MVC的其他选项,包括MVC Java配置和MVC XML命名空间,这两者都提供了一个简单的起点,并且对Spring MVC的工作原理几乎不了解。无论您如何选择配置应用程序,本节中介绍的概念都是基础的,应该对您有所帮助。

18.2.3 DispatcherServlet 处理序列

在您设置了DispatcherServlet并且针对该特定DispatcherServlet启动了一个请求后,DispatcherServlet将按如下所示开始处理请求:

在请求中搜索并绑定WebApplicationContext作为控件和进程中的其他元素可以使用的属性。默认情况下,它将在DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE键下绑定。
语言环境解析器被绑定到请求,以使进程中的元素能够解决在处理请求时使用的区域设置(渲染视图,准备数据等)。如果您不需要语言环境解析,则不需要它。
主题解析器被绑定到使得诸如视图之类的元素确定要使用哪个主题的请求。如果不使用主题,可以忽略它。
如果指定了多部分文件解析器,则会检查该请求的多部分;如果找到多部分,则请求被包装在一个MultipartHttpServletRequest中,以便进程中的其他元素进一步处理。有关多部分处理的更多信息,请参见第18.10节“Spring的多部分(文件上传)支持”。
搜索适当的处理程序。如果找到处理程序,则执行与处理程序(预处理程序,后处理程序和控制器)关联的执行链,以便准备模型或呈现。
如果返回模型,则呈现视图。如果没有返回模型(可能是由于预处理程序或后处理程序拦截请求,可能是出于安全原因),因为请求可能已经被满足,所以不会呈现任何视图。
在WebApplicationContext中声明的处理程序异常解析程序在处理请求期间提取异常。使用这些异常解析器允许您定义自定义行为来解决异常。

Spring DispatcherServlet还支持返回由Servlet API指定的最后修改日期。确定特定请求的最后修改日期的过程很简单:DispatcherServlet查找适当的处理程序映射,并测试发现的处理程序是否实现了LastModified接口。如果是,则LastModified接口的long getLastModified(request)方法的值将返回给客户端。

您可以通过将Servlet初始化参数(init-param元素)添加到web.xml文件中的Servlet声明来自定义单独的DispatcherServlet实例。有关支持的参数列表,请参见下表。

表18.2. DispatcherServlet 初始化参数

参数 解释
contextClass 实现WebApplicationContext的类,它实例化了这个Servlet使用的上下文。 默认情况下,使用XmlWebApplicationContext。
contextConfigLocation 传递给上下文实例(由contextClass指定)以指示可以找到上下文的字符串。 该字符串可能包含多个字符串(使用逗号作为分隔符)来支持多个上下文。 在具有两次定义的bean的多个上下文位置的情况下,优先级最高。
namespace WebApplicationContext的命名空间。 默认为[servlet-name] -servlet。

18.3 实现Controllers

控制器提供对通常通过服务接口定义的应用程序行为的访问。控制器解释用户输入并将其转换为由视图表示给用户的模型。 Spring以非常抽象的方式实现控制器,使您能够创建各种各样的控制器。

Spring 2.5引入了一种基于注释的编程模型,用于使用诸如@RequestMapping,@RequestParam,@ModelAttribute等注释的MVC控制器。以这种风格实现的控制器不必扩展特定的基类或实现特定的接口。此外,它们通常不直接依赖于Servlet API,但是如果需要,您可以轻松地配置对Servlet设施的访问。

@Controller
public class HelloWorldController {

@RequestMapping( “/ HelloWorld” 的)
public String helloWorld(Model model){
model.addAttribute(“message”,“Hello World!”);

return “helloWorld”;
}
}
您可以看到,@Controller和@RequestMapping注释允许灵活的方法名称和签名。在这个特殊的例子中,该方法接受一个Model并返回一个视图名称作为一个String,但是可以使用各种其他的方法参数和返回值,如本节稍后所述。 @Controller和@RequestMapping和许多其他注释构成了Spring MVC实现的基础。本节介绍这些注释以及它们在Servlet环境中最常用的注释。

18.3.1 使用@Controller定义控制器

@Controller注释表示特定的类用于控制器的角色。 Spring不需要扩展任何控制器基类或引用Servlet API。 但是,如果需要,您仍然可以参考Servlet特定的功能。

@Controller注释作为注释类的构造型,表示其作用。 调度程序扫描这些注释类的映射方法,并检测@RequestMapping注释(请参阅下一节)。

您可以使用调度程序上下文中的标准Spring bean定义来明确定义带注释的控制器bean。 但是,@Controller构造型还允许自动检测,与Spring通用支持对齐,用于检测类路径中的组件类并自动注册它们的bean定义。要启用自动检测这些带注释的控制器,您可以向组态添加组件扫描。 使用spring-context模式,如以下XML代码片段所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
	<!-- ... -->
</beans>

18.3.2 使用@RequestMapping映射请求

您可以使用@RequestMapping注释将诸如/约会的URL映射到整个类或特定的处理程序方法。 通常,类级注释将特定的请求路径(或路径模式)映射到表单控制器上,其他方法级注释缩小了特定HTTP请求方法(“GET”,“POST”等)的主映射,或 HTTP请求参数条件。

Petcare示例中的以下示例显示了使用此注释的Spring MVC应用程序中的控制器:

@Controller
@RequestMapping("/appointments")
public class AppointmentsController {

	private final AppointmentBook appointmentBook;

	@Autowired
	public AppointmentsController(AppointmentBook appointmentBook) {
		this.appointmentBook = appointmentBook;
	}

	@RequestMapping(method = RequestMethod.GET)
	public Map<String, Appointment> get() {
		return appointmentBook.getAppointmentsForToday();
	}

	@RequestMapping(path = "/{day}", method = RequestMethod.GET)
	public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
		return appointmentBook.getAppointmentsForDay(day);
	}

	@RequestMapping(path = "/new", method = RequestMethod.GET)
	public AppointmentForm getNewForm() {
		return new AppointmentForm();
	}

	@RequestMapping(method = RequestMethod.POST)
	public String add(@Valid AppointmentForm appointment, BindingResult result) {
		if (result.hasErrors()) {
			return "appointments/new";
		}
		appointmentBook.addAppointment(appointment);
		return "redirect:/appointments";
	}
}
在上面的例子中,@RequestMapping用在很多地方。 第一个用法是类型(类)级别,这表示此控制器中的所有处理程序方法都相对于/约会路径。 get()方法还有一个@RequestMapping细化:它只接受GET请求,这意味着/appointments 的HTTP GET调用此方法。 add()有一个类似的细化,getNewForm()将HTTP方法和路径的定义组合成一个,以便通过该方法处理appointments /新的GET请求。
getForDay()方法显示了@RequestMapping:URI模板的另一种用法。 (参见“URI模板模式”一节)。
类级别上的@RequestMapping不是必需的。 没有它,所有的路径都是绝对的,而不是相对的。 PetClinic示例应用程序的以下示例显示了使用@RequestMapping的多操作控制器:
@Controller
public class ClinicController {

	private final Clinic clinic;

	@Autowired
	public ClinicController(Clinic clinic) {
		this.clinic = clinic;
	}

	@RequestMapping("/")
	public void welcomeHandler() {
	}

	@RequestMapping("/vets")
	public ModelMap vetsHandler() {
		return new ModelMap(this.clinic.getVets());
	}

}
上述示例不指定GET与PUT,POST等,因为@RequestMapping默认映射所有HTTP方法。 使用@RequestMapping(method = GET)或@GetMapping来缩小映射。

组合@RequestMapping变体

 Spring Framework 4.3引入了@RequestMapping注释的以下方法级组合变体,有助于简化常见HTTP方法的映射,并更好地表达注释处理程序方法的语义。 例如,@GetMapping可以被读取为GET @RequestMapping。
  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

以下示例显示了使用已组合的@RequestMapping注释简化的上一节中的AppointmentsController的修改版本。

@Controller
@RequestMapping("/appointments")
public class AppointmentsController {

	private final AppointmentBook appointmentBook;

	@Autowired
	public AppointmentsController(AppointmentBook appointmentBook) {
		this.appointmentBook = appointmentBook;
	}

	@GetMapping
	public Map<String, Appointment> get() {
		return appointmentBook.getAppointmentsForToday();
	}

	@GetMapping("/{day}")
	public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
		return appointmentBook.getAppointmentsForDay(day);
	}

	@GetMapping("/new")
	public AppointmentForm getNewForm() {
		return new AppointmentForm();
	}

	@PostMapping
	public String add(@Valid AppointmentForm appointment, BindingResult result) {
		if (result.hasErrors()) {
			return "appointments/new";
		}
		appointmentBook.addAppointment(appointment);
		return "redirect:/appointments";
	}
}

@Controller 和AOP 代理

在某些情况下,控制器可能需要在运行时用AOP代理进行装饰。 一个例子是如果您选择在控制器上直接使用@Transactional注释。 在这种情况下,对于控制器,我们建议使用基于类的代理。 这通常是控制器的默认选项。 但是,如果控制器必须实现不是Spring Context回调的接口(例如InitializingBean,* Aware等),则可能需要显式配置基于类的代理。 例如,使用<tx:annotation-driven />,更改为<tx:annotation-driven proxy-target-class =“true”/>。

Spring MVC 3.1中的@RequestMapping方法的新支持类

Spring 3.1分别为@RequestMapping方法引入了一组新的支持类,分别叫做RequestMappingHandlerMapping和RequestMappingHandlerAdapter。它们被推荐使用,甚至需要利用Spring MVC 3.1中的新功能和未来。默认情况下,MVC命名空间和MVC Java配置启用新的支持类,但是如果不使用,则必须显式配置。本节介绍旧支持类和新支持类之间的一些重要区别。

在Spring 3.1之前,类型和方法级请求映射在两个单独的阶段进行了检查 – 首先由DefaultAnnotationHandlerMapping选择一个控制器,并且实际的调用方法被AnnotationMethodHandlerAdapter缩小。

使用Spring 3.1中的新支持类,RequestMappingHandlerMapping是唯一可以决定哪个方法应该处理请求的地方。将控制器方法作为从类型和方法级@RequestMapping信息派生的每个方法的映射的唯一端点的集合。

这使得一些新的可能性。一旦HandlerInterceptor或HandlerExceptionResolver现在可以期望基于对象的处理程序是HandlerMethod,它允许它们检查确切的方法,其参数和关联的注释。 URL的处理不再需要跨不同的控制器进行拆分。

还有下面几件事情已经不复存在了:

  • 首先使用SimpleUrlHandlerMapping或BeanNameUrlHandlerMapping选择控制器,然后基于@RequestMapping注释来缩小方法。
  • 依赖于方法名称作为一种落后机制,以消除两个@RequestMapping方法之间的差异,这两个方法没有明确的路径映射URL路径, 通过HTTP方法。 在新的支持类中,@RequestMapping方法必须被唯一地映射。
  • 如果没有其他控制器方法更具体地匹配,请使用单个默认方法(无显式路径映射)处理请求。 在新的支持类中,如果找不到匹配方法,则会引发404错误。

    上述功能仍然支持现有的支持类。 不过要利用新的Spring MVC 3.1功能,您需要使用新的支持类。

    URI 模版模式

    可以使用URI模板方便地访问@RequestMapping方法中URL的所选部分。

    URI模板是一个类似URI的字符串,包含一个或多个变量名称。 当您替换这些变量的值时,模板将成为一个URI。 所提出的RFC模板RFC定义了URI如何参数化。 例如,URI模板http://www.example.com/users/{userId}包含变量userId。 将fred的值分配给变量会得到http://www.example.com/users/fred。

    在Spring MVC中,您可以使用方法参数上的@PathVariable注释将其绑定到URI模板变量的值:

    @GetMapping("/owners/{ownerId}")
    public String findOwner(@PathVariable String ownerId, Model model) {
    	Owner owner = ownerService.findOwner(ownerId);
    	model.addAttribute("owner", owner);
    	return "displayOwner";
    }
    URI模板“/ owners / {ownerId}”指定变量名ownerId。 当控制器处理此请求时,ownerId的值将设置为在URI的适当部分中找到的值。 例如,当/ owner / fred出现请求时,ownerId的值为fred。
    @GetMapping("/owners/{ownerId}")
    public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
    	// implementation omitted
    }

    或者如果URI模板变量名称与方法参数名称匹配,则可以省略该详细信息。 只要您的代码使用调试信息或Java 8上的参数编译器标记进行编译,Spring MVC将将方法参数名称与URI模板变量名称相匹配:

    @GetMapping("/owners/{ownerId}")
    public String findOwner(@PathVariable String ownerId, Model model) {
    	// implementation omitted
    }
    一个方法能够有任意数量的@PathVariable注解:
    
    @GetMapping("/owners/{ownerId}/pets/{petId}")
    public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
    	Owner owner = ownerService.findOwner(ownerId);
    	Pet pet = owner.getPet(petId);
    	model.addAttribute("pet", pet);
    	return "displayPet";
    }
    当在Map <String,String>参数上使用@PathVariable注释时,映射将填充所有URI模板变量。
    URI模板可以从类型和方法级别@RequestMapping注释中进行组合。 因此,可以使用/ owner / 42 / pets / 21等URL调用findPet()方法。
    @Controller
    @RequestMapping("/owners/{ownerId}")
    public class RelativePathUriTemplateController {
    
    	@RequestMapping("/pets/{petId}")
    	public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
    		// implementation omitted
    	}
    
    }
    一个@PathVariable参数可以是任何简单的类型,如int,long,Date等。如果没有这样做,Spring将自动转换为适当的类型或者抛出一个TypeMismatchException。 您还可以注册解析其他数据类型的支持。. See the section called “Method Parameters And Type Conversion”the section called “Customizing WebDataBinder initialization”.

    具有正则表达式的URI模板模式

    有时您需要更精确地定义URI模板变量。 考虑URL“/spring-web/spring-web-3.0.5.jar”。 你怎么把它分解成多个部分?

    @RequestMapping注释支持在URI模板变量中使用正则表达式。 语法是{varName:regex},其中第一部分定义了变量名,第二部分定义了正则表达式。 例如:

    @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}")
    public void handle(@PathVariable String version, @PathVariable String extension) {
    	// ...
    }
    
    

    路径模式

    除了URI模板之外,@RequestMapping注释和所有组合的@RequestMapping变体也支持Ant样式的路径模式(例如/myPath/*.do)。 还支持URI模板变量和Ant-style glob的组合(例如/ owners / * / pets / {petId})。

    路径模式比较

    当URL匹配多个模式时,使用排序来查找最具体的匹配。

    具有较低数量URI变量和通配符的模式被认为更具体。 例如/hotels/ {hotel} / *具有1个URI变量和1个通配符,被认为比/hotels/ {hotel} / **更具体,其中1个URI变量和2个通配符。

    如果两个模式具有相同的计数,那么较长的模式被认为更具体。 例如/ foo / bar *比较长,被认为比/ foo / *更具体。

    当两个模式具有相同的计数和长度时,具有较少通配符的模式被认为更具体。 例如/hotels/ {hotel}比/hotels/ *更具体。

    下面有些额外增加的特殊的规则:

    • 默认映射模式/ **比任何其他模式都要小。 例如/ api / {a} / {b} / {c}更具体。
    • 诸如/ public / **之类的前缀模式比不包含双通配符的任何其他模式都不那么具体。 例如/ public / path3 / {a} / {b} / {c}更具体。

For 有关详细信息,请参阅AntPathMatcher中的AntPatternComparator。 请注意,可以自定义PathMatcher(参见Section 18.16.11, “Path Matching” ).

具有占位符的路径模式

@RequestMapping注释中的模式支持对本地属性和/或系统属性和环境变量的$ {…}占位符。 在将控制器映射到的路径可能需要通过配置进行定制的情况下,这可能是有用的。 有关占位符的更多信息,请参阅PropertyPlaceholderConfigurer类的javadocs。

后缀模式匹配

默认情况下,Spring MVC执行“。*”后缀模式匹配,以便映射到/ person的控制器也隐式映射到/person.*。这使得通过URL路径(例如/person.pdf,/person.xml)可以轻松地请求资源的不同表示。

后缀模式匹配可以关闭或限制为一组明确注册用于内容协商的路径扩展。通常建议通过诸如/ person / {id}之类的常见请求映射来减少歧义,其中点可能不表示文件扩展名,例如/person/joe@email.com vs /person/joe@email.com.json。此外,如下面的说明中所解释的,后缀模式匹配以及内容协商可能在某些情况下用于尝试恶意攻击,并且有充分的理由有意义地限制它们。

有关后缀模式匹配配置,请参见第18.16.11节“路径匹配”,内容协商配置第18.16.6节“内容协商”。

后缀模式匹配和RFD

反思文件下载(RFD)攻击是由Trustwave在2014年的一篇论文中首次描述的。攻击类似于XSS,因为它依赖于响应中反映的输入(例如查询参数,URI变量)。然而,不是将JavaScript插入到HTML中,如果基于文件扩展名(例如.bat,.cmd)双击,则RFD攻击依赖于浏览器切换来执行下载并将响应视为可执行脚本。

在Spring MVC @ResponseBody和ResponseEntity方法存在风险,因为它们可以呈现客户端可以通过URL路径扩展请求的不同内容类型。但是请注意,单独禁用后缀模式匹配或禁用仅用于内容协商的路径扩展都可以有效地防止RFD攻击。

为了全面保护RFD,在呈现响应体之前,Spring MVC添加了Content-Disposition:inline; filename = f.txt头来建议一个固定和安全的下载文件。只有当URL路径包含既不是白名单的文件扩展名,也没有明确注册用于内容协商的目的,这是完成的。但是,当URL直接输入浏览器时,可能会产生副作用。

许多常见的路径扩展名默认为白名单。此外,REST API调用通常不是直接用于浏览器中的URL。然而,使用自定义HttpMessageConverter实现的应用程序可以明确地注册用于内容协商的文件扩展名,并且不会为此类扩展添加Content-Disposition头。见第18.16.6节“Content Negotiation”

这是CVE-2015-5211工作的一部分。 以下是报告中的其他建议:

  • 编码而不是转义JSON响应。 这也是OWASP XSS的建议。 有关Spring的例子,请参阅spring-jackson-owasp.
  • 将后缀模式匹配配置为关闭或仅限于明确注册的后缀
  • 配置使用属性“useJaf”和“ignoreUnknownPathExtensions”设置为false的内容协商,这将导致具有未知扩展名的URL的406响应。 但是请注意,如果URL自然希望有一个结束点,这可能不是一个选择。
  • 添加X-Content-Type-Options:nosniff头到响应。 Spring Security 4默认情况下执行此操作。

矩阵变量

URI规范RFC 3986定义了在路径段中包含名称 – 值对的可能性。规格中没有使用具体术语。可以应用一般的“URI路径参数”,尽管来自Tim Berners-Lee的旧帖子的更独特的“Matrix URI”也经常被使用并且是相当熟知的。在Spring MVC中,这些被称为矩阵变量。

矩阵变量可以出现在任何路径段中,每个矩阵变量用“;”分隔(分号)。例如:“/ cars; color = red; year = 2012”。多个值可以是“,”(逗号)分隔“color = red,green,blue”,或者变量名称可以重复“color = red; color = green; color = blue”。

如果URL预期包含矩阵变量,则请求映射模式必须使用URI模板来表示它们。这确保了请求可以正确匹配,无论矩阵变量是否存在,以及它们提供什么顺序。

以下是提取矩阵变量“q”的示例:

// GET /pets/42;q=11;r=22

@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

	// petId == 42
	// q == 11

}
由于所有路径段都可能包含矩阵变量,因此在某些情况下,您需要更具体地确定变量预期位于何处:
// GET /owners/42;q=11/pets/21;q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
		@MatrixVariable(name="q", pathVar="ownerId") int q1,
		@MatrixVariable(name="q", pathVar="petId") int q2) {

	// q1 == 11
	// q2 == 22

}
矩阵变量可以定义为可选参数,并指定一个默认值:
// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

	// q == 1

}
所有矩阵变量可以在Map中获得:
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
		@MatrixVariable MultiValueMap<String, String> matrixVars,
		@MatrixVariable(pathVar="petId"") MultiValueMap<String, String> petMatrixVars) {

	// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
	// petMatrixVars: ["q" : 11, "s" : 23]

}
请注意,为了使用矩阵变量,您必须将RequestMappingHandlerMapping的removeSemicolonContent属性设置为false。 默认设置为true。
MVC Java配置和MVC命名空间都提供了使用矩阵变量的选项。
如果您使用Java配置,使用MVC Java Config的高级自定义部分将介绍如何自定义RequestMappingHandlerMapping。
在MVC命名空间中,<mvc:annotation-driven>元素具有一个应该设置为true的enable-matrix-variables属性。 默认情况下设置为false。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<mvc:annotation-driven enable-matrix-variables="true"/>

</beans>

Consumable Media 类型

您可以通过指定consumable media类型的列表来缩小主要映射。 只有当Content-Type请求头与指定的媒体类型匹配时,才会匹配该请求。 例如:

@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet, Model model) {
	// implementation omitted
}
consumable media类型表达式也可以在!text / plain中否定,以匹配除Content-Type of text / plain之外的所有请求。 还要考虑使用MediaType中提供的常量,例如APPLICATION_JSON_VALUE和APPLICATION_JSON_UTF8_VALUE。

Producible Media 类型

您可以通过指定producible media类型列表来缩小主要映射。 只有当Accept请求头匹配这些值之一时,才会匹配该请求。 此外,使用产生条件确保用于产生响应的实际内容类型与产生条件中指定的媒体类型相关。 例如:

@GetMapping(path = "/pets/{petId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
	// implementation omitted
}
就像消费一样,可生产的媒体类型表达式可以被否定为!text / plain,以匹配除了接受头文件值为text / plain的所有请求。 还要考虑使用MediaType中提供的常量,例如APPLICATION_JSON_VALUE和APPLICATION_JSON_UTF8_VALUE。

请求参数和头部值

您可以通过请求参数条件(如“myParam”,“!myParam”或“myParam = myValue”)缩小请求匹配。 前两个测试请求参数存在/不存在,第三个为特定参数值。 下面是一个请求参数值条件的例子:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

	@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
	public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
		// implementation omitted
	}

}
也可以根据特定的请求头值来测试请求头存在/不存在或匹配:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

	@GetMapping(path = "/pets", headers = "myHeader=myValue")
	public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
		// implementation omitted
	}

}

HTTP 头部和 HTTP 可选项

映射到“GET”的@RequestMapping方法也隐式映射到“HEAD”,即不需要明确声明“HEAD”。处理HTTP HEAD请求就像是HTTP GET一样,而不是仅写入正文,仅计数字节数,并设置“Content-Length”头。

@RequestMapping方法内置支持HTTP选项。默认情况下,通过将所有@RequestMapping方法上显式声明的具有匹配URL模式的HTTP方法设置为“允许”响应头来处理HTTP OPTIONS请求。当没有明确声明HTTP方法时,“允许”标题设置为“GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS”。理想地总是声明@RequestMapping方法要处理的HTTP方法,或者使用专用的组合@RequestMapping变体之一(参见“Composed @RequestMapping Variants”一节)。

虽然不需要@RequestMapping方法可以映射到HTTP HEAD或HTTP选项,也可以两者兼容。

18.3.3 定义@RequestMapping 处理方法

@RequestMapping处理方法可以有非常灵活的签名。 支持的方法参数和返回值将在以下部分中介绍。 大多数参数可以按任意顺序使用,唯一的例外是BindingResult参数。 这将在下一节中介绍。

支持的方法参数类型

下面是支持的方法参数类型:

  • org.springframework.web.context.request.WebRequest or org.springframework.web.context.request.NativeWebRequest. 允许通用请求参数访问以及请求/会话属性访问,而不涉及本机Servlet API。
  • Request or response objects (Servlet API). 选择任意特定的请求或响应类型, for example ServletRequest or HttpServletRequest or Spring’sMultipartRequest/MultipartHttpServletRequest.
  • Session对象(Servlet API)类型为HttpSession。 此类型的参数强制存在相应的会话。 因此,这样的论证从不为空。

会话访问可能不是线程安全的,特别是在Servlet环境中。 如果允许多个请求同时访问会话,请考虑将RequestMappingHandlerAdapter的“synchronizeOnSession”标志设置为“true”。

  • java.servlet.http.PushBuilder用于关联的Servlet 4.0推送构建器API,允许编程的HTTP / 2资源推送。
  • java.security.Principal(或一个特定的Principal实现类(如果已知)),包含当前验证的用户。
  • org.springframework.http.HttpMethod为HTTP请求方法,表示为Spring的HttpMethod枚举。
  • 由当前请求区域设置的java.util.Locale,由最具体的语言环境解析器确定,实际上是在MVC环境中配置的LocaleResolver / LocaleContextResolver。
  • 与当前请求相关联的时区的java.util.TimeZone(Java 6+)/ java.time.ZoneId(Java 8+),由LocaleContextResolver确定。
  • java.io.InputStream / java.io.Reader,用于访问请求的内容。该值是由Servlet API公开的原始InputStream / Reader。
  • java.io.OutputStream / java.io.Writer用于生成响应的内容。该值是由Servlet API公开的原始OutputStream / Writer。
  • @PathVariable注释参数,用于访问URI模板变量。请参阅the section called “URI Template Patterns”.
  • @MatrixVariable注释参数,用于访问位于URI路径段中的名称/值对。请参阅 the section called “Matrix Variables”.
  • @RequestParam用于访问特定Servlet请求参数的注释参数。参数值将转换为声明的方法参数类型。请参阅 the section called “Binding request parameters to method parameters with @RequestParam”.
  • @RequestHeader用于访问特定Servlet请求HTTP标头的注释参数。参数值将转换为声明的方法参数类型。请参阅 the section called “Mapping request header attributes with the @RequestHeader annotation”.
  • @RequestBody用于访问HTTP请求体的注释参数。使用HttpMessageConverters将参数值转换为声明的方法参数类型。请参阅the section called “Mapping the request body with the @RequestBody annotation”.
  • @RequestPart注释参数,用于访问“multipart / form-data”请求部分的内容。请参见Section 18.10.5, “Handling a file upload request from programmatic clients”Section 18.10, “Spring’s multipart (file upload) support”.
  • @SessionAttribute用于访问现有的永久会话属性(例如,用户认证对象)的注释参数,而不是通过@SessionAttributes作为控制器工作流的一部分临时存储在会话中的模型属性。
  • @RequestAttribute用于访问请求属性的注释参数。
  • HttpEntity <?>参数访问Servlet请求HTTP头和内容。请求流将使用HttpMessageConverters转换为实体。请参阅 the section called “Using HttpEntity”.
  • java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap用于丰富暴露于Web视图的隐式模型。
  • org.springframework.web.servlet.mvc.support.RedirectAttributes来指定在重定向情况下使用的精确的属性集,并且还添加Flash属性(临时存储在服务器端的属性,使其可以在请求之后使用重定向)。请参见 the section called “Passing Data To the Redirect Target”Section 18.6, “Using flash attributes”.
  • 根据@InitBinder方法和/或HandlerAdapter配置,命令或表单对象将请求参数绑定到bean属性(通过setter)或直接转换为字段,并进行可定制的类型转换。请参阅RequestMappingHandlerAdapter上的webBindingInitializer属性。默认情况下,这些命令对象及其验证结果将作为模型属性公开,使用命令类名称 – 例如。对于“some.package.OrderAddress”类型的命令对象的model属性“orderAddress”。 ModelAttribute注释可以用于方法参数来自定义所使用的模型属性名称。
  • org.springframework.validation.Errors / org.springframework.validation.BindingResult验证前一个命令或表单对象的结果(即在前面的方法参数)。
  • 用于将表单处理标记为完整的org.springframework.web.bind.support.SessionStatus状态句柄,它触发在处理程序类型级别上由@SessionAttributes注释指示的会话属性的清除。
  • org.springframework.web.util.UriComponentsBuilder用于准备与当前请求的主机,端口,方案,上下文路径以及servlet映射的文字部分相关的URL的构建器。

错误或BindingResult参数必须遵循正在绑定的模型对象,因为方法签名可能有多个模型对象,Spring将为每个模型对象创建一个单独的BindingResult实例,因此以下示例将不起作用:

BindingResult和@ModelAttribute的排序无效。

@PostMapping
public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { ... }
注意,Pet和BindingResult之间有一个Model参数。 要使其工作,您必须重新排序参数如下:
@PostMapping
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { ... }

支持的方法返回类型

以下是支持的返回类型:

  • 一个ModelAndView对象,其中模型隐含地丰富了命令对象和@ModelAttribute注释引用数据访问器方法的结果。
  • 一个Model对象,其视图名称通过RequestToViewNameTranslator隐式确定,隐式丰富了命令对象的模型以及@ModelAttribute注释引用数据访问器方法的结果。
  • 用于暴露模型的Map对象,其视图名称通过RequestToViewNameTranslator隐式确定,隐式丰富了命令对象的模型以及@ModelAttribute注释引用数据访问器方法的结果。
  • 一个View对象,其模型通过命令对象和@ModelAttribute注释引用数据访问器方法隐式确定。处理程序方法也可以通过声明一个Model参数(见上文)以编程方式丰富模型。
  • 解释为逻辑视图名称的字符串值,模型通过命令对象和@ModelAttribute注释引用数据访问器方法隐式确定。处理程序方法也可以通过声明一个Model参数(见上文)以编程方式丰富模型。
  • 如果方法处理响应本身(通过直接写入响应内容,为此目的声明一个类型为ServletResponse / HttpServletResponse的参数),或者如果视图名称通过RequestToViewNameTranslator隐式确定(不在处理程序方法签名)。
  • 如果该方法用@ResponseBody注释,则返回类型将写入响应HTTP主体。返回值将使用HttpMessageConverters转换为声明的方法参数类型。请参阅 the section called “Mapping the response body with the @ResponseBody annotation”.
  • 一个HttpEntity <?>或ResponseEntity <?>对象来提供对Servlet响应HTTP头和内容的访问。实体将使用HttpMessageConverters转换为响应流。请参阅 the section called “Using HttpEntity”.
  • 一个HttpHeaders对象返回没有正文的响应。
  • 当应用程序想要在由Spring MVC管理的线程中异步生成返回值时,可以返回Callable <?>。
  • 当应用程序想从自己选择​​的线程生成返回值时,可以返回DeferredResult <?>
  • 当应用程序想要从线程池提交中产生值时,可以返回ListenableFuture <?>或CompletableFuture <?> / CompletionStage <?>。
  • 可以返回ResponseBodyEmitter以异步地将多个对象写入响应;也支持作为ResponseEntity内的主体。
  • 可以返回SseEmitter以将异步的Server-Sent事件写入响应;也支持作为ResponseEntity内的主体。
  • 可以返回StreamingResponseBody以异步写入响应OutputStream;也支持作为ResponseEntity内的主体
  • 任何其他返回类型都被认为是要暴露给视图的单一模型属性,使用在方法级别(或基于返回类型类名称的默认属性名称)中通过@ModelAttribute指定的属性名称。该模型隐含地丰富了命令对象和@ModelAttribute注释引用数据访问器方法的结果。

通过@RequestParam绑定请求参数到方法

使用@RequestParam注解将请求参数绑定到控制器中的方法参数。
以下代码片段显示用法:
@Controller
@RequestMapping("/pets")
@SessionAttributes("pet")
public class EditPetForm {

	// ...

	@GetMapping
	public String setupForm(@RequestParam("petId") int petId, ModelMap model) {
		Pet pet = this.clinic.loadPet(petId);
		model.addAttribute("pet", pet);
		return "petForm";
	}

	// ...

}
默认情况下,使用此注释的参数是必需的,但您可以通过将@ RequestParam的必需属性设置为false(例如@RequestParam(name =“id”,required = false))来指定参数是可选的。
如果目标方法参数类型不是String,则会自动应用类型转换。 请参阅“方法参数和类型转换”一节。
当在Map <String,String>或MultiValueMap <String,String>参数上使用@RequestParam注释时,映射将填充所有请求参数。

使用@RequestBody注释映射请求体

@RequestBody方法参数注释表示方法参数应绑定到HTTP请求体的值。 例如:

@PutMapping("/something")
public void handle(@RequestBody String body, Writer writer) throws IOException {
	writer.write(body);
}
通过使用HttpMessageConverter将请求体转换为method参数。 HttpMessageConverter负责将HTTP请求消息转换为对象,并从对象转换为HTTP响应体。 RequestMappingHandlerAdapter支持带有以下默认HttpMessageConverters的@RequestBody注释:
  • byteArrayHttpMessageConverter converts byte arrays.
  • StringHttpMessageConverter converts strings.
  • FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.
  • SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
有关这些转换器的更多信息,请参阅消息转换器。 另请注意,如果使用MVC命名空间或MVC Java配置,默认情况下会注册更广泛的消息转换器。 有关详细信息,请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”。 Section 18.16.1, “Enabling the MVC Java Config or the MVC XML Namespace” 
如果您打算读写XML,则需要使用org.springframework.oxm包中的特定Marshaller和Unmarshaller实现配置MarshallingHttpMessageConverter。 下面的示例显示了如何直接在配置中执行此操作,但是如果您的应用程序通过MVC命名空间或MVC Java配置进行配置,请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
	<property name="messageConverters">
		<util:list id="beanList">
			<ref bean="stringHttpMessageConverter"/>
			<ref bean="marshallingHttpMessageConverter"/>
		</util:list>
	</property>
</bean>

<bean id="stringHttpMessageConverter"
		class="org.springframework.http.converter.StringHttpMessageConverter"/>

<bean id="marshallingHttpMessageConverter"
		class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
	<property name="marshaller" ref="castorMarshaller"/>
	<property name="unmarshaller" ref="castorMarshaller"/>
</bean>

<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
@RequestBody方法参数可以用@Valid注释,在这种情况下,它将使用配置的Validator实例进行验证。 当使用MVC命名空间或MVC Java配置时,会自动配置一个JSR-303验证器,假设在类路径上可用JSR-303实现。
就像@ModelAttribute参数一样,可以使用一个Errors参数来检查错误。 如果未声明此类参数,则将引发MethodArgumentNotValidException异常。 该异常在DefaultHandlerExceptionResolver中处理,它将向客户端发送一个400错误。

使用@ResponseBody注解映射响应体

使用@RequestParam注释将请求参数绑定到控制器中的方法参数。

以下代码片段显示用法:

@Controller 
@RequestMapping(“/ pets”)
@SessionAttributes(“pet”)
 public  class EditPetForm { // ... @GetMapping public String setupForm( @RequestParam(“petId”)int petId,ModelMap model){
		Pet pet = this .clinic.loadPet(petId); 
		model.addAttribute( “pet”,pet);
		返回“petForm” ; 
	} // ...

	

	
	 

	

}

使用这个注解的参数默认情况下必需的,但你可以指定一个参数是通过设置可选@RequestParamrequired属性false(如@RequestParam(name="id", required=false))。

如果目标方法参数类型不是,则会自动应用类型转换 String。请参阅“方法参数和类型转换”一节

@RequestParam在一个Map<String, String>或者 MultiValueMap<String, String>参数上使用注释时,地图将填充所有请求参数。

使用@RequestBody注释映射请求体

所述@RequestBody方法参数注释指示方法参数应绑定到HTTP请求正文的值。例如:

@PutMapping(“/ something”)
 public  void handle( @RequestBody String body,Writer writer) throws IOException {
	writer.write(body); 
}

您可以使用a将请求体转换为method参数HttpMessageConverterHttpMessageConverter负责从HTTP请求消息转换为对象,并从对象转换为HTTP响应正文。该RequestMappingHandlerAdapter支持@RequestBody使用以下默认注释HttpMessageConverters

  • ByteArrayHttpMessageConverter 转换字节数组。
  • StringHttpMessageConverter 转换字符串。
  • FormHttpMessageConverter 将表单数据转换为/从MultiValueMap <String,String>转换。
  • SourceHttpMessageConverter 转换为/从javax.xml.transform.Source转换。

有关这些转换器的更多信息,请参阅消息转换器。另请注意,如果使用MVC命名空间或MVC Java配置,默认情况下会注册更广泛的消息转换器。有关详细信息,请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”

如果您打算读写XML,则需要 从包中MarshallingHttpMessageConverter配置特定的Marshaller和实现。下面的示例显示了如何直接在配置中执行此操作,但是如果您的应用程序通过MVC命名空间或MVC Java配置进行配置,请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”Unmarshallerorg.springframework.oxm

<豆  = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter” > 
	<属性  = “了MessageConverter” > 
		<util的:列表 ID = “beanList” > 
			<REF   = “stringHttpMessageConverter” /> 
			< ref  bean = “marshallingHttpMessageConverter” /> 
		</ util:list> 
	</ property> 
</ bean> 

<bean  id = “stringHttpMessageConverter” 
		class = “org.springframework.http。转换器.StringHttpMessageConverter“ /> 

<bean  id = ”marshallingHttpMessageConverter“ 
		class = ”org.springframework.http.converter.xml.MarshallingHttpMessageConverter“ > 
	<property  name = ”marshaller“  ref = ”castorMarshaller“ /> 
	<property  name = ”unmarshaller“  ref = “castorMarshaller” /> 
</ bean> 

<bean  id = “castorMarshaller”  class = “org.springframework.oxm.castor.CastorMarshaller”/>

一种@RequestBody方法参数可以与进行注释@Valid,在这种情况下,它将使用配置的验证Validator实例。当使用MVC命名空间或MVC Java配置时,会自动配置一个JSR-303验证器,假设在类路径上可用JSR-303实现。

就像使用@ModelAttribute参数,一个Errors参数可以用来检查错误。如果没有宣布这样的论据,MethodArgumentNotValidException 将会提出一个。在异常处理DefaultHandlerExceptionResolver,它发送400错误回到客户端。

[Note]
有关通过MVC命名空间或MVC Java配置配置消息转换器和验证器的信息请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”

使用@ResponseBody注释映射响应体

@ResponseBody注释是类似@RequestBody。该注释可以放在一个方法上,并指示返回类型应该直接写入HTTP响应体(而不是放在模型中,或者解释为视图名称)。例如:

@GetMapping(“/ something”)
@ResponseBody
 public String helloWorld(){
	 return  “Hello World” ; 
}

上述示例将导致文本Hello World被写入HTTP响应流。

与之一样@RequestBody,Spring通过使用一个转换将返回的对象转换为响应体HttpMessageConverter。有关这些转换器的更多信息,请参阅上一部分和消息转换器

使用@RestController注释创建REST控制器

控制器实现REST API是一个非常常见的用例,因此仅提供JSON,XML或定制的MediaType内容。为方便起见,您可以使用以下@RequestMapping方式@ResponseBody来注释您的控制器类,而不是注释所有 方法@RestController

@RestController is a stereotype annotation that combines @ResponseBody and @Controller. More than that, it gives more meaning to your Controller and also may carry additional semantics in future releases of the framework.

与常规@ControllerS,A @RestController可以通过协助 @ControllerAdvice@RestControllerAdvice豆类。有关 更多详细信息,请参阅“使用@ControllerAdvice和@RestControllerAdvice建议控制器”一节。

使用HttpEntity

HttpEntity是相似的@RequestBody@ResponseBody。除了访问请求和响应主体之外HttpEntity(和响应特定子类ResponseEntity)还允许访问请求和响应头,如下所示:

@RequestMapping(“/ something”)
 public ResponseEntity <String> handle(HttpEntity < byte []> requestEntity) throws UnsupportedEncodingException {
	String requestHeader = requestEntity.getHeaders()。getFirst( “MyRequestHeader”);
	byte [] requestBody = requestEntity.getBody(); //使用请求头和身体 
	做某事HttpHeaders responseHeaders = new HttpHeaders(); 
	responseHeaders.set( “MyResponseHeader” “MyValue”);
	返回新的 ResponseEntity <String>( “Hello World”,responseHeaders,HttpStatus。创建); 
}

	

上述示例获取MyRequestHeader请求标头的值,并将其作为字节数组读取。它将MyResponseHeader响应添加到Hello World响应流中,并将响应状态代码设置为201(已创建)。

至于@RequestBody@ResponseBody,Spring使用HttpMessageConverter从和请求和响应流转换。有关这些转换器的更多信息,请参阅上一部分和消息转换器

在方法上使用@ModelAttribute

@ModelAttribute注释可以对方法或方法的参数来使用。本节将介绍其在方法上的用法,下一节将介绍其在方法参数上的用法。

An @ModelAttribute on a method indicates the purpose of that method is to add one or more model attributes. Such methods support the same argument types as @RequestMapping methods but cannot be mapped directly to requests. Instead @ModelAttribute methods in a controller are invoked before @RequestMappingmethods, within the same controller. A couple of examples:

//添加一个属性
//该方法的返回值被添加到名为“account”的模型中
//您可以通过@ModelAttribute(“myAccount”)

@ModelAttribute
自定义名称@ModelAttribute public Account addAccount(@RequestParam String number) {
	 return accountManager.findAccount(number); 
} //添加多个属性@ModelAttribute public void populateModel(@RequestParam String number,Model model){ 
	model.addAttribute(accountManager.findAccount(number)); //添加更多... 
}




 

@ModelAttribute方法用于填充具有常用属性的模型,例如使用状态或宠物类型填充下拉列表,或者检索诸如Account的命令对象,以便使用它来表示HTML表单上的数据。后一种情况在下一节进一步讨论。

注意两种风格的@ModelAttribute方法。在第一个方法中,该方法通过返回它隐式地添加一个属性。在第二个方法中,该方法接受Model并添加任意数量的模型属性。您可以根据需要选择两种风格。

控制器可以有多种@ModelAttribute方法。所有这些方法都@RequestMapping在相同控制器的方法之前被调用。

@ModelAttribute方法也可以在一个@ControllerAdvice注释类中定义,并且这种方法适用于许多控制器。有关更多详细信息,请参阅“使用@ControllerAdvice和@RestControllerAdvice建议控制器”一节。

[Tip]
当没有明确指定模型属性名称时会发生什么?在这种情况下,根据其类型将默认名称分配给模型属性。例如,如果该方法返回类型的对象Account,则使用的默认名称为“account”。您可以通过@ModelAttribute注释的值更改它。如果直接添加属性Model,请使用适当的重载addAttribute(..)方法 – 即,带有或不带有属性名称。

@ModelAttribute批注可在使用@RequestMapping方法为好。在这种情况下,@RequestMapping方法的返回值将被解释为模型属性而不是视图名称。视图名称是基于视图名称约定导出的,非常类似于返回的方法void - 请参见第18.13.3节“View – RequestToViewNameTranslator”

在方法参数上使用@ModelAttribute

如上一节所述@ModelAttribute,可以在方法或方法参数上使用。本节介绍了其在方法参数中的用法。

一个@ModelAttribute上的方法参数指示参数应该从模型中检索。如果模型中不存在,参数首先被实例化,然后添加到模型中。一旦出现在模型中,参数的字段应该从具有匹配名称的所有请求参数中填充。这被称为Spring MVC中的数据绑定,这是一种非常有用的机制,可以节省您逐个解析每个表单字段。

@PostMapping(“/ owners / {ownerId} / pets / {petId} / edit”)
 public String processSubmit( @ModelAttribute Pet pet){}

鉴于上述例子,宠物实例可以从哪里来?有几个选择:

一种@ModelAttribute方法是从数据库中检索属性的常用方法,可以通过使用可选地在请求之间存储属性 @SessionAttributes。在某些情况下,通过使用URI模板变量和类型转换器来检索属性可能很方便。这是一个例子:

@PutMapping(“/ accounts / {account}”)
 public String save( @ModelAttribute(“account”)帐户帐号){
	 // ... 
}

在此示例中,模型属性(即“account”)的名称与URI模板变量的名称相匹配。如果您注册Converter<String, Account>,可以将 String帐户值转换为一个Account实例,则上述示例将无需使用@ModelAttribute方法。

下一步是数据绑定。该WebDataBinder级比赛要求参数名称-包括查询字符串参数和表单域-以模拟通过名称属性字段。在必要时已经应用了类型转换(从字符串到目标字段类型)之后填充匹配字段。数据绑定和验证在 第5章验证,数据绑定和类型转换中介绍。自定义控制器级别的数据绑定过程将在“自定义WebDataBinder初始化”一节中介绍

由于数据绑定,可能会出现错误,例如缺少必填字段或类型转换错误。要检查这些错误,请在BindingResult参数后立即添加一个@ModelAttribute参数:

@PostMapping(“/ owners / {ownerId} / pets / {petId} / edit”)
 public String processSubmit( @ModelAttribute(“pet”)Pet Pet,BindingResult result){ if(result.hasErrors()){
		 return “petForm “ ; 
	} // ... 
}

	 

	

使用一个BindingResult你可以检查是否发现错误,在这种情况下,渲染相同的形式通常是在Spring的<errors> 表单标签的帮助下显示错误的。

请注意,在某些情况下,在没有数据绑定的情况下获取模型中的属性可能是有用的。对于这种情况,您可以将其注入Model控制器,或者使用注释上的binding标志:

@ModelAttribute
 public AccountForm setUpForm(){
     return  new AccountForm(); 
} @ModelAttribute public Account findAccount( @PathVariable String accountId){
     return accountRepository.findOne(accountId); 
} @PostMapping(“update”) public String update( @Valid AccountUpdateForm form,BindingResult result,
         @ModelAttribute(binding = false) Account account){ // ... 
}







除了数据绑定之外,您还可以使用自己的自定义验证器调用验证,传递与BindingResult用于记录数据绑定错误相同的验证器。这允许在一个地方累积数据绑定和验证错误,并随后向用户报告:

@PostMapping( “/老板/ {} OWNERID /宠物/ {} petId /编辑”)
公共字符串processSubmit( PetValidator()验证(PET,结果);
	如果(result.hasErrors()){
		回报“petForm” ;
	} // ... 
}@ModelAttribute("pet") Pet pet, BindingResult result) {

	 

	

或者您可以通过添加JSR-303 @Valid 注释自动调用验证:

@PostMapping(“/ owners / {ownerId} / pets / {petId} / edit”)
 public String processSubmit( @Valid @ModelAttribute(“pet”)Pet pet,BindingResult result){ if(result.hasErrors()){
		 return “petForm” ; 
	} // ... 
}

	 

	

有关如何配置和使用验证的详细信息,请参见第5.8节“Spring验证”第5章验证,数据绑定和类型转换。

使用@SessionAttributes将模型属性存储在请求之间的HTTP会话中

类型级@SessionAttributes注释声明特定处理程序使用的会话属性。这通常将列出模型属性或模型属性的类型,这些模型属性或类型应该透明地存储在会话或某些会话存储中,作为后续请求之间的格式支持bean。

以下代码片段显示了此注释的用法,指定了模型属性名称:

@Controller 
@RequestMapping(“/ editPet.do”)
@SessionAttributes(“pet”)
 public class EditPetForm {
	// ...
}

使用@SessionAttribute访问预先存在的全局会话属性

如果您需要访问全局管理的预先存在的会话属性,即控制器外部(例如,通过过滤器),并且可能存在或可能不存在,@SessionAttribute则会使用方法参数上的注释:

@RequestMapping(“/”)
 public String handle( @SessionAttribute User user){
	 // ... 
}

对于需要添加或删除会话属性的用例,请考虑注入 org.springframework.web.context.request.WebRequestjavax.servlet.http.HttpSession控制方法。

为了在会话中临时存储模型属性作为控制器工作流的一部分,请考虑使用“使用@SessionAttributes将模型属性存储在请求之间的HTTP会话中”SessionAttributes所述的一节

使用@RequestAttribute来访问请求属性

到类似@SessionAttribute@RequestAttribute注释可以被用于访问由滤波器或拦截器创建的预先存在的请求属性:

@RequestMapping(“/”)
 public String handle( @RequestAttribute Client client){
	 // ... 
}

使用“application / x-www-form-urlencoded”数据

以前的章节介绍了@ModelAttribute如何支持浏览器客户端的表单提交请求。建议与非浏览器客户端的请求一起使用相同的注释。然而,在使用HTTP PUT请求时,有一个显着的区别。浏览器可以通过HTTP GET或HTTP POST提交表单数据。非浏览器客户端也可以通过HTTP PUT提交表单。这提出了一个挑战,因为Servlet规范要求ServletRequest.getParameter*()一系列方法仅支持HTTP POST的表单域访问,而不支持HTTP PUT。

为了支持HTTP PUT和PATCH请求,该spring-web模块提供了HttpPutFormContentFilter可以在以下配置中的过滤器 web.xml

<filter> 
	<filter-name> httpPutFormFilter </ filter-name> 
	<filter-class> org.springframework.web.filter.HttpPutFormContentFilter </ filter-class> 
</ filter> 

<filter-mapping> 
	<filter-name> httpPutFormFilter </ filter-name> 
	<servlet-name> dispatcherServlet </ servlet-name> 
</ filter-mapping> 

<servlet> 
	<servlet-name> dispatcherServlet </ servlet-name> 
	<servlet-class> org.springframework.web。 servlet.DispatcherServlet </ servlet-class> 
</ servlet>

上述过滤器拦截具有内容类型的HTTP PUT和PATCH请求application/x-www-form-urlencoded,从请求的正文 中读取表单数据,并包装ServletRequest以便通过ServletRequest.getParameter*()一系列方法使表单数据可用 。

[Note]
由于HttpPutFormContentFilter消耗了请求的正文,因此不应配置为依赖其他转换器的PUT或PATCH URL application/x-www-form-urlencoded。这包括@RequestBody MultiValueMap<String, String>HttpEntity<MultiValueMap<String, String>>

使用@CookieValue注释映射Cookie值

@CookieValue注释允许将方法参数绑定到HTTP cookie的值。

让我们考虑以下cookie已被接收到http请求:

JSESSIONID = 415A4AC178C59DACE0B2C9CA727CDD84

以下代码示例演示如何获取JSESSIONIDcookie 的值:

@RequestMapping(“/ displayHeaderInfo.do”)
 public  void displayHeaderInfo( @CookieValue(“JSESSIONID”) String cookie){
	 // ... 
}

如果目标方法参数类型不是,则会自动应用类型转换 String。请参阅“方法参数和类型转换”一节

使用@RequestHeader注释映射请求标头属性

@RequestHeader注释允许将一个方法参数绑定到请求头。

以下是一个示例请求标头:

主机本地主机:8080 
接受文本/ html应用程序/ xhtml + xml应用程序/ xml; q = 0.9 
接受语言fr,en-gb; q = 0.7,en; q = 0.3 
接受编码gzip,放大
Accept-Charset ISO -8859-1,utf-8; q = 0.7,*; q = 0.7 
保持活力300

以下代码示例演示了如何获取Accept-EncodingKeep-Alive标题的值:

@RequestMapping(“/ displayHeaderInfo.do”)
 public  void displayHeaderInfo( @RequestHeader(“Accept-Encoding”) String encoding,
		 @RequestHeader(“Keep-Alive”)  long keepAlive){
	 // ... 
}

Type conversion is applied automatically if the method parameter is not String. See the section called “Method Parameters And Type Conversion”.

@RequestHeader注解上的使用Map<String, String>MultiValueMap<String, String>HttpHeaders参数,则地图被填充有所有标头值。

[Tip]
内置支持可用于将逗号分隔的字符串转换为字符串或类型转换系统已知的其他类型的数组/集合。例如,注释的方法参数@RequestHeader("Accept")可以是类型String,也可以是 String[]List<String>

方法参数和类型转换

从请求中提取的基于字符串的值(包括请求参数,路径变量,请求标头和cookie值)可能需要转换为方法参数或字段的目标类型(例如,将请求参数绑定到参数中的字段@ModelAttribute)他们一定会。如果目标类型不是String,Spring将自动转换为相应的类型。支持所有简单的类型,如int,long,Date等。您可以进一步自定义通过转换过程WebDataBinder(见称为“定制WebDataBinder初始化”一节),或者通过注册FormattersFormattingConversionService(参见5.6节,“春字段格式”)。

自定义WebDataBinder初始化

要通过Spring定制与PropertyEditor的请求参数绑定 WebDataBinder,可以使用@InitBinder控制器中的-annotated @InitBinder方法,@ControllerAdvice类中的方法或提供自定义 WebBindingInitializer。有关更多详细信息,请参阅“使用@ControllerAdvice和@RestControllerAdvice建议控制器”一节。

使用@InitBinder自定义数据绑定

注释控制器方法,@InitBinder允许您直接在控制器类中配置Web数据绑定。@InitBinder识别用于初始化WebDataBinder将用于填充命名和表示注释处理程序方法的对象参数的方法。

这种init-binder方法支持方法支持的所有参数@RequestMapping,除了命令/表单对象和相应的验证结果对象。Init-binder方法不能有返回值。因此,它们通常被声明为void。典型的参数包括WebDataBinderWebRequest或者 java.util.Locale允许代码注册上下文相关的编辑器。

以下示例演示@InitBinder如何CustomDateEditor为所有java.util.Date表单属性配置一个 。

@Controller
 public  class MyFormController { @InitBinder protected void initBinder(WebDataBinder binder){
		SimpleDateFormat dateFormat = new SimpleDateFormat( “yyyy-MM-dd”); 
		dateFormat.setLenient(假); 
		binder.registerCustomEditor(日期和CustomDateEditor(日期格式,FALSE)); 
	} // ... 
}

	
	 

或者,从Spring 4.2起,考虑使用addCustomFormatter来指定 Formatter实现而不是PropertyEditor实例。如果您碰巧Formatter在共享FormattingConversionService中安装一个基于安装程序的 方法,那么特别有用的方法可以重用于控制器特定的绑定规则调整。

@Controller
 public  class MyFormController { @InitBinder protected 
		binder.addCustomFormatter( new DateFormatter( “yyyy-MM-dd”)); 
	} // ... 
}

	
	 void initBinder(WebDataBinder binder) {

配置自定义WebBindingInitializer

要外部化数据绑定初始化,您可以提供WebBindingInitializer接口的自定义实现,然后通过为其提供自定义bean配置来启用RequestMappingHandlerAdapter,从而覆盖默认配置。

PetClinic应用程序中的以下示例显示了使用该接口的自定义实现的WebBindingInitializer配置org.springframework.samples.petclinic.web.ClinicBindingInitializer,它配置了几个PetClinic控制器所需的PropertyEditor。

<bean  class = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter” > 
	<property  name = “cacheSeconds”  value = “0” /> 
	<property  name = “webBindingInitializer” > 
		<bean  class = “org .springframework.samples.petclinic.web.ClinicBindingInitializer“ /> 
	</ property> 
</ bean>

@InitBinder方法也可以在一个@ControllerAdvice注释类中定义,在这种情况下,它们适用于匹配控制器。这提供了使用a的替代方法 WebBindingInitializer。有关更多详细信息,请参阅“使用@ControllerAdvice和@RestControllerAdvice建议控制器”一节。

通过@ControllerAdvice和@RestControllerAdvice为控制器提供建议

@ControllerAdvice注释是一个组件注释允许实现类自动检测通过类路径扫描。当使用MVC命名空间或MVC Java配置时,它将自动启用。

带注释的类@ControllerAdvice可以包含@ExceptionHandler@InitBinder@ModelAttribute注解的方法,这些方法将适用于 @RequestMapping所有控制器的层次结构的方法,而不是内声明它们控制器层次。

@RestControllerAdvice是一种@ExceptionHandler方法@ResponseBody,默认情况下方法采用语义。

二者@ControllerAdvice@RestControllerAdvice可以针对控制器的一个子集:

//目标所有使用@RestController注释的控制器
@ControllerAdvice(annotations = RestController.class)
 public  class AnnotationAdvice {} //定位特定包中的所有控制器@ControllerAdvice(“org.example.controllers”) public class BasePackageAdvice {} // Target所有可分配给特定类的控制器@ControllerAdvice(assignableTypes = {ControllerInterface.class,AbstractController.class}) public class AssignableTypesAdvice {}



 



查看 @ControllerAdvice 文档了解更多详细信息。

杰克逊序列化视图支持

有时将内容过滤将被序列化到HTTP响应主体的对象有时是有用的。为了提供这样的功能,Spring MVC内置了对Jackson的Serialization Views进行渲染的支持。

要使用@ResponseBody返回的控制器方法或控制器方法 ResponseEntity,只需@JsonView使用指定要使用的视图类或接口的类参数添加注释:

@RestController
 public  class UserController { @GetMapping(“/ user”)@JsonView(User.WithoutPasswordView.class) public User getUser(){
		 return new User( “eric” “7!jd#h23”); 
	}
}公共用户{公共接口 WithoutPasswordView {};
	public Interface WithPasswordView extends WithoutPasswordView {}; 私人字符串用户名;
	私人字符串密码; public User(){
	}
	} @JsonView(WithoutPasswordView。class) public String getUsername(){
		 return this .username; 
	} @JsonView(WithPasswordView.class) public String getPassword(){
		 return this .password; 
	}
}

	
	
	 

 

	  

	

	

	public User(String username, String password) {
		this.username = username;
		this.password = password;

	
	 

	
[Note]
请注意,尽管@JsonView允许指定多个类,但在控制器方法上的使用只支持一个类参数。如果需要启用多个视图,请考虑使用复合接口。

对于依赖于视图分辨率的控制器,只需将序列化视图类添加到模型中:

@Controller
 public  class UserController extends AbstractController {

	@GetMapping(“/ user”)
	 public String getUser(Model model){
		model.addAttribute(“user”new User(“eric”“7!jd#h23”));
		model.addAttribute(JsonView  .getName(),User.WithoutPasswordView。);
		返回 “userView” ;
	}
}

杰克逊JSONP支持

为了启用JSONP支持@ResponseBodyResponseEntity方法,声明一个@ControllerAdvice扩展的bean, AbstractJsonpResponseBodyAdvice如下所示,构造函数参数指示JSONP查询参数名称:

@ControllerAdvice
 public  class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

	public JsonpAdvice(){
		 super“callback”);
	}
}

对于依赖于视图分辨率的控制器,当请求具有名为jsonp或者的查询参数时,将自动启用JSONP callback。这些名字可以通过jsonpParameterNames财产定制。

18.3.4异步请求处理

Spring MVC 3.2介绍了基于Servlet 3的异步请求处理。与往常一样,一个控制器方法现在可以返回一个java.util.concurrent.Callable并从Spring MVC管理的线程生成返回值,而不是返回一个 值。同时,主要的Servlet容器线程被退出并被释放并允许处理其他请求。Spring MVC Callable在一个单独的线程中调用一个单独的线程TaskExecutor,当Callable返回时,请求被分派回到Servlet容器,以使用返回的值来恢复处理Callable。这是一个这样一个控制器方法的例子:

@PostMapping
 public Callable <String> processUpload( final MultipartFile文件){

	返回 新的 Callable <String>(){
		 public String call()throws Exception {
			 // ... 
			return  “someView” ;
		}
	};

}

另一个选项是控制器方法返回一个实例DeferredResult。在这种情况下,返回值也将从任何线程生成,即不由Spring MVC管理的线程。例如,可以响应于诸如JMS消息,计划任务等的一些外部事件而产生结果。这是一个这样一个控制器方法的例子:

@RequestMapping(“/ quotes”)
@ResponseBody
 public DeferredResult <String> quotes(){
	DeferredResult <String> deferredResult = new DeferredResult <String>();
	//将deferredResult保存在某个地方.. 
	return deferredResult;
}

//在其他一些线程中... 
deferredResult.setResult(data);

没有任何Servlet 3.0异步请求处理功能的知识可能难以理解。这肯定有助于阅读。以下是有关基本机制的几个基本事实:

  • A ServletRequest可以通过调用进入异步模式request.startAsync()。这样做的主要作用是Servlet以及任何过滤器都可以退出,但响应将保持开放状态,以便稍后完成处理。
  • 可以用于进一步控制异步处理的request.startAsync()返回 调用AsyncContext。例如,它提供的方法dispatch类似于Servlet API中的转发,但它允许应用程序在Servlet容器线程上恢复请求处理。
  • ServletRequest提供对当前DispatcherType可用于处理所述初始请求,一个异步调度,正向,以及其他的调度类型之间进行区分。

考虑到上述情况,以下是异步请求处理的事件的顺序Callable

  • 控制器返回a Callable
  • Spring MVC启动异步处理,并在单独的线程中提交Callable到一个TaskExecutor进行处理。
  • DispatcherServlet所有过滤器的退出Servlet容器线程,但反应仍然开放。
  • 所述Callable产生的结果和Spring MVC分派请求回Servlet容器以恢复处理。
  • DispatcherServlet再次调用和处理与来自所述异步生产结果恢复Callable

序列DeferredResult非常相似,除了由应用程序产生任何线程的异步结果:

  • 控制器返回一个DeferredResult并将其保存在某些内存中的队列或列表中,可以访问它。
  • Spring MVC启动异步处理。
  • DispatcherServlet所有配置的过滤器的退出请求处理线程,但反应仍然开放。
  • 应用程序DeferredResult从一些线程设置,Spring MVC将请求返回给Servlet容器。
  • DispatcherServlet再次调用和处理与异步生产结果恢复。

有关异步请求处理的动机的进一步背景,何时或为什么使用它,请阅读 此博客文章系列

异步请求异常处理

如果Callable从控制器方法返回的值在执行时引发异常,会发生什么?简短的答案与控制器方法引发异常时发生的情况相同。它经历了常规异常处理机制。更长的解释是,当Callable 一个Exception Spring MVC调度到具有Exception结果的Servlet容器并导致恢复请求处理时,Exception而不是控制器方法返回值。使用时,DeferredResult您可以选择是否调用 setResultsetErrorResult使用Exception实例。

拦截异步请求

HandlerInterceptor还可以实现AsyncHandlerInterceptor以执行afterConcurrentHandlingStarted回调,这就是所谓的代替postHandleafterCompletion处理开始异步时。

A HandlerInterceptor还可以注册一个CallableProcessingInterceptor 或一个DeferredResultProcessingInterceptor以更深入地与异步请求的生命周期集成,例如处理超时事件。有关AsyncHandlerInterceptor 详细信息,请参阅Javadoc 。

DeferredResult类型还提供了诸如onTimeout(Runnable)onCompletion(Runnable)。有关DeferredResult详细信息,请参阅Javadoc 。

使用时,Callable您可以使用一个实例来包装它WebAsyncTask ,它还提供了超时和完成的注册方法。

HTTP流式传输

控制器方法可以异步地使用DeferredResultCallable产生其返回值,并且可以用于实现诸如 长轮询的技术 ,其中服务器可以尽快将事件推送到客户端。

如果您想在单个HTTP响应中推送多个事件怎么办?这是一个与“长查询”相关的技术,被称为“HTTP流”。Spring MVC可以通过ResponseBodyEmitter可以用于发送多个对象的返回值类型来实现,而不是像通常情况那样@ResponseBody发送的对象,其中每个发送的对象都被写入到响应中HttpMessageConverter

这是一个例子:

@RequestMapping(“/ events”)
 public ResponseBodyEmitter handle(){
	ResponseBodyEmitter emitter = new ResponseBodyEmitter();
	//保存发射的地方.. 
	返回发射器;
}

//在其他一些线程 
emit.send(“Hello once”);

//再次在 
emitter.send(“Hello again”);

//并在某个时候完成 
emitter.complete();

注意,ResponseBodyEmitter也可以用作身体 ResponseEntity,以便自定义响应的状态和标题。

HTTP流与服务器发送的事件

SseEmitterResponseBodyEmitter服务器发送事件提供支持 的子类。服务器发送的事件是相同的“HTTP流”技术的另一个变体,除了从服务器推送的事件根据W3C服务器发送事件规范进行格式化。

服务器发送事件可以用于其预期目的,即将事件从服务器推送到客户端。在Spring MVC中很容易做到,只需返回一个类型的值即可SseEmitter

请注意,Internet Explorer不支持服务器发送事件,而对于更高级的Web应用程序消息传递场景(如在线游戏,协作,财务应用程序等),最好考虑Spring的WebSocket支持,其中包括SockJS风格的WebSocket仿真回落到非常广泛的浏览器(包括Internet Explorer)以及更高级别的消息传递模式,用于通过更多以消息为中心的体系结构中的发布订阅模型与客户端进行交互。有关进一步的背景,请参阅 以下博文

HTTP直接流向OutputStream

ResponseBodyEmitter允许通过将对象写入响应来发送事件HttpMessageConverter。这可能是最常见的情况,例如在编写JSON数据时。但是有时候,绕过邮件转换并直接写入响应OutputStream (例如文件下载)是有用的。这可以在StreamingResponseBody返回值类型的帮助下完成 。

这是一个例子:

@RequestMapping(“/ download”)
 public StreamingResponseBody handle(){
	 return  new StreamingResponseBody(){@
		 Override
		 public  void writeTo(OutputStream outputStream) throws IOException {
			 // write ...
		}
	};
}

注意,StreamingResponseBody也可以用作身体 ResponseEntity,以便自定义响应的状态和标题。

配置异步请求处理

Servlet容器配置

对于配置web.xml为确保更新到版本3.0的应用程序:

<web-app  xmlns = “http://java.sun.com/xml/ns/javaee” 
	xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance” 
			http:// java。 sun.com/xml/ns/javaee 
			http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd” 
	版本 = “3.0” >

	...

</ web的应用>

必须在DispatcherServlet通过 <async-supported>true</async-supported>子元素中启用异步支持web.xml。另外,任何Filter参与异步请求处理的任务都必须配置为支持ASYNC分派器类型。对于Spring Framework提供的所有过滤器,ASYNC调度器类型应该是安全的,因为它们通常是扩展的OncePerRequestFilter,并且具有对过滤器是否需要参与异步调度的运行时间检查。

以下是一些web.xml配置示例:

<web-app  xmlns = “http://java.sun.com/xml/ns/javaee” 
	xmlns:xsi = “http://www.w3.org/2001/XMLSchema-instance” 
	xsi:schemaLocation = “
			http://java.sun.com/xml/ns/javaee
			http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd” 
	版本 = “3.0” >

	<filter> 
		<filter-name> Spring OpenEntityManagerInViewFilter </ filter-name> 
		<filter-class> org.springframework。〜.OpenEntityManagerInViewFilter </ filter-class> 
		<async-supported> true </ async-supported> 
	</ filter>

	<滤波器映射> 
		<滤波器名称>春OpenEntityManagerInViewFilter </过滤器名称> 
		<URL模式> / * </ URL模式> 
		<调度> REQUEST </调度程序> 
		<调度> ASYNC </调度程序> 
	</过滤器-mapping>

</ web的应用>

如果使用Servlet 3,例如通过基于Java的配置WebApplicationInitializer,您还需要像之前一样设置“asyncSupported”标志以及ASYNC调度器类型web.xml。为了简化所有这些配置,请考虑扩展 AbstractDispatcherServletInitializer或更好地 AbstractAnnotationConfigDispatcherServletInitializer自动设置这些选项,并使其很容易注册Filter实例。

Spring MVC配置

MVC Java配置和MVC命名空间提供了配置异步请求处理的选项。WebMvcConfigurer具有该方法 configureAsyncSupport同时<mvc:annotation-driven>具有<async-support>子元素。

这些允许您配置用于异步请求的默认超时值,如果未设置,则取决于底层的Servlet容器(例如Tomcat上的10秒)。您还可以配置一个AsyncTaskExecutor用于执行Callable从控制器方法返回的实例。强制建议配置此属性,默认情况下Spring MVC使用SimpleAsyncTaskExecutor。MVC Java配置和MVC命名空间也允许您注册CallableProcessingInterceptorDeferredResultProcessingInterceptor实例。

如果需要覆盖特定的默认超时值DeferredResult,可以使用适当的类构造函数来实现。类似地,对于a Callable,可以将它包装在一个WebAsyncTask并使用适当的类构造函数来自定义超时值。类的构造函数WebAsyncTask也允许提供一个 AsyncTaskExecutor

18.3.5测试控制器

spring-test模块提供一流的支持,用于测试带注释的控制器。参见第11.6节“Spring MVC测试框架”

18.4处理程序映射

在以前的Spring版本中,用户需要HandlerMapping在Web应用程序上下文中定义一个或多个 bean,以将传入的Web请求映射到适当的处理程序。通过引入注释控制器,您通常不需要这样做,因为它RequestMappingHandlerMapping@RequestMapping自动在所有@Controllerbean 上查找 注释。但是,请记住,所有HandlerMapping扩展的类AbstractHandlerMapping都具有以下可用于自定义行为的属性:

  • interceptors要使用的拦截器列表。HandlerInterceptor第18.4.1节“使用HandlerInterceptor拦截请求”中讨论
  • defaultHandler 当这个处理程序映射不会导致一个匹配的处理程序时,使用默认处理程序。
  • order基于order属性的值(参见 org.springframework.core.Ordered接口),Spring会排序上下文中可用的所有处理程序映射,并应用第一个匹配处理程序。
  • alwaysUseFullPath如果trueSpring使用当前Servlet上下文中的完整路径来找到一个适当的处理程序。如果false(默认值),则使用当前Servlet映射中的路径。例如,如果使用Servlet /testing/*并将alwaysUseFullPath属性设置为true, /testing/viewPage.html则使用该属性,而如果该属性设置为false/viewPage.html
  • urlDecode默认为true,从Spring 2.5开始。如果您喜欢比较编码路径,请将此标志设置为false。但是,HttpServletRequest始终以解码形式公开Servlet路径。请注意,与编码路径相比,Servlet路径将不匹配。

以下示例显示如何配置拦截器:

<bean> 
	<bean  class = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping” > 
		<property  name = “interceptors” > 
			<bean  class = “example.MyInterceptor” /> 
		</ property> 
	</ bean> 
</ beans>

18.4.1用HandlerInterceptor拦截请求

Spring的处理程序映射机制包括处理程序拦截器,当您希望将特定功能应用于某些请求时,例如,检查主体,这是有用的。

位于处理程序映射中的拦截器必须HandlerInterceptororg.springframework.web.servlet包中实现。这个接口定义了三个方法: preHandle(..)被称为被执行的实际处理程序; postHandle(..)被称为执行的处理程序; 并在完成请求完成后afterCompletion(..)调用。这三种方法应提供足够的灵活性进行各种预处理和后处理。

preHandle(..)方法返回一个布尔值。您可以使用此方法来中断或继续处理执行链。当此方法返回true时,处理程序执行链将继续; 当它返回false时,DispatcherServlet 假定拦截器本身已经处理了请求(并且例如呈现适当的视图),并且不会继续执行其他拦截器和执行链中的实际处理程序。

拦截器可以使用interceptors属性进行配置,该属性存在于所有HandlerMapping类中AbstractHandlerMapping。这在下面的示例中显示:

<bean> 
	<bean  id = “handlerMapping” 
			class = “org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping” > 
		<property  name = “interceptors” > 
			<list> 
				<ref  bean = “officeHoursInterceptor” /> 
			</ list> 
		</ property> 
	</ bean>

	<bean  id = “officeHoursInterceptor” 
			class = “samples.TimeBasedAccessInterceptor” > 
		<property  name = “openingTime”  value = “9” /> 
		<property  name = “closingTime”  value = “18” /> 
	</ bean> 
</ beans >
包装样品;

public  class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {

	private  int openingTime;
	private  int closingTime;

	public  void setOpeningTime(int openingTime){
		 this .openingTime = openingTime;
	}

	public  void setClosingTime(int closingTime){
		 this .closingTime = closingTime;
	}

	public  boolean preHandle(HttpServletRequest request,HttpServletResponse response,
			对象处理程序)throws Exception {
		日历cal = Calendar.getInstance();
		int hour = cal.get(HOUR_OF_DAY);
		if(openingTime <= hour && hour <closingTime){
			 return true;
		}
		response.sendRedirect(“http://host.com/outsideOfficeHours.html”);
		返回假
	}
}

该映射处理的任何请求都被截取TimeBasedAccessInterceptor。如果当前时间在办公时间之外,用户将被重定向到静态HTML文件,例如,您只能在办公时间内访问该网站。

[注意]
当使用RequestMappingHandlerMapping实际处理程序时,HandlerMethod它的一个实例 标识将被调用的特定控制器方法。

您可以看到,Spring适配器类HandlerInterceptorAdapter可以更容易地扩展HandlerInterceptor接口。

[小费]
在上面的示例中,配置的拦截器将应用于使用注释控制器方法处理的所有请求。如果要缩小拦截器应用的URL路径,可以使用MVC命名空间或MVC Java配置,或声明类型的bean实例MappedInterceptor。请参见第18.16.1节“启用MVC Java配置或MVC XML命名空间”

请注意,该postHandle方法HandlerInterceptor并不总是非常适用于@ResponseBodyResponseEntity方法。在这种情况下HttpMessageConverter ,在postHandle调用之前写入并提交响应,这使得不可能更改响应,例如添加标题。相反,应用程序可以实现 ResponseBodyAdvice并将其声明为@ControllerAdvicebean或直接配置它RequestMappingHandlerAdapter

18.5解决观点

用于Web应用程序的所有MVC框架提供了一种解决视图的方法。Spring提供视图解析器,使您可以在浏览器中渲染模型,而不需要将您视为特定的视图技术。开箱即用,例如,Spring允许您使用JSP,FreeMarker模板和XSLT视图。请参见第19章,查看技术对于如何整合并使用不同的视图技术的讨论。

对于Spring处理视图的方式来说重要的两个接口是ViewResolverView。所述ViewResolver提供视图名称和实际视图之间的映射。该View接口解决了请求的准备,并将请求交给一种视图技术。

18.5.1使用ViewResolver界面解析视图

正如在讨论第18.3节,“实施控制器”,在Spring Web MVC框架控制器的所有处理方法必须解析为一个逻辑视图名称,明确地(例如,通过返回 StringViewModelAndView)或隐式(即基于惯例)。Spring中的视图由逻辑视图名称解析,并由视图解析器解析。春天有不少视角解析器。这张表列出了大部分; 以下几个例子。

表18.3。查看解析器

视图解析器 描述
AbstractCachingViewResolver 抽象视图解析器缓存视图。通常情况下,需要准备才能使用; 扩展此视图解析器提供缓存。
XmlViewResolver 实现ViewResolver它接受使用与Spring的XML bean工厂相同的DTD使用XML编写的配置文件。默认配置文件是/WEB-INF/views.xml
ResourceBundleViewResolver 实现ViewResolver它使用bean定义ResourceBundle,由bundle基本名称指定。通常,您可以在属性文件中定义bundle,该属性文件位于类路径中。默认文件名是views.properties
UrlBasedViewResolver 简单地实现ViewResolver了直接解析逻辑视图名称到URL的接口,而没有明确的映射定义。如果您的逻辑名称以直观的方式与视图资源的名称匹配,则这是适当的,而不需要任意映射。
InternalResourceViewResolver 方便的子类UrlBasedViewResolver支持InternalResourceView(实际上是Servlet和JSP)和子类,如JstlViewTilesView。您可以通过使用为此解析器生成的所有视图指定视图类 setViewClass(..)。有关UrlBasedViewResolver详细信息,请参阅javadoc。
FreeMarkerViewResolver 它的便利子类UrlBasedViewResolver支持FreeMarkerView和自定义子类。
ContentNegotiatingViewResolver 实现ViewResolver根据请求文件名或Accept头解析视图的界面。请参见第18.5.4节“ContentNegotiatingViewResolver”


例如,使用JSP作为视图技术,可以使用UrlBasedViewResolver。此视图解析器将视图名称转换为URL,并将请求转交给RequestDispatcher以呈现视图。

<bean  id = “viewResolver” 
		class = “org.springframework.web.servlet.view.UrlBasedViewResolver” > 
	<property  name = “viewClass”  value = “org.springframework.web.servlet.view.JstlView” /> 
	<property  name = “prefix”  value = “/ WEB-INF / jsp /” /> 
	<property  name = “suffix”  value = “.jsp” /> 
</ bean>

test以逻辑视图名称返回时,此视图解析器将请求转发到RequestDispatcher将要发送的请求/WEB-INF/jsp/test.jsp

当您在Web应用程序中组合不同的视图技术时,可以使用 ResourceBundleViewResolver

<bean  id = “viewResolver” 
		class = “org.springframework.web.servlet.view.ResourceBundleViewResolver” > 
	<property  name = “basename”  value = “views” /> 
	<property  name = “defaultParentView”  value = “parentView” / > 
</ bean>

ResourceBundleViewResolver考察ResourceBundle确定了基本名字和它应该解决每个视图,它使用属性的值 [viewname].(class)作为视图类和属性的值[viewname].url作为视图的URL。示例可以在下一章中找到,涵盖视图技术。您可以看到,您可以识别父视图,从属性文件中的所有视图“扩展”。这样,您可以指定默认视图类。

[注意]
AbstractCachingViewResolver他们解析的缓存视图实例的子类。缓存提高了某些视图技术的性能。可以通过将cache属性设置为关闭缓存false。此外,如果您必须在运行时刷新某个视图(例如,当FreeMarker模板被修改时),则可以使用该removeFromCache(String viewName, Locale loc)方法。

18.5.2链接视图解析器

Spring支持多个视图解析器。因此,您可以链接解析器,并且在某些情况下例如覆盖特定视图。您可以通过在应用程序上下文中添加多个解析器来链接视图解析器,如有必要,可以通过设置 order属性来指定排序。记住,order属性越高,视图解析器在链中的位置越晚。

在以下示例中,视图解析器由两个解析器组成,一个 InternalResourceViewResolver始终自动定位为链中的最后一个解析器,另一个XmlViewResolver用于指定Excel视图。Excel不支持Excel视图InternalResourceViewResolver

<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>

<! -  in views.xml  - >

<bean> 
	<bean  name = “report”  class = “org.springframework.example.ReportExcelView” /> 
</ beans>

如果一个特定的视图解析器不会产生视图,Spring会检查其他视图解析器的上下文。如果存在另外的视图解析器,Spring会继续检查它们,直到视图解决。如果没有视图解析器返回一个视图,Spring会抛出一个 ServletException

视图解析器的合同指定视图解析器可以返回null以指示无法找到视图。然而,并不是所有的视图解析器都这样做,因为在某些情况下,解析器根本无法检测视图是否存在。例如,内部InternalResourceViewResolver使用RequestDispatcher,分派是确定JSP是否存在的唯一方法,但此操作只能执行一次。对于FreeMarkerViewResolver其他一些人也是如此。检查特定视图解析器的javadoc以查看是否报告不存在的视图。因此,把一个InternalResourceViewResolver在链中比在链中的最后结果的其他地方没有得到充分检验,因为 InternalResourceViewResolver意志总是返回一个视图!

18.5.3重定向到视图

如前所述,控制器通常返回逻辑视图名称,视图解析器解析为特定视图技术。对于视图技术如JSP,其通过servlet或JSP引擎处理,此分辨率通常是通过组合处理InternalResourceViewResolverInternalResourceView,它发出一个内部正向或包括经由在Servlet API的RequestDispatcher.forward(..)方法或RequestDispatcher.include()方法。对于其他视图技术,如FreeMarker,XSLT等,视图本身将内容直接写入响应流。

在呈现视图之前,有时需要将HTTP重定向发回客户端。这是可取的,例如,当一个控制器已经被调用了 POST数据时,并且响应实际上是对另一个控制器的委派(例如,成功的表单提交)。在这种情况下,正常的内部向前将意味着另一个控制器也将看到相同的POST数据,如果它可能与其他预期数据混淆,这是潜在的问题。在显示结果之前执行重定向的另一个原因是消除用户多次提交表单数据的可能性。在这种情况下,浏览器将首先发送一个初始的POST; 然后会收到重定向到其他URL的响应; GET最后浏览器将为重定向响应中指定的URL执行后续操作。因此,从浏览器的角度来看,当前页面并不反映的结果POST,而是一个GET。最终的效果是用户无法POST通过执行刷新来意外重新获得相同的数据。刷新强制GET结果页面a,而不是重新发送初始POST数据。

RedirectView的

作为控制器响应的结果强制重定向的一种方法是控制器创建并返回Spring的实例RedirectView。在这种情况下, DispatcherServlet不使用普通视图分辨机制。而是因为已经给了(重定向)视图,DispatcherServlet简单地指示视图来完成它的工作。将RedirectView依次调用HttpServletResponse.sendRedirect() 发送一个HTTP重定向到客户端浏览器。

如果使用RedirectView并且视图由控制器本身创建,则建议您将重定向URL配置为注入到控制器中,以使其不会被烘烤到控制器中,而是在上下文中配置视图名称。在一节“重定向:前缀”有利于这种脱钩。

将数据传递到重定向目标

默认情况下,所有模型属性都被认为是重定向URL中的URI模板变量。在剩余的属性中,原始类型或原始类型的集合/数组的那些属性将自动附加为查询参数。

如果为重定向准备了模型实例,则将原始类型属性作为查询参数附加可能是所需的结果。然而,在注释控制器中,模型可能包含为渲染目的添加的附加属性(例如下拉字段值)。为了避免这种属性出现在URL中的可能性,一种@RequestMapping方法可以声明一个类型的参数,RedirectAttributes并使用它来指定可供使用的确切属性RedirectView。如果方法重定向,则使用内容RedirectAttributes。否则使用模型的内容。

RequestMappingHandlerAdapter提供了一个名为标志 "ignoreDefaultModelOnRedirect",可以用来表示默认的内容 Model,如果一个控制器方法重定向不应该被使用。相反,控制器方法应该声明一个类型的属性,RedirectAttributes或者如果它不这样做,则不应该传递任何属性RedirectView。MVC命名空间和MVC Java配置都将此标志设置false为保持向后兼容性。但是,对于新的应用程序,我们建议将其设置为true

请注意,当扩展重定向网址时,来自本请求的URI模板变量将自动提供,并且不需要通过Model或不显式添加RedirectAttributes。例如:

@PostMapping(“/ files / {path}”)
 public String upload(...){
	 // 
	返回 “redirect:files / {path}” ;
}

将数据传递到重定向目标的另一种方法是通过Flash属性。与其他重定向属性不同,Flash属性保存在HTTP会话中(因此不会出现在URL中)。有关详细信息,请参见第18.6节“使用Flash属性”

重定向:前缀

虽然使用RedirectView工程正常,如果控制器本身创建 RedirectView,则没有避免控制器知道重定向发生的事实。这是非常不合时宜的事情,太紧密地结合在一起。控制器不应该真正关心响应如何处理。一般来说,它应该仅在注入到其中的视图名称的方式操作。

特殊的redirect:前缀允许你完成这个。如果返回具有前缀的视图名称redirect:,则UrlBasedViewResolver(和所有子类)将会将其识别为需要重定向的特殊指示。视图名称的其余部分将被视为重定向网址。

净效果与控制器返回一样RedirectView,但现在控制器本身可以简单地按逻辑视图名称进行操作。一个逻辑视图名称,例如redirect:/myapp/some/resource将重定向到当前的Servlet上下文,而一个名称redirect:http://myhost.com/some/arbitrary/path 将重定向到绝对URL。

请注意,控制器处理程序使用注释@ResponseStatus,注释值优先于设置的响应状态RedirectView

转发:前缀

也可以使用forward:最终由子类决定的视图名称的特殊前缀UrlBasedViewResolver。这将创建一个 视图名称InternalResourceView(其最终将RequestDispatcher.forward()围绕其被视为URL)的视图名称。因此,这个前缀对于InternalResourceViewResolverInternalResourceView(对于JSP而言)不是有用的。但是,当您主要使用另一种视图技术时,前缀可能会有所帮助,但是仍然希望强制转发由Servlet / JSP引擎处理的资源。(请注意,您也可以链接多个视图解析器。)

redirect:前缀一样,如果具有前缀的视图名称forward:注入到控制器中,则控制器在处理响应方面没有检测到发生任何特殊事件。

18.5.4 ContentNegotiatingViewResolver

ContentNegotiatingViewResolver不会解析视图本身,而是委托给其他视图解析器,选择类似于客户端请求的表示的视图。客户端可以从服务器请求表示方式存在两种策略:

[注意]
Accept标题的一个问题是,不可能在HTML中的Web浏览器中设置它。例如,在Firefox中,它被修改为:

接受:text / html,application / xhtml + xml,application / xml; q = 0.9,* / *; q = 0.8

因此,在开发基于浏览器的Web应用程序时,通常会看到每个表示使用不同的URI。

为了支持资源的多个表示,Spring提供了 ContentNegotiatingViewResolver根据AcceptHTTP请求的文件扩展名或头部来解析视图。ContentNegotiatingViewResolver不执行视图分辨率本身,而是委托给您通过bean属性指定的视图解析器的列表ViewResolvers

ContentNegotiatingViewResolver选择一个合适的View通过比较与所述媒体类型(也被称为媒体请求类型(一个或多个),以处理该请求 Content-Type由支持)的View与每个其相关联ViewResolversView具有兼容性的列表中的第一个将表示Content-Type返回给客户端。如果链条不能提供兼容的视图,则会查看ViewResolver通过DefaultViews属性指定的视图列表。后一个选项适用于Views可以呈现当前资源的适当表示的单例,而不管逻辑视图名称如何。的Accept 报头可以包括通配符,例如text/*,在这种情况下View,其内容类型是text/xml为相容的匹配。

要支持基于文件扩展名的视图的自定义解析,请使用 ContentNegotiationManager:请参见第18.16.6节“内容协商”

以下是一个示例配置ContentNegotiatingViewResolver

<bean  class = “org.springframework.web.servlet.view.ContentNegotiatingViewResolver” > 
	<property  name = “viewResolvers” > 
		<list> 
			<bean  class = “org.springframework.web.servlet.view.BeanNameViewResolver” /> 
			<bean  class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > 
				<property  name = “prefix”  value = “/ WEB-INF / jsp /” /> 
				<property  name = “suffix” value = “.jsp” /> 
			</ bean> 
		</ list> 
	</ property> 
	<property  name = “defaultViews” > 
		<list> 
			<bean  class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> 
		</ list> 
	</ property> 
</ bean>web.servlet.view.json.MappingJackson2JsonView“ /> </ list> </ property> </ bean>web.servlet.view.json.MappingJackson2JsonView“ /> </ list> </ property> </ bean>

<bean  id = “content”  class = “com.foo.samples.rest.SampleContentAtomView” />

InternalResourceViewResolver手柄视图名称和JSP页面的翻译,而BeanNameViewResolver返回基于bean的名称的视图。(有关Spring如何查找和实例化视图的更多详细信息,请参阅“ 使用ViewResolver界面解析视图”。)在此示例中,该content bean是继承的类,该类AbstractAtomFeedView返回Atom RSS提要。有关创建Atom Feed表示的更多信息,请参阅Atom视图。

在上述配置中,如果使用扩展名进行请求.html,视图解析器将查找与text/html媒体类型匹配的视图。在 InternalResourceViewResolver提供了用于匹配视图text/html。如果请求是使用文件扩展名.atom,视图解析器将查找与application/atom+xml媒体类型相匹配的视图。该视图由该 BeanNameViewResolver映射提供给SampleContentAtomView如果返回的视图名称是content。如果使用文件扩展名进行请求.json,则无论视图名称如何, MappingJackson2JsonView将从DefaultViews列表中选择实例。或者,客户端请求可以在没有文件扩展名的情况下进行,但是将Accept标题设置为首选媒体类型,并且将发生对视图请求的相同解析。

[注意]
如果“ContentNegotiatingViewResolver”的ViewResolver列表未被明确配置,它会自动使用应用程序上下文中定义的任何ViewResolvers。

相应的控制器代码返回表单的URI http://localhost/content.atomhttp://localhost/content应用Accept程序/ atom + xml 的头部的Atom RSS提要 如下所示。

@Controller
 public  class ContentController {

	private List <SampleContent> contentList = new ArrayList <SampleContent>();

	@GetMapping(“/ content”)
	 public ModelAndView getContent(){
		ModelAndView mav = new ModelAndView();
		mav.setViewName(“content”);
		mav.addObject(“sampleContentList”,contentList);
		返回 mav
	}

}

18.6使用flash属性

Flash属性为一个请求存储旨在用于另一个的属性提供了一种方法。这是重定向时最常用的 – 例如 Post / Redirect / Get模式。闪存属性在重定向(通常在会话中)之前临时保存,以便在重定向后立即对请求提供可用的请求。

Spring MVC有两个主要的抽象支持Flash属性。FlashMap用于保存Flash属性,FlashMapManager用于存储,检索和管理 FlashMap实例。

Flash属性支持始终是“开”的,不需要明确启用,尽管如果不使用它,它不会导致HTTP会话创建。在每个请求上都有一个“输入” FlashMap,它具有从先前的请求传递的属性(如果有的话)和FlashMap带有属性的“输出” ,以保存后续请求。这两个FlashMap实例可以通过静态方法在Spring MVC中从任何地方访问RequestContextUtils

注释控制器通常不需要FlashMap直接使用。相反, @RequestMapping方法可以接受类型的参数,RedirectAttributes并使用它来为重定向方案添加闪存属性。添加的Flash属性将 RedirectAttributes自动传播到“输出”FlashMap。类似地,在重定向之后,来自“输入” FlashMap的属性将自动添加到 Model为目标URL提供服务的控制器中。

18.7构建URI

Spring MVC提供了一种用于使用UriComponentsBuilder和构建和编码URI的机制 UriComponents

例如,您可以扩展和编码URI模板字符串:

UriComponents uriComponents = UriComponentsBuilder.fromUriString(
		 “http://example.com/hotels/{hotel}/bookings/{booking}”).build();

URI uri = uriComponents.expand(“42”“21”).encode()。toUri();

请注意,这UriComponents是不可变的expand()encode()如果需要,并且操作返回新的实例。

您还可以使用各个URI组件进行扩展和编码:

UriComponents uriComponents = UriComponentsBuilder.newInstance()
		.scheme(“http”). host“example.com”).path(“/酒店/ {酒店} /预订/ {预订}”).build()
		.expand(“42”“21”)
		.encode();

在Servlet环境中,ServletUriComponentsBuilder子类提供静态工厂方法从Servlet请求中复制可用的URL信息:

HttpServletRequest request = ...

//重新使用host,scheme,port,path和query string 
//替换“accountId”查询参数

ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request)
		.replaceQueryParam(“accountId”“{id}”).build()
		.expand(“123”)
		.encode();

或者,您可以选择复制可用信息的子集,直到并包括上下文路径:

//重新使用主机,端口和上下文路径
//将“/ accounts”附加到路径

ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request)
		.path(“/ accounts”).build()

或者在DispatcherServlet按名称(例如/main/*)映射的情况下,还可以包含servlet映射的文字部分:

//重新使用主机,端口,上下文路径
//将servlet映射的文字部分附加到路径
//将“/ accounts”附加到路径

ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request)
		.path(“/ accounts”).build()

18.7.1构建控制器和方法的URI

Spring MVC还提供了一种构建控制器方法链接的机制。例如,给出:

@Controller 
@RequestMapping(“/ hotels / {hotel}”)
 public  class BookingController {

	@GetMapping(“/ bookings / {booking}”)
	 public String getBooking( @PathVariable Long booking){

	// ...
       }
}

您可以通过名称参考方法来准备链接:

UriComponents uriComponents = MvcUriComponentsBuilder
	.fromMethodName(BookingController “getBooking” 21).buildAndExpand(42);

URI uri = uriComponents.encode()。toUri();

在上面的例子中,我们提供了实际的方法参数值,在这种情况下是长值21,用作路径变量并插入到URL中。此外,我们提供了值42,以填充任何剩余的URI变量,例如从类型级请求映射继承的“hotel”变量。如果该方法有更多的参数,您可以为URL不需要的参数提供null。一般而言@PathVariable@RequestParam参数与构造URL相关。

还有其他的使用方法MvcUriComponentsBuilder。例如,您可以使用类似于通过代理模拟测试的技术,以避免通过名称引用控制器方法(示例假定静态导入MvcUriComponentsBuilder.on):

UriComponents uriComponents = MvcUriComponentsBuilder
	.fromMethodCall(上(BookingController。).getBooking(21))buildAndExpand(42);

URI uri = uriComponents.encode()。toUri();

上面的例子使用静态方法MvcUriComponentsBuilder。在内部,他们依靠ServletUriComponentsBuilder从当前请求的方案,主机,端口,上下文路径和servlet路径准备基本URL。这在大多数情况下运行良好,但有时可能不足。例如,您可能不在请求的上下文中(例如,准备链接的批处理),或者您可能需要插入路径前缀(例如,从请求路径中删除并需要重新插入到链接中的区域设置前缀)。

对于这种情况,您可以使用接受a UriComponentsBuilder使用基本URL 的静态“fromXxx”重载方法 。或者您可以MvcUriComponentsBuilder 使用基本URL 创建一个实例,然后使用基于实例的“withXxx”方法。例如:

UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath()。path(“/ en”);
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);
builder.withMethodCall(上(BookingController。).getBooking(21))buildAndExpand(42);

URI uri = uriComponents.encode()。toUri();

18.7.2构建来自视图的控制器和方法的URI

您还可以从JSP,Thymeleaf,FreeMarker等视图中构建带注释控制器的链接。这可以使用fromMappingName方法,MvcUriComponentsBuilder 其中指的是按名称映射。

每个都会@RequestMapping根据类的大写字母和完整的方法名称分配一个默认名称。例如,getFooFooController 中的方法被分配名称“FC#getFoo”。该策略可以通过创建一个实例HandlerMethodMappingNamingStrategy并将其插入到您中 来进行替换或定制RequestMappingHandlerMapping。默认策略实现也会查看name属性@RequestMapping,如果存在,则使用该属性。这意味着如果分配的默认映射名称与另一个冲突(例如重载方法),则可以在该目录上明确指定一个名称@RequestMapping

[注意]
分配的请求映射名称在启动时记录在TRACE级别。

Spring JSP标签库提供了一个名为mvcUrl该函数的函数,可用于根据此机制准备到控制器方法的链接。

例如:

@RequestMapping(“/ people / {id} / addresses”)
 public  class PersonAddressController {

    @RequestMapping(“/ {country}”)
     public HttpEntity getAddress( @PathVariable String country){...}
}

您可以从JSP准备一个链接,如下所示:

%@ taglib uri =“http://www.springframework.org/tags”prefix =“s”%>
...
<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">获取地址</a>

上述示例依赖于mvcUrl在Spring标签库(即META-INF / spring.tld)中声明的JSP函数。对于更高级的案例(例如上一节所述的自定义基本URL),可以轻松地定义自己的函数或使用自定义标记文件,以便使用MvcUriComponentsBuilder具有自定义基本URL 的特定实例。

18.8使用区域设置

Spring的大部分架构支持国际化,就像Spring Web MVC框架一样。DispatcherServlet使您能够使用客户端的语言环境自动解析邮件。这是用LocaleResolver对象完成的。

当一个请求进来时,DispatcherServlet寻找一个区域设置解析器,如果它找到一个它试图使用它来设置区域设置。使用该RequestContext.getLocale() 方法,您可以随时检索由语言环境解析器解析的区域设置。

除了自动区域设置解析之外,您还可以将拦截器附加到处理程序映射(参见第18.4.1节“拦截与HandlerInterceptor的请求”以获取有关处理程序映射拦截器的更多信息),以在特定情况下更改区域设置,例如,基于请求中的参数。

org.springframework.web.servlet.i18n程序包中定义了语言环境解析器和拦截器, 并以正常方式在应用程序上下文中配置。以下是Spring中包含的区域解析器的选择。

18.8.1获取时区信息

除了获取客户的区域设置之外,了解他们的时区通常也是有用的。该LocaleContextResolver界面提供了一个扩展LocaleResolver,允许解析器提供更丰富LocaleContext,可能包括时区信息。

如果可用,TimeZone可以使用该RequestContext.getTimeZone()方法获得 用户。时区信息将自动被Date / Time ConverterFormatterSpring注册的对象使用ConversionService

18.8.2 AcceptHeaderLocaleResolver

该语言环境解析器检查accept-language客户端发送的请求中的标题(例如,Web浏览器)。通常此标题字段包含客户端操作系统的区域设置。请注意,此解析器不支持时区信息。

18.8.3 CookieLocaleResolver

这个本地化解析器检查一个Cookie可能的客户端中,看是否有 LocaleTimeZone指定。如果是这样,它使用指定的细节。使用此语言环境解析器的属性,您可以指定cookie的名称以及最大的年龄。在下面找到一个定义a的例子CookieLocaleResolver

<bean  id = “localeResolver”  class = “org.springframework.web.servlet.i18n.CookieLocaleResolver” >

	<property  name = “cookieName”  value = “clientlanguage” />

	<! - 以秒为单位。如果设置为-1,则cookie不会持久化(在浏览器关闭时删除) - > 
	<property  name = “cookieMaxAge”  value = “100000” />

</豆>

表18.4。CookieLocaleResolver属性

属性 默认 描述
cookieName classname + LOCALE cookie的名称
名cookieMaxAge Servlet容器默认 Cookie在客户端上保持持续的最长时间。如果指定了-1,则cookie不会被持久化; 只有客户端关闭浏览器才可用。
cookiePath / 将Cookie的可见性限制在您网站的某个部分。指定cookiePath时,cookie将只对该路径及其下方的路径可见。

18.8.4 SessionLocaleResolver

SessionLocaleResolver可以检索LocaleTimeZone从可能与用户的请求相关的会话。相反 CookieLocaleResolver,该策略将本地选择的区域设置存储在Servlet容器中HttpSession。因此,这些设置对于每个会话都是临时的,因此在每个会话终止时丢失。

请注意,与Spring Session项目之类的外部会话管理机制没有直接关系。这SessionLocaleResolver将简单地根据HttpSession当前值来评估和修改相应的属性HttpServletRequest

18.8.5 LocaleChangeInterceptor

您可以通过添加LocaleChangeInterceptor到其中一个处理程序映射来启用更改区域设置(请参见第18.4节“处理程序映射”)。它将检测请求中的一个参数并更改区域设置。它呼吁setLocale()LocaleResolver上下文中也存在。以下示例显示,对包含*.view名为的参数的所有资源的调用siteLanguage现在将更改语言环境。因此,例如,对以下URL的请求http://www.sf.net/home.view?siteLanguage=nl将会将站点语言更改为荷兰语。

<bean  id = “localeChangeInterceptor” 
		class = “org.springframework.web.servlet.i18n.LocaleChangeInterceptor” > 
	<property  name = “paramName”  value = “siteLanguage” /> 
</ bean>

<bean  id = “localeResolver” 
		class = “org.springframework.web.servlet.i18n.CookieLocaleResolver” />

<bean  id = “urlMapping” 
		class = “org.springframework.web.servlet.handler.SimpleUrlHandlerMapping” > 
	<property  name = “interceptors” > 
		<list> 
			<ref  bean = “localeChangeInterceptor” /> 
		</ list> 
	</ property > 
	<property  name = “mappings” > 
		<value> /**/*.view=someController </ value> 
	</ property> 
</ bean>

18.9使用主题

18.9.1主题概述

您可以应用Spring Web MVC框架主题来设置应用程序的整体外观,从而增强用户体验。主题是影响应用程序视觉风格的静态资源(通常是样式表和图像)的集合。

18.9.2定义主题

要在Web应用程序中使用主题,您必须设置接口的 org.springframework.ui.context.ThemeSource实现。该WebApplicationContext 接口扩展ThemeSource,但其代表职责的专用实现。默认情况下,委托将是 org.springframework.ui.context.support.ResourceBundleThemeSource从类路径根目录加载属性文件的实现。要使用自定义ThemeSource 实现或配置基本名称前缀ResourceBundleThemeSource,可以在应用程序上下文中使用保留名称注册一个bean themeSource。Web应用程序上下文将自动检测具有该名称的bean并使用它。

使用时ResourceBundleThemeSource,在一个简单的属性文件中定义一个主题。属性文件列出构成主题的资源。这是一个例子:

=的styleSheet /主题/冷却/ style.css中
背景= /主题/冷却/ IMG / coolBg.jpg

属性的键是从视图代码引用主题元素的名称。对于JSP,您通常使用与spring:theme标记非常相似的自定义标签来执行此操作spring:message。以下JSP片段使用上一个示例中定义的主题来自定义外观:

<%@  taglib  prefix = “spring”  uri = “http://www.springframework.org/tags” %> 
<html> 
	<head> 
		<link  rel = “stylesheet”  href = “<spring:theme code ='styleSheet '/>“  type = ”text / css“ /> 
	</ head> 
	<body  style = ”background = <spring:theme code ='background'/>“ >
		...
	</ body> 
</ html>

默认情况下,ResourceBundleThemeSource使用空的基本名称前缀。因此,属性文件从类路径的根目录加载。因此,您可以将 cool.properties主题定义放在类路径的根目录下,例如/WEB-INF/classes。它ResourceBundleThemeSource使用标准的Java资源包加载机制,允许主题的全面国际化。例如,我们可以有一个/WEB-INF/classes/cool_nl.properties引用一个特殊的背景图像与荷兰文本。

18.9.3主题解析器

定义主题后,如上一节所述,您决定使用哪个主题。该 DispatcherServlet会寻找一个叫豆themeResolver,以找出 ThemeResolver使用实施。主题解析器的工作方式与a的方式大致相同 LocaleResolver。它检测到用于特定请求的主题,还可以更改请求的主题。以下主题解析器由Spring提供:

表18.5。ThemeResolver实现

描述
FixedThemeResolver 选择一个固定的主题,使用defaultThemeName属性设置。
SessionThemeResolver 主题维护在用户的HTTP会话中。它只需要为每个会话设置一次,但不会在会话之间持久化。
CookieThemeResolver 所选主题存储在客户端的cookie中。


Spring还提供了一个ThemeChangeInterceptor允许使用简单请求参数对每个请求进行主题更改的功能。

18.10 Spring的多部分(文件上传)支持

18.10.1介绍

Spring的内置多部分支持处理Web应用程序中的文件上传。您可以MultipartResolver使用org.springframework.web.multipart包中定义的可插入对象来 启用此multipart支持。Spring提供了一个MultipartResolver 用于Commons FileUpload的实现,另一个用于Servlet 3.0 multipart请求解析。

默认情况下,Spring没有多部门处理,因为一些开发人员想要自己处理多个部件。通过将多部分解析器添加到Web应用程序的上下文来启用Spring multipart处理。检查每个请求以查看它是否包含多部分。如果没有找到multipart,请求按预期方式继续。如果在请求中找到一个multipart,MultipartResolver则使用在上下文中声明的multipart 。之后,您的请求中的multipart属性被视为任何其他属性。

18.10.2使用Commons FileUpload的MultipartResolver

以下示例显示如何使用CommonsMultipartResolver

<bean  id = “multipartResolver” 
		class = “org.springframework.web.multipart.commons.CommonsMultipartResolver” >

	<! - 可用属性之一; 最大文件大小(以字节为单位) - > 
	<property  name = “maxUploadSize”  value = “100000” />

</豆>

当然,您还需要将适当的jar放在您的类路径中,以使多部分解析器工作。在这种情况下CommonsMultipartResolver,您需要使用 commons-fileupload.jar

当Spring DispatcherServlet检测到多部分请求时,它会激活已经在上下文中声明的解析器,并交给请求。解析器然后将当前包装HttpServletRequestMultipartHttpServletRequest支持多部分文件上传的内容中。使用它MultipartHttpServletRequest,您可以获取有关此请求所包含的多部分的信息,实际上可以在控制器中自己访问多部分文件。

18.10.3在Servlet 3.0中使用MultipartResolver

为了使用的Servlet 3.0基于多解析,您需要标记 DispatcherServlet"multipart-config"的部分web.xml,或用 javax.servlet.MultipartConfigElement在编程的Servlet注册,或在自定义Servlet类的情况下,可能与javax.servlet.annotation.MultipartConfig 你的Servlet类注解。需要在Servlet注册级别应用最大大小或存储位置等配置设置,因为Servlet 3.0不允许从MultipartResolver完成这些设置。

一旦使用上述方法之一启用了Servlet 3.0 multipart解析,您可以添加StandardServletMultipartResolver到Spring配置:

<bean  id = “multipartResolver” 
		class = “org.springframework.web.multipart.support.StandardServletMultipartResolver” > 
</ bean>

18.10.4以表单处理文件上传

完成MultipartResolver工作后,请求像其他任何一样处理。首先,创建一个带有文件输入的表单,允许用户上传表单。编码属性(enctype="multipart/form-data")允许浏览器知道如何将表单编码为多部分请求:

<html> 
	<head> 
		<title>上传文件请</ title> 
	</ head> 
	<body> 
		<h1>请上传文件</ h1> 
		<form  method = “post”  action = “/ form”  enctype = “multipart / form-data” > 
			<input  type = “text”  name = “name” /> 
			<input  type = “file”  name = “file” /> 
			<input  type = “submit”/> 
		</ form> 
	</ body> 
</ html>

下一步是创建一个处理文件上传的控制器。该控制器非常类似于正常注释@Controller,除了我们使用MultipartHttpServletRequestMultipartFile在方法参数中:

@Controller
 public  class FileUploadController {

	@PostMapping(“/ form”)
	 public String handleFormUpload( @RequestParam(“name”) String name,
			 @RequestParam(“file”) MultipartFile文件){

		if(!file.isEmpty()){
			 byte [] bytes = file.getBytes();
			//存储字节某处
			返回 “redirect:uploadSuccess” ;
		}

		返回 “redirect:uploadFailure” ;
	}

}

注意@RequestParam方法参数如何映射到表单中声明的​​输入元素。在这个例子中,没有什么是完成的byte[],但实际上你可以将它保存在数据库中,将它存储在文件系统上,等等。

当使用Servlet 3.0多部分解析时,您也可以使用javax.servlet.http.Part方法参数:

@Controller
 public  class FileUploadController {

	@PostMapping(“/ form”)
	 public String handleFormUpload( @RequestParam(“name”) String name,
			 @RequestParam(“file”)零件文件){

		InputStream inputStream = file.getInputStream();
		//将上传文件的字节存储在某处

		返回 “redirect:uploadSuccess” ;
	}

}

18.10.5处理来自编程客户端的文件上传请求

也可以在RESTful服务方案中从非浏览器客户端提交多部分请求。所有上述示例和配置也适用于此。然而,与通常提交文件和简单表单字段的浏览器不同,编程客户端还可以发送特定内容类型的更复杂数据,例如具有文件的多部分请求,第二部分使用JSON格式的数据:

POST / someUrl
Content-Type:multipart / mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
内容处理:表单数据; NAME =“元数据”
Content-Type:application / json; 字符集= UTF-8
内容传输编码:8bit

{
	“名称”:“值”
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
内容处理:表单数据; NAME =“文件的数据”; 文件名= “file.properties”
Content-Type:text / xml
内容传输编码:8bit
...文件数据...

您可以使用@RequestParam("meta-data") String metadatacontroller method参数访问名为“meta-data”的部分。但是,您可能更喜欢接受从请求部分正文中的JSON格式数据初始化的强类型对象,非常类似于@RequestBody在非帮助下将非多部分请求的正文转换为目标对象的方式HttpMessageConverter

您可以使用@RequestPart注释而不是@RequestParam注释用于此目的。它允许您通过HttpMessageConverter考虑多部分的'Content-Type'标题来传递特定多部分的内容:

@PostMapping(“/ someUrl”)
 public String onSubmit( @RequestPart(“meta-data”)MetaData元数据,
		@RequestPart(“file-data”)MultipartFile文件){

	// ...

}

注意如何MultipartFile使用@RequestParam@RequestPart互换访问方法参数。但是,@RequestPart("meta-data") MetaData在这种情况下,该方法参数将被读取为基于其'Content-Type'头部的JSON内容,并在此帮助下进行转换MappingJackson2HttpMessageConverter

18.11处理例外

18.11.1 HandlerExceptionResolver

Spring HandlerExceptionResolver实现处理控制器执行期间发生的意外异常。一个HandlerExceptionResolver有点象异常映射的,你可以在Web应用程序描述符定义web.xml。但是,它们提供了一种更灵活的方法。例如,它们提供有关在抛出异常时正在执行哪个处理程序的信息。此外,处理异常的编程方式可以在请求转发到另一个URL之前提供更多的响应选项(与使用Servlet特定的异常映射相同的最终结果)。

除了实现HandlerExceptionResolver接口,这只是一个实现resolveException(Exception, Handler)方法和返回的问题 ModelAndView,您还可以使用提供的SimpleMappingExceptionResolver或创建 @ExceptionHandler方法。将SimpleMappingExceptionResolver让您采取可能被抛出的异常的类名,并将它映射到视图名。这在功能上等同于Servlet API的异常映射功能,但也可以从不同的处理程序实现更精细的异常映射。@ExceptionHandler另一方面,注释可以用于应该调用来处理异常的方法。这样的方法可以定义在本地内部,@Controller或者可以在@Controller类中定义时应用于许多类 @ControllerAdvice。以下部分将对此进行更详细的解释。

18.11.2 @ExceptionHandler

HandlerExceptionResolver接口和SimpleMappingExceptionResolver 实现允许您在转发到这些视图之前将异常与声明性地映射到特定视图以及一些可选的Java逻辑。然而,在某些情况下,特别是在依赖于@ResponseBody方法而不是视图分辨率的情况下,直接设置响应的状态并可选地将错误内容写入响应主体可能会更为方便。

你可以用@ExceptionHandler方法来做到这一点。当在控制器内声明时,这种方法适用于由@RequestMapping该控制器(或其任何子类)的方法引发的异常。您也可以@ExceptionHandler@ControllerAdvice类中声明一个方法, 在这种情况下,它可以处理@RequestMapping 来自多个控制器的方法的异常。下面是一个控制器局部@ExceptionHandler方法的例子 :

@Controller
 public  class SimpleController {

	// @RequestMapping方法省略...

	@ExceptionHandler(IOException.class)
	 public ResponseEntity <String> handleIOException(IOException ex){
		 // prepare responseEntity 
		return responseEntity;
	}

}

@ExceptionHandler值可以设置为一个异常类型的数组。如果抛出与列表中的一个类型匹配的异常,@ExceptionHandler则将调用注释与匹配的方法。如果未设置注释值,则使用列为方法参数的异常类型。

与使用@RequestMapping注释注释的标准控制器方法非常相似,方法的方法参数和返回值@ExceptionHandler可以是灵活的。例如,HttpServletRequest可以在Servlet环境中访问。返回类型可以是一个String,它被解释为一个视图名称,一个ModelAndView对象,一个ResponseEntity或者你也可以添加@ResponseBody一个方法返回值转换为消息转换器并写入响应流。

18.11.3处理标准Spring MVC异常

Spring MVC可能会在处理请求时引发许多异常。根据需要, SimpleMappingExceptionResolver可以轻松地将任何异常映射到默认错误视图。但是,在使用以自动方式解释响应的客户端时,您将需要在响应中设置特定的状态代码。根据引发的异常,状态代码可能会指示客户端错误(4xx)或服务器错误(5xx)。

DefaultHandlerExceptionResolverSpring MVC异常转换为特定的错误状态代码。它默认注册了MVC命名空间,MVC Java配置,还有DispatcherServlet(即不使用MVC命名空间或Java配置时)。下面列出了这个解析器处理的一些异常和相应的状态代码:

例外 HTTP状态码
BindException 400(不良要求)
ConversionNotSupportedException 500内部服务器错误)
HttpMediaTypeNotAcceptableException 406(不可接受)
HttpMediaTypeNotSupportedException 415(不支持的媒体类型)
HttpMessageNotReadableException 400(不良要求)
HttpMessageNotWritableException 500内部服务器错误)
HttpRequestMethodNotSupportedException 405(不允许方法)
MethodArgumentNotValidException 400(不良要求)
MissingPathVariableException 500内部服务器错误)
MissingServletRequestParameterException 400(不良要求)
MissingServletRequestPartException 400(不良要求)
NoHandlerFoundException 错误(404)
NoSuchRequestHandlingMethodException 错误(404)
TypeMismatchException 400(不良要求)

DefaultHandlerExceptionResolver通过设置响应的状态透明地工作。但是,您的应用程序可能需要为每个错误响应添加开发人员友好的内容,例如提供REST API时,不会将任何错误内容写入响应正文。你可以准备一个ModelAndView 和渲染通过视图解析错误内容-通过配置,即ContentNegotiatingViewResolverMappingJackson2JsonView,等等。但是,您可能更喜欢使用@ExceptionHandler方法。

如果您喜欢通过@ExceptionHandler方法编写错误内容,可以ResponseEntityExceptionHandler改为扩展 。这是提供处理标准Spring MVC异常和返回@ControllerAdvice@ExceptionHandler方法的类的方便基础 ResponseEntity。这允许您自定义响应并使用消息转换器写入错误内容。有关ResponseEntityExceptionHandler详细信息,请参阅 javadocs。

18.11.4 REST控制器异常处理

一个@RestController可以使用@ExceptionHandler返回一个方法, ResponseEntity在响应的主体既提供响应状态和错误的详细信息。这些方法也可以被添加到@ControllerAdvice 用于子集或所有控制器的异常处理的类中。

一个常见的要求是在响应的正文中包含错误详细信息。Spring不会自动执行此操作(尽管Spring Boot),因为响应主体中的错误详细信息的表示是特定于应用程序的。

希望在响应体中实现具有错误详细信息的全局异常处理策略的应用程序应考虑扩展抽象基类ResponseEntityExceptionHandler,为Spring MVC引发的异常提供处理,并提供钩子来自定义响应体以及处理其他异常。只需将扩展类声明为一个Spring bean并用它进行注释@ControllerAdvice。有关详细信息,请参阅ResponseEntityExceptionHandler

18.11.5使用@ResponseStatus注释业务异常

可以注释业务异常@ResponseStatus。当异常提出时,ResponseStatusExceptionResolver通过相应地设置响应的状态来处理它。默认情况下,DispatcherServlet寄存器 ResponseStatusExceptionResolver,它是可用。

18.11.6自定义默认Servlet容器错误页面

当响应的状态设置为错误状态代码并且响应的正文为空时,Servlet容器通常会呈现HTML格式的错误页面。要自定义容器的默认错误页面,可以在其中声明一个<error-page> 元素web.xml。直到Servlet 3,该元素必须映射到特定的状态代码或异常类型。从Servlet 3开始,不需要映射错误页面,这意味着指定的位置定制了默认的Servlet容器错误页面。

<error-page> 
	<location> / error </ location> 
</ error-page>

请注意,错误页面的实际位置可以是容器中的JSP页面或其他一些URL,包括通过一种@Controller方法处理的页面:

编写错误信息时,HttpServletResponse可通过控制器中的请求属性访问状态码和设置的错误信息 :

@Controller
 public  class ErrorController {

	@RequestMapping(path =“/ error”,produce = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	 public Map <String,Object> handle(HttpServletRequest request){

		Map <String,Object> map = new HashMap <String,Object>();
		map.put(“status”,request.getAttribute(“javax.servlet.error.status_code”));
		map.put(“reason”,request.getAttribute(“javax.servlet.error.message”));

		返回地图;
	}

}

或在JSP中:

<%@  page  contentType = “application / json”  pageEncoding = “UTF-8” %> 
{ 
	status:<% = request.getAttribute(“javax.servlet.error.status_code”) %>,
	reason:<% = request。 getAttribute(“javax.servlet.error.message”) %> 
}

18.12网络安全

春季安全项目提供的功能,以防止恶意攻击Web应用程序。请参阅 “CSRF保护”“安全响应头”以及 “Spring MVC集成”部分中的参考文档。请注意,使用Spring Security来保护应用程序并不一定需要所有功能。例如,CSRF保护可以通过添加CsrfFilter和配置 CsrfRequestDataValueProcessor来添加。参见 Spring MVC展示 示例。

另一个选择是使用专门用于Web Security的框架。 HDIV是一个这样的框架,并与Spring MVC集成。

18.13关于配置支持的公约

对于很多项目,坚持既定的约定和合理的默认值就是它们(项目)所需要的,而Spring Web MVC现在已经明确地支持约定的配置。这意味着如果您建立了一组命名约定等等,您可以大幅度减少设置处理程序映射,查看解析器,ModelAndView实例等所需的配置量 。这对于快速原型,并且如果您选择将其推向生产,还可以在代码库中提供一定程度的(始终如一的)一致性。

公约超配置支持解决了MVC的三个核心领域:模型,视图和控制器。

18.13.1 Controller ControllerClassNameHandlerMapping

ControllerClassNameHandlerMapping班是一个HandlerMapping使用惯例来确定请求的URL和之间的映射实现Controller 是要处理这些请求的情况。

考虑以下简单的Controller实现。特别注意 课程名称

public  class  ViewShoppingCartController  implements Controller {

	public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){
		 //实现对于这个例子来说不是很重要...
	}

}

以下是相应的Spring Web MVC配置文件的代码段:

<bean  class = “org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping” />

<bean  id = viewShoppingCart ”class =“xyzViewShoppingCartController”>
	 <! - 根据需要注入依赖关系...  - > 
</ bean>

ControllerClassNameHandlerMapping找出所有的处理程序(或 Controller在其应用上下文定义的)豆和剥离Controller掉,以限定其处理程序映射的名称。因此,ViewShoppingCartController映射到 /viewshoppingcart*请求URL。

我们再来看一些更多的例子,使中心思想变得熟悉。(注意URL中的全部小写,与骆驼Controller类的类名相反)。

  • WelcomeController映射到/welcome*请求URL
  • HomeController映射到/home*请求URL
  • IndexController映射到/index*请求URL
  • RegisterController映射到/register*请求URL

MultiActionController处理程序类的情况下,生成的映射稍微复杂一点。以下Controller示例中的名称被假定为实现MultiActionController

  • AdminController映射到/admin/*请求URL
  • CatalogController映射到/catalog/*请求URL

如果你按照你的命名的惯例Controller实现的 xxxController,将ControllerClassNameHandlerMapping节省您定义和维护一个潜在的沉闷一长串SimpleUrlHandlerMapping(或类似的东西)。

ControllerClassNameHandlerMapping类扩展AbstractHandlerMapping基类,所以你可以定义HandlerInterceptor实例和一切,就像你与许多其他HandlerMapping的实现。

18.13.2 ModelMap(ModelAndView)

ModelMap班本质上是一种荣耀Map,可以使补充说,是要显示(或上)一个对象View坚持一个共同的命名约定。考虑以下Controller实现; 注意对象被添加到ModelAndView没有指定的任何关联的名称。

public  class DisplayShoppingCartController implements Controller {

	public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){

		列表cartItems = //获取CartItem对象的列表 
		User user = //让用户进行购物

		ModelAndView mav = new ModelAndView(“displayShoppingCart”); < - 逻辑视图名称

		mav.addObject(cartItems); < - 看ma,没有名字,只是对象
		mav.addObject(用户);  - 再次!

		返回 mav
	}
}

ModelAndView类使用一个ModelMap类,它是一个自定义的Map,可自动生成用于当对象被添加到它的对象的键实现。在标量对象的情况下,用于确定添加对象的名称的策略是User使用对象类的短类名。以下示例是为放置到ModelMap实例中的标量对象生成的名称。

  • x.y.User添加 的实例将生成名称user
  • x.y.Registration添加 的实例将生成名称registration
  • x.y.Foo添加 的实例将生成名称foo
  • java.util.HashMap添加 的实例将生成名称hashMap。在这种情况下,您可能想要明确说明名称,因为hashMap它不直观。
  • 添加null将导致IllegalArgumentException被抛出。如果您要添加的对象(或对象)可能是null,那么您还需要明确说明名称。

在添加一个Set或一个之后生成名称的策略List是窥视集合,获取集合中第一个对象的短类名称,并将其List附加到名称后面。这同样适用于数组,尽管数组不需要窥视数组内容。几个例子将使得集合名称生成的语义更加清晰:

  • 添加x.y.User[]零个或多个x.y.User元素 的数组将生成名称 userList
  • 添加x.y.Foo[]零个或多个x.y.User元素 的数组将生成名称 fooList
  • 添加java.util.ArrayList一个或多个x.y.User元素的 A 将生成名称 userList
  • 添加java.util.HashSet一个或多个x.y.Foo元素的 A 将生成名称 fooList
  • 根本不会添加 一个 java.util.ArrayList(实际上, addObject(..)调用本质上是无操作的)。

18.13.3 View – RequestToViewNameTranslator

RequestToViewNameTranslator接口确定的逻辑View当没有这样的逻辑视图名称被明确地提供的名称。它只有一个实现,DefaultRequestToViewNameTranslator类。

DefaultRequestToViewNameTranslator请求的URL映射到逻辑视图的名称,与该实施例中:

public  class RegistrationController implements Controller {

	public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){
		 //处理请求... 
		ModelAndView mav = new ModelAndView();
		//根据需要添加数据到模型... 
		return mav;
		//注意没有设置View或逻辑视图名称
	}

}
<?xml version =“1.0”encoding =“UTF-8”?> 
<beans  xmlns = “http://www.springframework.org/schema/beans” 
	xmlns:xsi = “http://www.w3.org / 2001 / XMLSchema-instance“ 
	xsi:schemaLocation = ”
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd“ >

	<! - 这个具有众所周知的名称的bean为我们生成视图名称 - > 
	<bean  id = “viewNameTranslator” 
			class = “org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator” />

	<bean  class = “xyRegistrationController” > 
		<! - 根据需要注入依赖项 - > 
	</ bean>

	<! - 将请求URL映射到控制器名称 - > 
	<bean  class = “org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping” />

	<bean  id = “viewResolver”  class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > 
		<property  name = “prefix”  value = “/ WEB-INF / jsp /” /> 
		<property  name = “suffix”  value = “.jsp” /> 
	</ bean>

</豆>

注意在执行handleRequest(..)方法no View或逻辑视图名称是否被设置ModelAndView返回的。该 DefaultRequestToViewNameTranslator任务是 从请求的URL 生成逻辑视图名称。另外,在上述的情况下RegistrationController,这是在与结合使用ControllerClassNameHandlerMapping的一个请求的URLhttp://localhost/registration.html中的一个逻辑视图名称结果registration 被生成的DefaultRequestToViewNameTranslator。该逻辑视图名称然后/WEB-INF/jsp/registration.jspInternalResourceViewResolverbean 解析为视图 。

[小费]
您不需要明确定义一个DefaultRequestToViewNameTranslatorbean。如果您喜欢默认设置DefaultRequestToViewNameTranslator,可以依靠Spring Web MVC DispatcherServlet实例化此类的实例,如果未明确配置。

当然,如果您需要更改默认设置,那么您需要明确配置自己的DefaultRequestToViewNameTranslatorbean。有关可配置DefaultRequestToViewNameTranslator的各种属性的详细信息,请查阅全面的 javadocs。

18.14 HTTP缓存支持

良好的HTTP缓存策略可以显着提高Web应用程序的性能和客户体验。所述'Cache-Control'HTTP响应报头主要是为这个负责,使用条件报头,例如沿'Last-Modified''ETag'

'Cache-Control'HTTP响应头劝告私有的高速缓存(如浏览器),以及他们如何缓存进一步重用HTTP响应的公共高速缓存(例如代理)。

一个的ETag(实体标签)是由HTTP / 1.1兼容的Web用于在给定的URL来确定内容改变服务器返回的HTTP响应报头中。它可以被认为是Last-Modified标题的更复杂的继承者 。当服务器返回具有ETag头的表示时,客户端可以在标题中的后续GET中使用此If-None-Match头。如果内容没有改变,服务器返回304: Not Modified

本节介绍在Spring Web MVC应用程序中配置HTTP缓存的不同选择。

18.14.1缓存控制HTTP头

Spring Web MVC支持许多用例和方式为应用程序配置“Cache-Control”标头。虽然RFC 7234第5.2.2节 完全描述了标题及其可能的指令,但是有几种方法可以解决最常见的情况。

Spring Web MVC在其几个API中使用配置约定 setCachePeriod(int seconds)

  • -1值将不生成'Cache-Control'响应头。
  • 一个0值,将使用防止缓存'Cache-Control: no-store'指令。
  • 一个n > 0值将缓存对于给定的响应n使用秒 'Cache-Control: max-age=n'指令。

CacheControl生成器类简单地描述了可用的“缓存控制”指令,并使其更容易建立自己的HTTP缓存策略。一旦构建,一个CacheControl实例就可以被接受为几个Spring Web MVC API中的参数。

//缓存一小时 - “Cache-Control:max-age = 3600” 
   CacheControl ccCacheOneHour = CacheControl.maxAge(1,TimeUnit.HOURS);

   //防止缓存 - “Cache-Control:no-store”
   CacheControl ccNoStore = CacheControl.noStore();

   //在公共和私有缓存中缓存十天,
   //公共缓存不应该转换响应
   //“Cache-Control:max-age = 864000,public,no-transform” 
   CacheControl ccCustom = CacheControl.maxAge(10,TimeUnit 。天)
   									。.noTransform()cachePublic();

18.14.2 HTTP缓存支持静态资源

应使用适当的'Cache-Control'条件标题来提供静态资源,以获得最佳性能。 配置一个ResourceHttpRequestHandler用于提供静态资源的功能不仅'Last-Modified'通过读取文件的元数据来定位'Cache-Control'头文件,而且还可以正确配置头文件。

您可以设置cachePeriod属性ResourceHttpRequestHandler或使用CacheControl实例,该实例支持更具体的指令:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”)
				.addResourceLocations(“/ public-resources /”)
				.setCacheControl(CacheControl.maxAge(1,TimeUnit.HOURS).cachePublic());
	}

}

在XML中:

<mvc:resources  mapping = “/ resources / **”  location = “/ public-resources /” > 
	<mvc:cache-control  max-age = “3600”  cache-public = “true” /> 
</ mvc:resources >

18.14.3支持控制器中的Cache-Control,ETag和Last-Modified响应头

控制器可以支持'Cache-Control''ETag'和/或'If-Modified-Since'HTTP请求; 如果'Cache-Control'要在响应中设置标题,则建议这样做。这涉及计算long给定请求的lastModified 和/或Etag值,将其与'If-Modified-Since'请求头值进行比较,并可能返回具有状态代码304(未修改)的响应。

“使用HttpEntity”一节所述,控制器可以使用HttpEntity类型与请求/响应进行交互 。返回的控制器ResponseEntity可以包括响应中的HTTP缓存信息,如下所示:

@GetMapping(“/ book / {id}”)
 public ResponseEntity <Book> showBook @PathVariable Long id){

	Book book = findBook(id);
	String version = book.getVersion();

	返回 ResponseEntity
				。好()
				.cacheControl(CacheControl.maxAge(30,TimeUnit.DAYS))
				.eTag(version)// lastModified也可用
				。体(书);
}

如果客户端发送的条件标头与控制器设置的缓存信息匹配,这样做将不仅包括响应中的头'ETag''Cache-Control'头,还会将响应转换HTTP 304 Not Modified为空体

一种@RequestMapping方法也不妨支持相同的行为。这可以实现如下:

@RequestMapping
 public String myHandleMethod(WebRequest webRequest,Model model){

	long lastModified = // 1.应用程序特定的计算

	if(request.checkNotModified(lastModified)){
		 // 2.快捷方式退出 - 无需进一步处理必需
		返回 null;
	}

	// 3.否则进一步请求处理,实际准备内容
	model.addAttribute(...);
	返回 “myViewName” ;
}

这里有两个关键元素:呼叫request.checkNotModified(lastModified)和返回null。前者在返回之前设置适当的响应状态和标题true。后者与前者相结合,导致Spring MVC不再进一步处理请求。

请注意,有3种变体:

  • request.checkNotModified(lastModified)与比较上次更改时间 'If-Modified-Since''If-Unmodified-Since'请求头
  • request.checkNotModified(eTag)将eTag与'If-None-Match'请求标头 进行比较
  • request.checkNotModified(eTag, lastModified) 这两者都意味着两个条件都应该是有效的

当接收到条件'GET'/ 'HEAD'请求时,checkNotModified将检查资源是否未被修改,如果是这样,它将导致HTTP 304 Not Modified 响应。在有条件的'POST'/ 'PUT'/ 'DELETE'请求的情况下,checkNotModified 将检查资源是否尚未被修改,如果已经被修改,它将导致 HTTP 409 Precondition Failed响应以防止并发修改。

18.14.4浅层ETag支持

支持ETag由Servlet过滤器提供ShallowEtagHeaderFilter。它是一个简单的Servlet过滤器,因此可以与任何Web框架结合使用。该 ShallowEtagHeaderFilter滤波器产生所谓的浅ETag的(相对于深的ETag,稍后详细说明)。该过滤器缓存呈现JSP(或其他内容)的内容,产生超过其MD5哈希,并返回作为ETag头在回应中。下一次客户端发送对同一资源的请求时,它将使用该哈希作为该If-None-Match值。过滤器检测到这一点,再次渲染视图,并比较两个散列。如果它们相等,304则返回a。

请注意,此策略可节省网络带宽但不节省CPU,因为必须为每个请求计算完整的响应。控制器级别的其他策略(如上所述)可以节省网络带宽并避免计算。

此过滤器具有一个writeWeakETag参数,可将过滤器配置为写入弱符号ETags,如RFC 7232第2.3节W/"02a2d595e6ed9a0b24f027f2b63b134d6"中所定义 。

您配置ShallowEtagHeaderFilterweb.xml

<filter> 
	<filter-name> etagFilter </ filter-name> 
	<filter-class> org.springframework.web.filter.ShallowEtagHeaderFilter </ filter-class> 
	<! - 配置过滤器写入弱ETag的可选参数
	<INIT-PARAM>
       	<PARAM名称> writeWeakETag </ PARAM名称>
       	<PARAM值>真</ PARAM值>
	</ INIT-param>
	- > 
</ filter>

<filter-mapping> 
	<filter-name> etagFilter </ filter-name> 
	<servlet-name> petclinic </ servlet-name> 
</ filter-mapping>

或者在Servlet 3.0+环境中,

public  class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

	// ...

	@Override
	 protected Filter [] getServletFilters(){
		 return  new Filter [] { new ShallowEtagHeaderFilter()};
	}

}

有关详细信息,请参见第18.15节“基于代码的Servlet容器初始化”

18.15基于代码的Servlet容器初始化

在Servlet 3.0+环境中,您可以选择以编程方式配置Servlet容器作为替代方法或与web.xml文件组合使用。下面是一个注册一个例子DispatcherServlet

import org.springframework.web.WebApplicationInitializer;

public  class MyWebApplicationInitializer 实现 WebApplicationInitializer {

	@Override
	 public  void onStartup(ServletContext container){
		XmlWebApplicationContext appContext = new XmlWebApplicationContext();
		appContext.setConfigLocation(“/WEB-INF/spring/dispatcher-config.xml”);

		ServletRegistration.Dynamic注册= container.addServlet(“dispatcher”新的 DispatcherServlet(appContext));
		registration.setLoadOnStartup(1);
		registration.addMapping(“/”);
	}

}

WebApplicationInitializer是由Spring MVC提供的接口,可确保您的实现被检测并自动用于初始化任何Servlet 3容器。WebApplicationInitializer命名 的抽象基类实现通过简单地覆盖方法来指定servlet映射和配置的位置AbstractDispatcherServletInitializer更容易注册 。DispatcherServletDispatcherServlet

推荐使用基于Java的Spring配置的应用程序:

public  class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	 protected Class <?> [] getRootConfigClasses(){
		 return null;
	}

	@Override
	 protected Class <?> [] getServletConfigClasses(){
		 return  new Class [] {MyWebConfig。 };
	}

	@Override
	 protected String [] getServletMappings(){
		 return  new String [] { “/” };
	}

}

如果使用基于XML的Spring配置,您应该直接从 AbstractDispatcherServletInitializer

public  class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

	@Override
	 protected WebApplicationContext createRootApplicationContext(){
		 return null;
	}

	@Override
	 protected WebApplicationContext createServletApplicationContext(){
		XmlWebApplicationContext cxt = new XmlWebApplicationContext();
		cxt.setConfigLocation(“/WEB-INF/spring/dispatcher-config.xml”);
		返回 cxt;
	}

	@Override
	 protected String [] getServletMappings(){
		 return  new String [] { “/” };
	}

}

AbstractDispatcherServletInitializer还提供了一种方便的方法来添加Filter 实例并让它们自动映射到DispatcherServlet

public  class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

	// ...

	@Override
	 protected Filter [] getServletFilters(){
		 return  new Filter [] { new HiddenHttpMethodFilter(), new CharacterEncodingFilter()};
	}

}

每个过滤器根据具体类型添加默认名称并自动映射到DispatcherServlet

提供单一位置的isAsyncSupported受保护方法AbstractDispatcherServletInitializer可以在DispatcherServlet映射到它的所有过滤器上启用异步支持。默认情况下,该标志设置为true

最后,如果需要进一步自定义DispatcherServlet自身,可以覆盖该createDispatcherServlet方法。

18.16配置Spring MVC

第18.2.1节“WebApplicationContext中的特殊Bean类型”第18.2.2节“默认DispatcherServlet配置”解释了Spring MVC的特殊bean以及该使用的默认实现DispatcherServlet。在本节中,您将了解配置Spring MVC的两种其他方法。即MVC Java配置和MVC XML命名空间。

MVC Java配置和MVC命名空间提供了类似的默认配置,可以覆盖DispatcherServlet默认配置。目标是使大多数应用程序不必创建相同的配置,并提供更高级别的构造,用于配置作为简单起始点的Spring MVC,并且需要很少或根本没有底层配置的知识。

您可以根据自己的喜好选择MVC Java配置或MVC命名空间。另外,您将在下面进一步看到,使用MVC Java配置,可以更容易地查看底层配置,以及直接对创建的Spring MVC bean进行细粒度的自定义。但是让我们从一开始就开始。

18.16.1启用MVC Java Config或MVC XML命名空间

要启用MVC Java配置,请将注释添加@EnableWebMvc到您的一个 @Configuration类中:

@Configuration 
@EnableWebMvc
 public  class WebConfig {

}

要在XML中实现相同的使用mvc:annotation-drivenDispatcherServlet上下文中的元素(如果您没有定义DispatcherServlet上下文,则在根上下文中):

<?xml version =“1.0”encoding =“UTF-8”?> 
<beans  xmlns = “http://www.springframework.org/schema/beans” 
	xmlns:mvc = “http://www.springframework.org / schema / mvc“ 
	xmlns:xsi = ”http://www.w3.org/2001/XMLSchema-instance“ 
	xsi:schemaLocation = ”
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd“ >

	<MVC:注释驱动/>

</豆>

在上述寄存器一RequestMappingHandlerMapping,一个RequestMappingHandlerAdapter,和ExceptionHandlerExceptionResolver在支持带注释控制器方法的处理请求(等等)使用注释诸如@RequestMapping@ExceptionHandler和其他。

它还支持以下功能:

  1. 除了用于数据绑定的JavaBeans PropertyEditor之外,还可以 通过ConversionService实例进行Spring 3样式类型转换。
  2. 支持格式使用数字字段@NumberFormat通过注释ConversionService
  3. 支持的格式 DateCalendarLong,和乔达时间使用的字段 @DateTimeFormat注解。
  4. 支持验证 @Controller输入@Valid,如果JSR-303提供程序存在于类路径中。
  5. HttpMessageConverter支持@RequestBody方法参数和@ResponseBody 方法返回值@RequestMapping@ExceptionHandler方法。

    这是由mvc设计的HttpMessageConverters的完整列表:annotation-driven:

    1. ByteArrayHttpMessageConverter 转换字节数组。
    2. StringHttpMessageConverter 转换字符串。
    3. ResourceHttpMessageConverter转换为/从 org.springframework.core.io.Resource为所有媒体类型。
    4. SourceHttpMessageConverter转换到/从a javax.xml.transform.Source
    5. FormHttpMessageConverter将表单数据转换为/从a转换MultiValueMap<String, String>
    6. Jaxb2RootElementHttpMessageConverter 将Java对象转换为/从XML中添加 – 如果存在JAXB2,并且类别路径中不存在Jackson 2 XML扩展名。
    7. MappingJackson2HttpMessageConverter 转换为/从JSON中添加,如果杰克逊2存在于类路径。
    8. MappingJackson2XmlHttpMessageConverter转换为/从XML中添加,如果 Jackson 2 XML扩展存在于类路径中。
    9. MappingJackson2SmileHttpMessageConverter转换为/从微笑(二进制JSON) – 添加如果 杰克逊2微笑扩展 存在于类路径。
    10. MappingJackson2CborHttpMessageConverter转换为/从CBOR – 添加如果 杰克逊2 CBOR扩展 存在于类路径。
    11. AtomFeedHttpMessageConverter 转换Atom Feed – 如果罗马存在于类路径中则添加。
    12. RssChannelHttpMessageConverter 转换RSS提要 – 如果罗马存在于类路径中,则会添加。

有关如何自定义这些默认转换器的更多信息,请参见第18.16.12节“消息转换器”

[注意]
使用ObjectMapper创建的实例创建 Jackson JSON和XML转换器Jackson2ObjectMapperBuilder ,以提供更好的默认配置。此构建器使用以下命令自定义Jackson的默认属性:

如果在类路径中检测到它们,它也会自动注册以下众所周知的模块:

  1. jackson-datatype-jdk7:支持Java 7类型java.nio.file.Path
  2. jackson-datatype-joda:支持Joda-Time类型。
  3. jackson-datatype-jsr310:支持Java 8 Date&Time API类型。
  4. jackson-datatype-jdk8:支持其他Java 8类型Optional

18.16.2定制提供的配置

要定制Java中的默认配置,您只需实现该 WebMvcConfigurer接口,或者更有可能扩展该类WebMvcConfigurerAdapter 并覆盖所需的方法:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	//覆盖配置方法...

}

要自定义默认配置,<mvc:annotation-driven/>检查它支持哪些属性和子元素。您可以查看 Spring MVC XML模式或使用IDE的代码完成功能来发现哪些属性和子元素可用。

18.16.3转换和格式化

默认情况下,已安装格式化程序NumberDate类型,包括对@NumberFormat@DateTimeFormat注释的支持。如果Joda时间存在于类路径上,则还将完全支持Joda Time格式化库。要注册自定义格式化程序和转换器,请覆盖该addFormatters方法:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addFormatters(FormatterRegistry registry){
		 //添加格式化程序和/或转换器
	}

}

在MVC命名空间<mvc:annotation-driven>中,添加相同的默认值。注册自定义格式化器和转换器只需提供一个ConversionService

<?xml version =“1.0”encoding =“UTF-8”?> 
<beans  xmlns = “http://www.springframework.org/schema/beans” 
	xmlns:mvc = “http://www.springframework.org / schema / mvc“ 
	xmlns:xsi = ”http://www.w3.org/2001/XMLSchema-instance“ 
	xsi:schemaLocation = ”
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd“ >

	<mvc:annotation-driven  conversion-service = “conversionService” />

	<豆 ID = “conversionService” 
			 = “org.springframework.format.support.FormattingConversionServiceFactoryBean” > 
		<属性  = “转换器” > 
			<设定> 
				<豆  = “org.example.MyConverter” /> 
			</设定> 
		< / property> 
		<property  name = “formatters” > 
			<set> 
				<bean  class = “org.example.MyFormatter” /> 
				<bean  class = “org.example。MyAnnotationFormatterFactory“ /> 
			</ set> 
		</ property> 
		<property  name = ”formatterRegistrars“ > 
			<set> 
				<bean  class = ”org.example.MyFormatterRegistrar“ /> 
			</ set> 
		</ property> 
	</ bean>

</豆>
[注意]
参见5.6.4节,“FormatterRegistrar SPI”FormattingConversionServiceFactoryBean 关于何时使用FormatterRegistrars更多的信息。

18.16.4验证

Spring提供了一个验证器接口,可用于在应用程序的所有层中进行验证。在Spring MVC中,您可以将其配置为用作全局Validator实例,以在遇到@Valid@Validated控制器方法参数时使用,和/或Validator通过@InitBinder方法作为控制器中的本地 使用。可以组合全局和本地验证器实例以提供复合验证。

Spring还支持JSR-303 / JSR-349 Bean验证,通过LocalValidatorFactoryBean它可以将Spring org.springframework.validation.Validator 界面适应Bean验证javax.validation.Validator合同。这个类可以插入Spring MVC作为下面描述的全局验证器。

默认情况下,通过在类路径中检测到一个Bean验证提供程序(如Hibernate Validator)时,可以在Spring MVC中使用@EnableWebMvc<mvc:annotation-driven>自动注册Bean验证支持LocalValidatorFactoryBean

[注意]
有时,将LocalValidatorFactoryBean注入到控制器或其他类中是很方便的。最简单的方法是声明自己的@Bean,并且标记它,@Primary以避免与MVC Java配置提供的冲突。如果你喜欢使用MVC的Java的配置之一,你需要重写 mvcValidator的方法WebMvcConfigurationSupport,并声明明确回报的方法LocalValidatorFactory,而不是Validator。有关 如何切换扩展提供的配置的信息,请参见第18.16.13节“使用MVC Java Config进行高级定制”

或者,您可以配置自己的全局Validator实例:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public Validator getValidator(); {
		 //返回“全局”验证器
	}

}

和XML:

<?xml version =“1.0”encoding =“UTF-8”?> 
<beans  xmlns = “http://www.springframework.org/schema/beans” 
	xmlns:mvc = “http://www.springframework.org / schema / mvc“ 
	xmlns:xsi = ”http://www.w3.org/2001/XMLSchema-instance“ 
	xsi:schemaLocation = ”
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc.xsd“ >

	<mvc:annotation-driven  validator = “globalValidator” />

</豆>

要将全局和本地验证结合起来,只需添加一个或多个本地验证器:

@Controller
 public  class MyController {

	@InitBinder
	 protected  void initBinder(WebDataBinder binder){
		binder.addValidators(new FooValidator());
	}

}

使用这个最小配置,任何时候遇到一个@Valid@Validated方法参数,它将被配置的验证器验证。任何验证违规将自动BindingResult作为方法参数可访问的错误公开,并且也可以在Spring MVC HTML视图中呈现。

18.16.5拦截器

您可以配置HandlerInterceptorsWebRequestInterceptors应用于所有传入的请求或限制在特定的URL路径模式。

在Java中注册拦截器的示例:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addInterceptors(InterceptorRegistry registry){
		registry.addInterceptor(new LocaleInterceptor());
		registry.addInterceptor(new ThemeInterceptor())。addPathPatterns(“/ **”).excludePathPatterns(“/ admin / **”);
		registry.addInterceptor(new SecurityInterceptor())。addPathPatterns(“/ secure / *”);
	}

}

而在XML中使用<mvc:interceptors>元素:

<mvc:interceptors> 
	<bean  class = “org.springframework.web.servlet.i18n.LocaleChangeInterceptor” /> 
	<mvc:interceptor> 
		<mvc:mapping  path = “/ **” /> 
		<mvc:exclude-mapping  path = “/ admin / **” /> 
		<bean  class = “org.springframework.web.servlet.theme.ThemeChangeInterceptor” /> 
	</ mvc:interceptor> 
	<mvc:interceptor> 
		<mvc:mapping  path = “/ secure / “ /> 
		<bean  class = ”org.example.SecurityInterceptor“/> 
	</ mvc:interceptor> 
</ mvc:interceptors>

18.16.6 Content Negotiation

You can configure how Spring MVC determines the requested media types from the request. The available options are to check the URL path for a file extension, check the “Accept” header, a specific query parameter, or to fall back on a default content type when nothing is requested. By default the path extension in the request URI is checked first and the “Accept” header is checked second.

The MVC Java config and the MVC namespace register json, xml, rss, atom by default if corresponding dependencies are on the classpath. Additional path extension-to-media type mappings may also be registered explicitly and that also has the effect of whitelisting them as safe extensions for the purpose of RFD attack detection (see the section called “Suffix Pattern Matching and RFD” for more detail).

Below is an example of customizing content negotiation options through the MVC Java config:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.mediaType("json", MediaType.APPLICATION_JSON);
	}
}

In the MVC namespace, the <mvc:annotation-driven> element has a content-negotiation-manager attribute, which expects a ContentNegotiationManagerthat in turn can be created with a ContentNegotiationManagerFactoryBean:

<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
	<property name="mediaTypes">
		<value>
			json=application/json
			xml=application/xml
		</value>
	</property>
</bean>

If not using the MVC Java config or the MVC namespace, you’ll need to create an instance of ContentNegotiationManager and use it to configure RequestMappingHandlerMapping for request mapping purposes, and RequestMappingHandlerAdapter and ExceptionHandlerExceptionResolver for content negotiation purposes.

Note that ContentNegotiatingViewResolver now can also be configured with a ContentNegotiationManager, so you can use one shared instance throughout Spring MVC.

In more advanced cases, it may be useful to configure multiple ContentNegotiationManager instances that in turn may contain customContentNegotiationStrategy implementations. For example you could configure ExceptionHandlerExceptionResolver with a ContentNegotiationManagerthat always resolves the requested media type to "application/json". Or you may want to plug a custom strategy that has some logic to select a default content type (e.g. either XML or JSON) if no content types were requested.

18.16.7 View Controllers

This is a shortcut for defining a ParameterizableViewController that immediately forwards to a view when invoked. Use it in static cases when there is no Java controller logic to execute before the view generates the response.

An example of forwarding a request for "/" to a view called "home" in Java:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addViewController(ViewControllerRegistry registry){
		registry.addViewController(“/”).setViewName(“home”);
	}

}

而且在XML中使用相同的<mvc:view-controller>元素:

<mvc:view-controller  path = “/”  view-name = “home” />

18.16.8查看解决方案

MVC配置简化了视图解析器的注册。

以下是一个Java配置示例,它使用FreeMarker HTML模板配置内容协商视图分辨率,并将Jackson作为ViewJSON渲染的默认值:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(new MappingJackson2JsonView());
		registry.jsp();
	}

}

和XML一样:

<mvc:view-resolvers> 
	<mvc:content-negotiation> 
		<mvc:default-views> 
			<bean  class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> 
		</ mvc:default-views> 
	</ mvc:content-negotiation> 
	<mvc:jsp /> 
</ mvc:view-resolvers>

不过请注意,FreeMarker,Tiles,Groovy Markup和脚本模板也需要配置底层视图技术。

MVC命名空间提供专用元素。例如与FreeMarker:

<mvc:view-resolvers> 
	<mvc:content-negotiation> 
		<mvc:default-views> 
			<bean  class = “org.springframework.web.servlet.view.json.MappingJackson2JsonView” /> 
		</ mvc:default-views> 
	</ mvc:content-negotiation> 
	<mvc:freemarker  cache = “false” /> 
</ mvc:view-resolvers>

<mvc:freemarker-configurer> 
	<mvc:template-loader-path  location = “/ freemarker” /> 
</ mvc:freemarker-configurer>

在Java配置中,只需添加相应的“Configurer”bean:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(new MappingJackson2JsonView());
		。registry.freeMarker()高速缓存(假);
	}

	@Bean
	 public FreeMarkerConfigurer freeMarkerConfigurer(){
		FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
		configurer.setTemplateLoaderPath(“/ WEB-INF /”);
		return configurer;
	}

}

18.16.9资源服务

此选项允许在特定URL模式之后的静态资源请求由位置ResourceHttpRequestHandler列表中的任何一个Resource提供。这提供了一种方便的方式来从除Web应用程序根以外的位置(包括类路径上的位置)提供静态资源。该cache-period属性可用于设置远期未来的到期标头(1年是优化工具的推荐,如Page Speed和YSlow),以便客户端更有效地利用它们。处理程序也正确地评估Last-Modified标题(如果存在),以便适当地304返回状态代码,避免客户端已经缓存的资源的不必要的开销。例如,/resources/**public-resources

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”).addResourceLocations(“/ public-resources /”);
	}

}

和XML一样:

<mvc:resources  mapping = “/ resources / **”  location = “/ public-resources /” />

为了在未来1年的时间内为这些资源提供服务,以确保最大限度地利用浏览器缓存并减少浏览器发出的HTTP请求:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/资源/ **”).addResourceLocations(“/公共资源/”).setCachePeriod(31556926);
	}

}

在XML中:

<mvc:resources  mapping = “/ resources / **”  location = “/ public-resources /”  cache-period = “31556926” />

有关详细信息,请参阅静态资源的HTTP缓存支持

mapping属性必须是可以被使用的蚂蚁图案 SimpleUrlHandlerMapping,并且location属性必须指定一个或多个有效的资源目录位置。可以使用逗号分隔的值列表来指定多个资源位置。指定的位置将按照指定的顺序检查是否存在任何给定请求的资源。例如,要启用来自Web应用程序根目录和类路径/META-INF/public-web-resources/中任何jar中已知路径 的资源的使用,请执行以下操作:

@EnableWebMvc 
@Configuration
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”)
				.addResourceLocations(“/”“classpath:/ META-INF / public-web-resources /”);
	}

}

在XML中:

<mvc:resources  mapping = “/ resources / **”  location = “/,classpath:/ META-INF / public-web-resources /” />

当部署新版本的应用程序时可能会更改的资源时,建议您将版本字符串合并到用于请求资源的映射模式中,以便您可以强制客户端请求新部署的应用程序资源版本。版本化URL的支持内置在框架中,可以通过在资源处理程序上配置资源链来启用。该链由ResourceResolver 一个或多个ResourceTransformer实例之后的一个实例组成。他们一起可以提供任意解决和资源转型。

内置VersionResourceResolver可配置不同的策略。例如,FixedVersionStrategy可以使用属性,日期或其他作为版本。A ContentVersionStrategy使用从资源的内容计算的MD5哈希(称为“指纹识别”URL)。请注意,VersionResourceResolver在服务资源时,会自动将解析的版本字符串用作HTTP ETag标头值。

ContentVersionStrategy是一个很好的默认选择,除非不能使用(例如使用JavaScript模块加载器)的情况。您可以针对不同的模式配置不同的版本策略,如下所示。请记住,计算基于内容的版本是昂贵的,因此在生产中应该启用资源链缓存。

Java配置示例

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(“/ resources / **”)
				.addResourceLocations(“/ public-resources /”)
				.resourceChain(真).addResolver(
					new VersionResourceResolver()。addContentVersionStrategy(“/ **”));
	}

}

XML示例:

<MVC:资源 映射 = “/资源/ **”  位置 = “/公共资源/” > 
	<MVC:资源链> 
		<MVC:资源高速缓存/> 
		<MVC:解析器> 
			<MVC:版本-解析器> 
				<mvc:content-version-strategy  patterns = “/ **” /> 
			</ mvc:version-resolver> 
		</ mvc:resolvers> 
	</ mvc:resource-chain> 
</ mvc:resources>

为了使上述工作,应用程序还必须使用版本来呈现URL。最简单的方法是配置 ResourceUrlEncodingFilter其中包含响应并覆盖其encodeURL方法。这将在JSP,FreeMarker以及调用响应encodeURL方法的任何其他视图技术中起作用 。或者,应用程序还可以直接注入和使用 ResourceUrlProviderbean,该bean将自动使用MVC Java配置和MVC命名空间声明。

还支持Webjars WebJarsResourceResolver,当"org.webjars:webjars-locator"库位于类路径时,Webjars会自动注册。该解析器允许资源链从HTTP GET请求中解析版本不可知库 "GET /jquery/jquery.min.js"将返回资源"/jquery/1.2.0/jquery.min.js"。它也可以通过在模板中重写资源URL来实现<script src="/jquery/jquery.min.js"/> → <script src="/jquery/1.2.0/jquery.min.js"/>

18.16.10回退在“默认”Servlet服务资源

这允许映射DispatcherServlet到“/”(从而覆盖容器的默认Servlet的映射),同时仍允许静态资源请求由容器的默认Servlet处理。它DefaultServletHttpRequestHandler使用“/ **”的URL映射和相对于其他URL映射的最低优先级来配置。

该处理程序将所有请求转发到默认Servlet。因此,重要的是按照所有其他URL的顺序保持最后HandlerMappings。如果您使用<mvc:annotation-driven>或者如果您正在设置自己的自定义HandlerMapping实例,请确保将其order属性设置为低于该值的值DefaultServletHttpRequestHandler,否则将是这种情况Integer.MAX_VALUE

要使用默认设置启用该功能,请使用:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable();
	}

}

或在XML中:

<MVC:默认servlet的处理程序/>

覆盖“/”Servlet映射的注意事项是,RequestDispatcher默认的Servlet必须通过名称而不是路径检索。该 DefaultServletHttpRequestHandler会尝试自动检测在启动时容器中的默认的Servlet,使用大多数主要的Servlet容器(包括软件Tomcat,Jetty的GlassFish,JBoss和树脂中,WebLogic和WebSphere)已知名称的列表。如果默认的Servlet已经使用不同的名称自定义配置,或者如果使用了默认Servlet名称未知的其他Servlet容器,那么必须明确地提供默认的Servlet名称,如下例所示:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable(“myCustomDefaultServlet”);
	}

}

或在XML中:

<mvc:default-servlet-handler  default-servlet-name = “myCustomDefaultServlet” />

18.16.11路径匹配

这允许自定义与URL映射和路径匹配相关的各种设置。有关各个选项的详细信息,请查看 PathMatchConfigurer API。

以下是Java配置中的一个示例:

@Configuration 
@EnableWebMvc
 public  class WebConfig extends WebMvcConfigurerAdapter {

	@Override
	 public  void configurePathMatch(PathMatchConfigurer configurer){
		配置者
		    .setUseSuffixPatternMatch(真)
		    .setUseTrailingSlashMatch(假)
		    .setUseRegisteredSuffixPatternMatch(真)
		    .setPathMatcher(antPathMatcher())
		    .setUrlPathHelper(urlPathHelper());
	}

	@Bean
	 public UrlPathHelper urlPathHelper(){
	     // ...
	}

	@Bean
	 public PathMatcher antPathMatcher(){
	     // ...
	}

}

和XML一样,使用<mvc:path-matching>元素:

<mvc:annotation-driven>
    <mvc:path-matching
        suffix-pattern="true"
        trailing-slash="false"
        registered-suffixes-only="true"
        path-helper="pathHelper"
        path-matcher="pathMatcher"/>
</mvc:annotation-driven>

<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>

18.16.12消息转换器

如果要替换由Spring MVC创建的默认转换器,或者 如果您只想自定义它们或将额外的转换器添加到默认转换器,HttpMessageConverter则可以通过重写Java配置来实现 定制。configureMessageConverters()extendMessageConverters()

以下是使用自定义ObjectMapper而不是默认值添加Jackson JSON和XML转换器的示例 :

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
				.indentOutput(true)
				.dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
				.modulesToInstall(new ParameterNamesModule());
		converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
	}在此示例中,Jackson2ObjectMapperBuilder用于为两者创建通用配置,MappingJackson2HttpMessageConverterMappingJackson2XmlHttpMessageConverter启用缩进,自定义日期格式以及 jackson-module-parameter-name的注册 ,以增加对访问参数名称的支持(Java 8中添加的功能)。ngJackson2XmlHttpMessageConverter with indentation enabled, a customized date format and the registration of jackson-module-parameter-names that adds support for accessing parameter names (feature added in Java 8).
[注意]
使用Jackson XML支持启用缩进功能需要 woodstox-core-asl 除依赖之外jackson-dataformat-xml

其他有趣的杰克逊模块可用:

  1. jackson-datatype-money:支持javax.money类型(非官方模块)
  2. jackson-datatype-hibernate:支持Hibernate的特定类型和属性(包括延迟加载方面)

也可以在XML中执行相同的操作:

<mvc:annotation-driven> 
    <mvc:message-converters> 
        <bean  class = “org.springframework.http.converter.json.MappingJackson2HttpMessageConverter” > 
            <property  name = “objectMapper”  ref = “objectMapper” /> 
        </ bean> 
        <bean  class = “org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter” > 
            <property  name = “objectMapper”  ref = “xmlMapper” /> 
        </ bean> 
    </ mvc:message-converters> 
</ mvc:注解驱动>

<bean  id = “objectMapper”  class = “org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean” 
      p:indentOutput = “true” 
      p:simpleDateFormat = “yyyy-MM-dd” 
      p:modulesToInstall = “com.fasterxml.jackson .module.paramnames.ParameterNamesModule“ />

<bean  id = “xmlMapper”  parent = “objectMapper”  p:createXmlMapper = “true” />

18.16.13使用MVC Java Config进行高级定制

从上述示例可以看出,MVC Java配置和MVC命名空间提供了不需要深入了解为您创建的基础bean的更高级别的构造。相反,它可以帮助您专注于您的应用程序需求。但是,在某些时候,您可能需要更细致的控制,或者您可能只想了解底层配置。

更精细控制的第一步是查看为您创建的基础bean。在MVC Java配置中,您可以看到javadoc和其中的@Bean方法 WebMvcConfigurationSupport。此类中的配置通过注释自动导入@EnableWebMvc。其实如果你打开@EnableWebMvc你可以看到@Import声明。

更精细控制的下一步是定制一个在其中创建的bean之一WebMvcConfigurationSupport或可能提供您自己的实例的属性。这需要两件事情 – 删除@EnableWebMvc注释,以防止导入,然后从DelegatingWebMvcConfiguration一个子类 扩展WebMvcConfigurationSupport。这是一个例子:

@Configuration
 public  class WebConfig extends DelegatingWebMvcConfiguration {

	@Override
	 public  void addInterceptors(InterceptorRegistry registry){
		 // ...
	}

	@Override 
	@Bean
	 public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
		 //创建或让“超级”创建适配器
		//然后自定义其属性之一
	}

}
[注意]
应用程序应该只有一个配置扩展DelegatingWebMvcConfiguration 或单个@EnableWebMvc注释类,因为它们都注册了相同的底层bean。以这种方式修改bean不会阻止您使用本节前面显示的任何更高级别的构造。WebMvcConfigurerAdapter子类和 WebMvcConfigurer实现仍在使用中。

18.16.14使用MVC命名空间进行高级自定义

对您创建的配置的细粒度控制对于MVC命名空间来说有点困难。

如果您确实需要这样做,而不是复制其提供的配置,请考虑配置一个BeanPostProcessor检测要按类型自定义的bean,然后根据需要修改其属性。例如:

@Component
 public  class MyPostProcessor实现 BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean,String name)throws BeansException {
		 if(Bean instanceof RequestMappingHandlerAdapter){
			 //修改适配器的属性
		}
	}

}

请注意,MyPostProcessor需要将其包含在其中<component scan/>以便检测到它,或者如果您喜欢,可以使用XML bean声明明确声明它。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 《Spring 5 官方文档》18. Web MVC 框架

  • Trackback 关闭
  • 评论 (0)
  1. 暂无评论

return top