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

《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 中请求处理流程

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声明和映射:


	
		example
		org.springframework.web.servlet.DispatcherServlet
		1
	

	
		example
		/example/*
	


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

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

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


	
		golfing
		org.springframework.web.servlet.DispatcherServlet
		1
	
	
		golfing
		/golfing/*
	

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

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


	
		contextConfigLocation
		/WEB-INF/root-context.xml
	
	
		dispatcher
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			
		
		1
	
	
		dispatcher
		/*
	
	
		org.springframework.web.context.ContextLoaderListener
	

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代码片段所示:


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

	 base-package="org.springframework.samples.petclinic.web"/>
	


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 get() {
		return appointmentBook.getAppointmentsForToday();
	}

	@RequestMapping(path = "/{day}", method = RequestMethod.GET)
	public Map 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 get() {
		return appointmentBook.getAppointmentsForToday();
	}

	@GetMapping("/{day}")
	public Map 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等),则可能需要显式配置基于类的代理。 例如,使用,更改为

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 参数上使用@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/[email protected] vs /person/[email protected]。此外,如下面的说明中所解释的,后缀模式匹配以及内容协商可能在某些情况下用于尝试恶意攻击,并且有充分的理由有意义地限制它们。

有关后缀模式匹配配置,请参见第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 matrixVars,
		@MatrixVariable(pathVar="petId"") MultiValueMap 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命名空间中,元素具有一个应该设置为true的enable-matrix-variables属性。 默认情况下设置为false。

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

	 enable-matrix-variables="true"/>

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 或MultiValueMap 参数上使用@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.
  • 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命名空间”。
 class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
	 name="messageConverters">
		 id="beanList">
			 bean="stringHttpMessageConverter"/>
			 bean="marshallingHttpMessageConverter"/>
		
	


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

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


 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或者 MultiValueMap参数上使用注释时,地图将填充所有请求参数。

使用@RequestBody注释映射请求体

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

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

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

  • ByteArrayHttpMessageConverter 转换字节数组。
  • StringHttpMessageConverter 转换字符串。
  • FormHttpMessageConverter 将表单数据转换为/从MultiValueMap 转换。
  • 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” > 
		 ID = “beanList” > 
			  = “stringHttpMessageConverter” /> 
			< ref  bean = “marshallingHttpMessageConverter” /> 
		 
	 
 

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

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

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

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

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

有关通过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  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  “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建议控制器”一节。

当没有明确指定模型属性名称时会发生什么?在这种情况下,根据其类型将默认名称分配给模型属性。例如,如果该方法返回类型的对象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){}

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

  • 由于使用@SessionAttributes - 可能已经在模型中- 请参阅 “使用@SessionAttributes将模型属性存储在请求之间的HTTP会话中”一节。
  • 由于@ModelAttribute在同一控制器中的方法,它可能已经在模型中 – 如上一节所述。
  • 它可以基于URI模板变量和类型转换器(下面更详细地解释)来检索。
  • 它可以使用其默认构造函数实例化。

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

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

在此示例中,模型属性(即“account”)的名称与URI模板变量的名称相匹配。如果您注册Converter,可以将 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的 表单标签的帮助下显示错误的。

请注意,在某些情况下,在没有数据绑定的情况下获取模型中的属性可能是有用的。对于这种情况,您可以将其注入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

 
	 httpPutFormFilter  
	 org.springframework.web.filter.HttpPutFormContentFilter  
 

 
	 httpPutFormFilter  
	 dispatcherServlet  
 

 
	 dispatcherServlet  
	 org.springframework.web。 servlet.DispatcherServlet  

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

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

使用@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-Encoding Keep-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注解上的使用MapMultiValueMapHttpHeaders参数,则地图被填充有所有标头值。

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

方法参数和类型转换

从请求中提取的基于字符串的值(包括请求参数,路径变量,请求标头和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。

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

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

	
	 

	
请注意,尽管@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  processUpload( final MultipartFile文件){

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

}

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

@RequestMapping(“/ quotes”)
@ResponseBody
 public DeferredResult  quotes(){
	DeferredResult  deferredResult = new DeferredResult ();
	//将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的应用程序:

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

	...

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

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

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

	 
		 Spring OpenEntityManagerInViewFilter  
		 org.springframework。〜.OpenEntityManagerInViewFilter  
		 true  
	

	<滤波器映射> 
		<滤波器名称>春OpenEntityManagerInViewFilter  
		 / *  
		<调度> REQUEST  
		<调度> ASYNC  
	

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

Spring MVC配置

MVC Java配置和MVC命名空间提供了配置异步请求处理的选项。WebMvcConfigurer具有该方法 configureAsyncSupport同时具有子元素。

这些允许您配置用于异步请求的默认超时值,如果未设置,则取决于底层的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路径将不匹配。

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

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

18.4.1用HandlerInterceptor拦截请求

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

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

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

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

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

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

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 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以呈现视图。

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

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

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

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

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

 id = “jspViewResolver”  class = “org.springframework.web.servlet.view.InternalResourceViewResolver” > 
	 name = “viewClass”  value = “org.springframework.web.servlet.view.JstlView” /> 
	 name = “prefix”  value = “/ WEB-INF / jsp /” /> 
	 name = “suffix”  value = “.jsp” /> 


 id = “excelViewResolver”  class = “org.springframework.web.servlet.view.XmlViewResolver” > 
	 name = “order”  value = “1” /> 
	 name = “location”  value = “/ WEB- INF / views.xml“ /> 


<! -  in views.xml  - >

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

如果一个特定的视图解析器不会产生视图,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不会解析视图本身,而是委托给其他视图解析器,选择类似于客户端请求的表示的视图。客户端可以从服务器请求表示方式存在两种策略:

  • 通常通过在URI中使用不同的文件扩展名为每个资源使用不同的URI。例如,URI http://www.example.com/users/fred.pdf请求用户fred的PDF表示,并http://www.example.com/users/fred.xml请求XML表示。
  • 使用相同的URI来为客户端定位资源,但设置AcceptHTTP请求标头以列出它理解的媒体类型。例如,一个HTTP请求, http://www.example.com/users/fred其中一个Accept头设置为application/pdf 请求用户fred的PDF表示,同时 http://www.example.com/users/fred使用Accept头设置来text/xml请求XML表示。这个策略被称为 内容谈判。
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

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

 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  contentList = new ArrayList ();

	@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”%>
...
获取地址

上述示例依赖于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

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

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

	<! - 以秒为单位。如果设置为-1,则cookie不会持久化(在浏览器关闭时删除) - > 
	 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将会将站点语言更改为荷兰语。

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


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

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

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” %> 
 
	 
		 rel = “stylesheet”  href =  type = ”text / css“ /> 
	 
	 style = ”background = >
		...
	 

默认情况下,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

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

	<! - 可用属性之一; 最大文件大小(以字节为单位) - > 
	 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配置:

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

18.10.4以表单处理文件上传

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

 
	 
		</span>上传文件请<span class="hl-tag"></ title> </span>
	<span class="hl-tag"></ head> </span>
	<span class="hl-tag"><body> </span>
		<span class="hl-tag"><h1></span>请上传文件<span class="hl-tag"></ h1> </span>
		<span class="hl-tag"><form </span> <span class="hl-attribute">method</span> = <span class="hl-value">“post” </span> <span class="hl-attribute">action</span> = <span class="hl-value">“/ form” </span> <span class="hl-attribute">enctype</span> = <span class="hl-value">“multipart / form-data” </span><span class="hl-tag">> </span>
			<span class="hl-tag"><input </span> <span class="hl-attribute">type</span> = <span class="hl-value">“text” </span> <span class="hl-attribute">name</span> = <span class="hl-value">“name” </span><span class="hl-tag">/> </span>
			<span class="hl-tag"><input </span> <span class="hl-attribute">type</span> = <span class="hl-value">“file” </span> <span class="hl-attribute">name</span> = <span class="hl-value">“file” </span><span class="hl-tag">/> </span>
			<span class="hl-tag"><input </span> <span class="hl-attribute">type</span> = <span class="hl-value">“submit”</span><span class="hl-tag">/> </span>
		<span class="hl-tag"></ form> </span>
	<span class="hl-tag"></ body> </span>
<span class="hl-tag"></ html></span></pre> 
    <p>下一步是创建一个处理文件上传的控制器。该控制器非常类似于正常注释<code class="literal">@Controller</code>,除了我们使用<code class="literal">MultipartHttpServletRequest</code>或<code class="literal">MultipartFile</code>在方法参数中:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Controller</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> FileUploadController {

	<em><span class="hl-annotation">@PostMapping(“/ form”)</span></em>
	<span class="hl-keyword"> public</span> String handleFormUpload(<em><span class="hl-annotation"> @RequestParam(“name”)</span></em> String name,
			<em><span class="hl-annotation"> @RequestParam(“file”)</span></em> MultipartFile文件){

		<span class="hl-keyword">if</span>(!file.isEmpty()){
			 <span class="hl-keyword">byte</span> [] bytes = file.getBytes();
			<span class="hl-comment">//存储字节某处</span>
			<span class="hl-keyword">返回</span> <span class="hl-string">“redirect:uploadSuccess”</span> ;
		}

		<span class="hl-keyword">返回</span> <span class="hl-string">“redirect:uploadFailure”</span> ;
	}

}</pre> 
    <p>注意<code class="literal">@RequestParam</code>方法参数如何映射到表单中声明的​​输入元素。在这个例子中,没有什么是完成的<code class="literal">byte[]</code>,但实际上你可以将它保存在数据库中,将它存储在文件系统上,等等。</p> 
    <p>当使用Servlet 3.0多部分解析时,您也可以使用<code class="literal">javax.servlet.http.Part</code>方法参数:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Controller</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> FileUploadController {

	<em><span class="hl-annotation">@PostMapping(“/ form”)</span></em>
	<span class="hl-keyword"> public</span> String handleFormUpload(<em><span class="hl-annotation"> @RequestParam(“name”)</span></em> String name,
			<em><span class="hl-annotation"> @RequestParam(“file”)</span></em>零件文件){

		InputStream inputStream = file.getInputStream();
		<span class="hl-comment">//将上传文件的字节存储在某处</span>

		<span class="hl-keyword">返回</span> <span class="hl-string">“redirect:uploadSuccess”</span> ;
	}

}</pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.10.5处理来自编程客户端的文件上传请求</h3> 
      </div> 
     </div> 
    </div> 
    <p>也可以在RESTful服务方案中从非浏览器客户端提交多部分请求。所有上述示例和配置也适用于此。然而,与通常提交文件和简单表单字段的浏览器不同,编程客户端还可以发送特定内容类型的更复杂数据,例如具有文件的多部分请求,第二部分使用JSON格式的数据:</p> 
    <pre class="literallayout">POST / someUrl
Content-Type:multipart / mixed

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

{
	“名称”:“值”
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
内容处理:表单数据; NAME =“文件的数据”; 文件名= “file.properties”
Content-Type:text / xml
内容传输编码:8bit
...文件数据...</pre> 
    <p>您可以使用<code class="literal">@RequestParam("meta-data") String metadata</code>controller method参数访问名为“meta-data”的部分。但是,您可能更喜欢接受从请求部分正文中的JSON格式数据初始化的强类型对象,非常类似于<code class="literal">@RequestBody</code>在非帮助下将非多部分请求的正文转换为目标对象的方式<code class="literal">HttpMessageConverter</code>。</p> 
    <p>您可以使用<code class="literal">@RequestPart</code>注释而不是<code class="literal">@RequestParam</code>注释用于此目的。它允许您通过<code class="literal">HttpMessageConverter</code>考虑多部分的<code class="literal">'Content-Type'</code>标题来传递特定多部分的内容:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@PostMapping(“/ someUrl”)</span></em>
<span class="hl-keyword"> public</span> String onSubmit(<span class="strong"><strong> @RequestPart(“meta-data”)MetaData元数据,
		@RequestPart(“file-data”)MultipartFile文件</strong></span>){

	<span class="hl-comment">// ...</span>

}</pre> 
    <p>注意如何<code class="literal">MultipartFile</code>使用<code class="literal">@RequestParam</code>或 <code class="literal">@RequestPart</code>互换访问方法参数。但是,<code class="literal">@RequestPart("meta-data") MetaData</code>在这种情况下,该方法参数将被读取为基于其<code class="literal">'Content-Type'</code>头部的JSON内容,并在此帮助下进行转换<code class="literal">MappingJackson2HttpMessageConverter</code>。</p> 
   </div> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.11处理例外</h2> 
     </div> 
    </div> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.1 HandlerExceptionResolver</h3> 
      </div> 
     </div> 
    </div> 
    <p>Spring <code class="literal">HandlerExceptionResolver</code>实现处理控制器执行期间发生的意外异常。一个<code class="literal">HandlerExceptionResolver</code>有点象异常映射的,你可以在Web应用程序描述符定义<code class="literal">web.xml</code>。但是,它们提供了一种更灵活的方法。例如,它们提供有关在抛出异常时正在执行哪个处理程序的信息。此外,处理异常的编程方式可以在请求转发到另一个URL之前提供更多的响应选项(与使用Servlet特定的异常映射相同的最终结果)。</p> 
    <p>除了实现<code class="literal">HandlerExceptionResolver</code>接口,这只是一个实现<code class="literal">resolveException(Exception, Handler)</code>方法和返回的问题 <code class="literal">ModelAndView</code>,您还可以使用提供的<code class="literal">SimpleMappingExceptionResolver</code>或创建 <code class="literal">@ExceptionHandler</code>方法。将<code class="literal">SimpleMappingExceptionResolver</code>让您采取可能被抛出的异常的类名,并将它映射到视图名。这在功能上等同于Servlet API的异常映射功能,但也可以从不同的处理程序实现更精细的异常映射。<code class="literal">@ExceptionHandler</code>另一方面,注释可以用于应该调用来处理异常的方法。这样的方法可以定义在本地内部,<code class="literal">@Controller</code>或者可以在<code class="literal">@Controller</code>类中定义时应用于许多类 <code class="literal">@ControllerAdvice</code>。以下部分将对此进行更详细的解释。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.2 @ExceptionHandler</h3> 
      </div> 
     </div> 
    </div> 
    <p><code class="literal">HandlerExceptionResolver</code>接口和<code class="literal">SimpleMappingExceptionResolver</code> 实现允许您在转发到这些视图之前将异常与声明性地映射到特定视图以及一些可选的Java逻辑。然而,在某些情况下,特别是在依赖于<code class="literal">@ResponseBody</code>方法而不是视图分辨率的情况下,直接设置响应的状态并可选地将错误内容写入响应主体可能会更为方便。</p> 
    <p>你可以用<code class="literal">@ExceptionHandler</code>方法来做到这一点。当在控制器内声明时,这种方法适用于由<code class="literal">@RequestMapping</code>该控制器(或其任何子类)的方法引发的异常。您也可以<code class="literal">@ExceptionHandler</code>在<code class="literal">@ControllerAdvice</code>类中声明一个方法, 在这种情况下,它可以处理<code class="literal">@RequestMapping</code> 来自多个控制器的方法的异常。下面是一个控制器局部<code class="literal">@ExceptionHandler</code>方法的例子 :</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Controller</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> SimpleController {

	<span class="hl-comment">// @RequestMapping方法省略...</span>

	<em><span class="hl-annotation">@ExceptionHandler(IOException.class)</span></em>
	<span class="hl-keyword"> public</span> ResponseEntity <String> handleIOException(IOException ex){
		<span class="hl-comment"> // prepare responseEntity </span>
		<span class="hl-keyword">return</span> responseEntity;
	}

}</pre> 
    <p>该<code class="literal">@ExceptionHandler</code>值可以设置为一个异常类型的数组。如果抛出与列表中的一个类型匹配的异常,<code class="literal">@ExceptionHandler</code>则将调用注释与匹配的方法。如果未设置注释值,则使用列为方法参数的异常类型。</p> 
    <p>与使用<code class="literal">@RequestMapping</code>注释注释的标准控制器方法非常相似,方法的方法参数和返回值<code class="literal">@ExceptionHandler</code>可以是灵活的。例如,<code class="literal">HttpServletRequest</code>可以在Servlet环境中访问。返回类型可以是一个<code class="literal">String</code>,它被解释为一个视图名称,一个<code class="literal">ModelAndView</code>对象,一个<code class="literal">ResponseEntity</code>或者你也可以添加<code class="literal">@ResponseBody</code>一个方法返回值转换为消息转换器并写入响应流。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.3处理标准Spring MVC异常</h3> 
      </div> 
     </div> 
    </div> 
    <p>Spring MVC可能会在处理请求时引发许多异常。根据需要, <code class="literal">SimpleMappingExceptionResolver</code>可以轻松地将任何异常映射到默认错误视图。但是,在使用以自动方式解释响应的客户端时,您将需要在响应中设置特定的状态代码。根据引发的异常,状态代码可能会指示客户端错误(4xx)或服务器错误(5xx)。</p> 
    <p>将<code class="literal">DefaultHandlerExceptionResolver</code>Spring MVC异常转换为特定的错误状态代码。它默认注册了MVC命名空间,MVC Java配置,还有<code class="literal">DispatcherServlet</code>(即不使用MVC命名空间或Java配置时)。下面列出了这个解析器处理的一些异常和相应的状态代码:</p> 
    <div class="informaltable"> 
     <table> 
      <colgroup> 
       <col class="col_1"> 
       <col class="col_2"> 
      </colgroup> 
      <thead> 
       <tr> 
        <th valign="top" align="left">例外</th> 
        <th valign="top" align="left">HTTP状态码</th> 
       </tr> 
      </thead> 
      <tbody> 
       <tr> 
        <td valign="top" align="left"><code class="literal">BindException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">ConversionNotSupportedException</code></td> 
        <td valign="top" align="left">500内部服务器错误)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">HttpMediaTypeNotAcceptableException</code></td> 
        <td valign="top" align="left">406(不可接受)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">HttpMediaTypeNotSupportedException</code></td> 
        <td valign="top" align="left">415(不支持的媒体类型)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">HttpMessageNotReadableException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">HttpMessageNotWritableException</code></td> 
        <td valign="top" align="left">500内部服务器错误)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">HttpRequestMethodNotSupportedException</code></td> 
        <td valign="top" align="left">405(不允许方法)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">MethodArgumentNotValidException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">MissingPathVariableException</code></td> 
        <td valign="top" align="left">500内部服务器错误)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">MissingServletRequestParameterException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">MissingServletRequestPartException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">NoHandlerFoundException</code></td> 
        <td valign="top" align="left">错误(404)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">NoSuchRequestHandlingMethodException</code></td> 
        <td valign="top" align="left">错误(404)</td> 
       </tr> 
       <tr> 
        <td valign="top" align="left"><code class="literal">TypeMismatchException</code></td> 
        <td valign="top" align="left">400(不良要求)</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
    <p>将<code class="literal">DefaultHandlerExceptionResolver</code>通过设置响应的状态透明地工作。但是,您的应用程序可能需要为每个错误响应添加开发人员友好的内容,例如提供REST API时,不会将任何错误内容写入响应正文。你可以准备一个<code class="literal">ModelAndView</code> 和渲染通过视图解析错误内容-通过配置,即<code class="literal">ContentNegotiatingViewResolver</code>,<code class="literal">MappingJackson2JsonView</code>,等等。但是,您可能更喜欢使用<code class="literal">@ExceptionHandler</code>方法。</p> 
    <p>如果您喜欢通过<code class="literal">@ExceptionHandler</code>方法编写错误内容,可以<code class="literal">ResponseEntityExceptionHandler</code>改为扩展 。这是提供处理标准Spring MVC异常和返回<code class="literal">@ControllerAdvice</code>的<code class="literal">@ExceptionHandler</code>方法的类的方便基础 <code class="literal">ResponseEntity</code>。这允许您自定义响应并使用消息转换器写入错误内容。有关<code class="literal">ResponseEntityExceptionHandler</code>详细信息,请参阅 javadocs。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.4 REST控制器异常处理</h3> 
      </div> 
     </div> 
    </div> 
    <p>一个<code class="literal">@RestController</code>可以使用<code class="literal">@ExceptionHandler</code>返回一个方法, <code class="literal">ResponseEntity</code>在响应的主体既提供响应状态和错误的详细信息。这些方法也可以被添加到<code class="literal">@ControllerAdvice</code> 用于子集或所有控制器的异常处理的类中。</p> 
    <p>一个常见的要求是在响应的正文中包含错误详细信息。Spring不会自动执行此操作(尽管Spring Boot),因为响应主体中的错误详细信息的表示是特定于应用程序的。</p> 
    <p>希望在响应体中实现具有错误详细信息的全局异常处理策略的应用程序应考虑扩展抽象基类<code class="literal">ResponseEntityExceptionHandler</code>,为Spring MVC引发的异常提供处理,并提供钩子来自定义响应体以及处理其他异常。只需将扩展类声明为一个Spring bean并用它进行注释<code class="literal">@ControllerAdvice</code>。有关详细信息,请参阅<code class="literal">ResponseEntityExceptionHandler</code>。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.5使用@ResponseStatus注释业务异常</h3> 
      </div> 
     </div> 
    </div> 
    <p>可以注释业务异常<code class="literal">@ResponseStatus</code>。当异常提出时,<code class="literal">ResponseStatusExceptionResolver</code>通过相应地设置响应的状态来处理它。默认情况下,<code class="literal">DispatcherServlet</code>寄存器 <code class="literal">ResponseStatusExceptionResolver</code>,它是可用。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.11.6自定义默认Servlet容器错误页面</h3> 
      </div> 
     </div> 
    </div> 
    <p>当响应的状态设置为错误状态代码并且响应的正文为空时,Servlet容器通常会呈现HTML格式的错误页面。要自定义容器的默认错误页面,可以在其中声明一个<code class="literal"><error-page></code> 元素<code class="literal">web.xml</code>。直到Servlet 3,该元素必须映射到特定的状态代码或异常类型。从Servlet 3开始,不需要映射错误页面,这意味着指定的位置定制了默认的Servlet容器错误页面。</p> 
    <pre class="programlisting"><span class="hl-tag"><error-page> </span>
	<span class="hl-tag"><location></span> / error <span class="hl-tag"></ location> </span>
<span class="hl-tag"></ error-page></span></pre> 
    <p>请注意,错误页面的实际位置可以是容器中的JSP页面或其他一些URL,包括通过一种<code class="literal">@Controller</code>方法处理的页面:</p> 
    <p>编写错误信息时,<code class="literal">HttpServletResponse</code>可通过控制器中的请求属性访问状态码和设置的错误信息 :</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Controller</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> ErrorController {

	<em><span class="hl-annotation">@RequestMapping(path =“/ error”,produce = MediaType.APPLICATION_JSON_UTF8_VALUE)</span>
	<span class="hl-annotation">@ResponseBody</span></em>
	<span class="hl-keyword"> public</span> Map <String,Object> handle(HttpServletRequest request){

		Map <String,Object> map = <span class="hl-keyword">new</span> HashMap <String,Object>();
		map.put(<span class="hl-string">“status”</span>,request.getAttribute(<span class="hl-string">“javax.servlet.error.status_code”</span>));
		map.put(<span class="hl-string">“reason”</span>,request.getAttribute(<span class="hl-string">“javax.servlet.error.message”</span>));

		<span class="hl-keyword">返回</span>地图;
	}

}</pre> 
    <p>或在JSP中:</p> 
    <pre class="programlisting"><span class="hl-tag"><%@ </span> <span class="hl-attribute">page </span> <span class="hl-attribute">contentType</span> = <span class="hl-value">“application / json” </span> <span class="hl-attribute">pageEncoding</span> = <span class="hl-value">“UTF-8” </span><span class="hl-attribute">%> </span>
<span class="hl-attribute">{ </span>
	<span class="hl-attribute">status:<%</span> = <span class="hl-value">request.getAttribute(“javax.servlet.error.status_code”)</span> <span class="hl-attribute">%>,</span>
	<span class="hl-attribute">reason:<%</span> = <span class="hl-value">request。 getAttribute(“javax.servlet.error.message”)</span> <span class="hl-attribute">%> </span>
<span class="hl-attribute">}</span></pre> 
   </div> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.12网络安全</h2> 
     </div> 
    </div> 
   </div> 
   <p>在春季安全项目提供的功能,以防止恶意攻击Web应用程序。请参阅 “CSRF保护”, “安全响应头”以及 “Spring MVC集成”部分中的参考文档。请注意,使用Spring Security来保护应用程序并不一定需要所有功能。例如,CSRF保护可以通过添加<code class="literal">CsrfFilter</code>和配置 <code class="literal">CsrfRequestDataValueProcessor</code>来添加。参见 Spring MVC展示 示例。</p> 
   <p>另一个选择是使用专门用于Web Security的框架。 HDIV是一个这样的框架,并与Spring MVC集成。</p> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.13关于配置支持的公约</h2> 
     </div> 
    </div> 
   </div> 
   <p>对于很多项目,坚持既定的约定和合理的默认值就是它们(项目)所需要的,而Spring Web MVC现在已经明确地支持<span class="emphasis"><em>约定的配置</em></span>。这意味着如果您建立了一组命名约定等等,您可以<span class="emphasis"><em>大幅度</em></span>减少设置处理程序映射,查看解析器,<code class="literal">ModelAndView</code>实例等所需的配置量 。这对于快速原型,并且如果您选择将其推向生产,还可以在代码库中提供一定程度的(始终如一的)一致性。</p> 
   <p>公约超配置支持解决了MVC的三个核心领域:模型,视图和控制器。</p> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.13.1 Controller ControllerClassNameHandlerMapping</h3> 
      </div> 
     </div> 
    </div> 
    <p>该<code class="literal">ControllerClassNameHandlerMapping</code>班是一个<code class="literal">HandlerMapping</code>使用惯例来确定请求的URL和之间的映射实现<code class="literal">Controller</code> 是要处理这些请求的情况。</p> 
    <p>考虑以下简单的<code class="literal">Controller</code>实现。特别注意 课程<span class="emphasis"><em>名称</em></span>。</p> 
    <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class </span> <span class="strong"><strong>ViewShoppingCartController</strong></span> <span class="hl-keyword"> implements</span> Controller {

	<span class="hl-keyword">public</span> ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){
		 <span class="hl-comment">//实现对于这个例子来说不是很重要...</span>
	}

}</pre> 
    <p>以下是相应的Spring Web MVC配置文件的代码段:</p> 
    <pre class="programlisting"><span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping” </span><span class="hl-tag">/></span>

<span class="hl-tag"><bean </span> <span class="hl-attribute">id</span> = <span class="hl-value">“ </span><span class="strong"><strong>viewShoppingCart</strong></span> ”class =“xyzViewShoppingCartController”>
	 <span class="hl-comment"><! - 根据需要注入依赖关系...  - > </span>
<span class="hl-tag"></ bean></span></pre> 
    <p>在<code class="literal">ControllerClassNameHandlerMapping</code>找出所有的处理程序(或 <code class="literal"> Controller</code>在其应用上下文定义的)豆和剥离<code class="literal">Controller</code>掉,以限定其处理程序映射的名称。因此,<code class="literal">ViewShoppingCartController</code>映射到 <code class="literal">/viewshoppingcart*</code>请求URL。</p> 
    <p>我们再来看一些更多的例子,使中心思想变得熟悉。(注意URL中的全部小写,与骆驼<code class="literal">Controller</code>类的类名相反)。</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem"><code class="literal">WelcomeController</code>映射到<code class="literal">/welcome*</code>请求URL</li> 
      <li class="listitem"><code class="literal">HomeController</code>映射到<code class="literal">/home*</code>请求URL</li> 
      <li class="listitem"><code class="literal">IndexController</code>映射到<code class="literal">/index*</code>请求URL</li> 
      <li class="listitem"><code class="literal">RegisterController</code>映射到<code class="literal">/register*</code>请求URL</li> 
     </ul> 
    </div> 
    <p>在<code class="literal">MultiActionController</code>处理程序类的情况下,生成的映射稍微复杂一点。以下<code class="literal">Controller</code>示例中的名称被假定为实现<code class="literal">MultiActionController</code>:</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem"><code class="literal">AdminController</code>映射到<code class="literal">/admin/*</code>请求URL</li> 
      <li class="listitem"><code class="literal">CatalogController</code>映射到<code class="literal">/catalog/*</code>请求URL</li> 
     </ul> 
    </div> 
    <p>如果你按照你的命名的惯例<code class="literal">Controller</code>实现的 <code class="literal">xxxController</code>,将<code class="literal">ControllerClassNameHandlerMapping</code>节省您定义和维护一个潜在的沉闷<span class="emphasis"><em>一长串</em></span><code class="literal">SimpleUrlHandlerMapping</code>(或类似的东西)。</p> 
    <p>本<code class="literal">ControllerClassNameHandlerMapping</code>类扩展<code class="literal">AbstractHandlerMapping</code>基类,所以你可以定义<code class="literal">HandlerInterceptor</code>实例和一切,就像你与许多其他<code class="literal">HandlerMapping</code>的实现。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.13.2 ModelMap(ModelAndView)</h3> 
      </div> 
     </div> 
    </div> 
    <p>该<code class="literal">ModelMap</code>班本质上是一种荣耀<code class="literal">Map</code>,可以使补充说,是要显示(或上)一个对象<code class="literal">View</code>坚持一个共同的命名约定。考虑以下<code class="literal">Controller</code>实现; 注意对象被添加到<code class="literal">ModelAndView</code>没有指定的任何关联的名称。</p> 
    <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> DisplayShoppingCartController <span class="hl-keyword">implements</span> Controller {

	<span class="hl-keyword">public</span> ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){

		列表cartItems = <span class="hl-comment">//获取CartItem对象的列表</span> 
		User user = <span class="hl-comment">//让用户进行购物</span>

		ModelAndView mav = <span class="hl-keyword">new</span> ModelAndView(<span class="hl-string">“displayShoppingCart”</span>); < - 逻辑视图名称

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

		<span class="hl-keyword">返回</span> mav
	}
}</pre> 
    <p>的<code class="literal">ModelAndView</code>类使用一个<code class="literal">ModelMap</code>类,它是一个自定义的<code class="literal">Map</code>,可自动生成用于当对象被添加到它的对象的键实现。在标量对象的情况下,用于确定添加对象的名称的策略是<code class="literal">User</code>使用对象类的短类名。以下示例是为放置到<code class="literal">ModelMap</code>实例中的标量对象生成的名称。</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem"><code class="literal">x.y.User</code>添加 的实例将生成名称<code class="literal">user</code>。</li> 
      <li class="listitem"><code class="literal">x.y.Registration</code>添加 的实例将生成名称<code class="literal">registration</code>。</li> 
      <li class="listitem"><code class="literal">x.y.Foo</code>添加 的实例将生成名称<code class="literal">foo</code>。</li> 
      <li class="listitem"><code class="literal">java.util.HashMap</code>添加 的实例将生成名称<code class="literal">hashMap</code>。在这种情况下,您可能想要明确说明名称,因为<code class="literal">hashMap</code>它不直观。</li> 
      <li class="listitem">添加<code class="literal">null</code>将导致<code class="literal">IllegalArgumentException</code>被抛出。如果您要添加的对象(或对象)可能是<code class="literal">null</code>,那么您还需要明确说明名称。</li> 
     </ul> 
    </div> 
    <div class="sidebar"> 
     <div class="titlepage"> 
      <div> 
       <div> 
        <p class="title"><strong>什么,没有自动多元化?</strong></p> 
       </div> 
      </div> 
     </div> 
     <p>Spring Web MVC的常规配置支持不支持自动多元化。也就是说,你不能添加<code class="literal">List</code>的<code class="literal">Person</code>对象的<code class="literal">ModelAndView</code> ,并有生成的名字会<code class="literal">people</code>。</p> 
     <p>这个决定是经过一番辩论,最终赢得了“最不起眼的原则”。</p> 
    </div> 
    <p>在添加一个<code class="literal">Set</code>或一个之后生成名称的策略<code class="literal">List</code>是窥视集合,获取集合中第一个对象的短类名称,并将其<code class="literal">List</code>附加到名称后面。这同样适用于数组,尽管数组不需要窥视数组内容。几个例子将使得集合名称生成的语义更加清晰:</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem">添加<code class="literal">x.y.User[]</code>零个或多个<code class="literal">x.y.User</code>元素 的数组将生成名称 <code class="literal">userList</code>。</li> 
      <li class="listitem">添加<code class="literal">x.y.Foo[]</code>零个或多个<code class="literal">x.y.User</code>元素 的数组将生成名称 <code class="literal">fooList</code>。</li> 
      <li class="listitem">添加<code class="literal">java.util.ArrayList</code>一个或多个<code class="literal">x.y.User</code>元素的 A 将生成名称 <code class="literal">userList</code>。</li> 
      <li class="listitem">添加<code class="literal">java.util.HashSet</code>一个或多个<code class="literal">x.y.Foo</code>元素的 A 将生成名称 <code class="literal">fooList</code>。</li> 
      <li class="listitem">根本不会添加 一个<span class="emphasis"><em>空</em></span> <code class="literal"> java.util.ArrayList</code>(实际上, <code class="literal">addObject(..)</code>调用本质上是无操作的)。</li> 
     </ul> 
    </div> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.13.3 View – RequestToViewNameTranslator</h3> 
      </div> 
     </div> 
    </div> 
    <p>该<code class="literal">RequestToViewNameTranslator</code>接口确定的逻辑<code class="literal">View</code>当没有这样的逻辑视图名称被明确地提供的名称。它只有一个实现,<code class="literal">DefaultRequestToViewNameTranslator</code>类。</p> 
    <p>该<code class="literal">DefaultRequestToViewNameTranslator</code>请求的URL映射到逻辑视图的名称,与该实施例中:</p> 
    <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> RegistrationController <span class="hl-keyword">implements</span> Controller {

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

}</pre> 
    <pre class="programlisting"><span class="hl-directive"><?xml version =“1.0”encoding =“UTF-8”?> </span>
<span class="hl-tag"><beans </span> <span class="hl-attribute">xmlns</span> = <span class="hl-value">“http://www.springframework.org/schema/beans” </span>
	<span class="hl-attribute">xmlns:xsi</span> = <span class="hl-value">“http://www.w3.org / 2001 / XMLSchema-instance“ </span>
	<span class="hl-attribute">xsi:schemaLocation</span> = <span class="hl-value">”
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd“ </span><span class="hl-tag">></span>

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

	<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“xyRegistrationController” </span><span class="hl-tag">> </span>
		<span class="hl-comment"><! - 根据需要注入依赖项 - > </span>
	<span class="hl-tag"></ bean></span>

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

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

<span class="hl-tag"></豆></span></pre> 
    <p>注意在执行<code class="literal">handleRequest(..)</code>方法no <code class="literal"> View</code>或逻辑视图名称是否被设置<code class="literal">ModelAndView</code>返回的。该 <code class="literal"> DefaultRequestToViewNameTranslator</code>任务是 从请求的URL 生成<span class="emphasis"><em>逻辑视图名称</em></span>。另外,在上述的情况下<code class="literal">RegistrationController</code>,这是在与结合使用<code class="literal">ControllerClassNameHandlerMapping</code>的一个请求的URL<code class="literal">http://localhost/registration.html</code>中的一个逻辑视图名称结果<code class="literal">registration</code> 被生成的<code class="literal">DefaultRequestToViewNameTranslator</code>。该逻辑视图名称然后<code class="literal">/WEB-INF/jsp/registration.jsp</code>由<code class="literal">InternalResourceViewResolver</code>bean 解析为视图 。</p> 
    <div class="tip"> 
     <table summary="Tip" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">您不需要明确定义一个<code class="literal">DefaultRequestToViewNameTranslator</code>bean。如果您喜欢默认设置<code class="literal">DefaultRequestToViewNameTranslator</code>,可以依靠Spring Web MVC <code class="literal">DispatcherServlet</code>实例化此类的实例,如果未明确配置。</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
    <p>当然,如果您需要更改默认设置,那么您需要明确配置自己的<code class="literal">DefaultRequestToViewNameTranslator</code>bean。有关可配置<code class="literal">DefaultRequestToViewNameTranslator</code>的各种属性的详细信息,请查阅全面的 javadocs。</p> 
   </div> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.14 HTTP缓存支持</h2> 
     </div> 
    </div> 
   </div> 
   <p>良好的HTTP缓存策略可以显着提高Web应用程序的性能和客户体验。所述<code class="literal">'Cache-Control'</code>HTTP响应报头主要是为这个负责,使用条件报头,例如沿<code class="literal">'Last-Modified'</code>和<code class="literal">'ETag'</code>。</p> 
   <p>该<code class="literal">'Cache-Control'</code>HTTP响应头劝告私有的高速缓存(如浏览器),以及他们如何缓存进一步重用HTTP响应的公共高速缓存(例如代理)。</p> 
   <p>一个的ETag(实体标签)是由HTTP / 1.1兼容的Web用于在给定的URL来确定内容改变服务器返回的HTTP响应报头中。它可以被认为是<code class="literal">Last-Modified</code>标题的更复杂的继承者 。当服务器返回具有ETag头的表示时,客户端可以在标题中的后续GET中使用此<code class="literal">If-None-Match</code>头。如果内容没有改变,服务器返回<code class="literal">304: Not Modified</code>。</p> 
   <p>本节介绍在Spring Web MVC应用程序中配置HTTP缓存的不同选择。</p> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.14.1缓存控制HTTP头</h3> 
      </div> 
     </div> 
    </div> 
    <p>Spring Web MVC支持许多用例和方式为应用程序配置“Cache-Control”标头。虽然RFC 7234第5.2.2节 完全描述了标题及其可能的指令,但是有几种方法可以解决最常见的情况。</p> 
    <p>Spring Web MVC在其几个API中使用配置约定 <code class="literal">setCachePeriod(int seconds)</code>:</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem">甲<code class="literal">-1</code>值将不生成<code class="literal">'Cache-Control'</code>响应头。</li> 
      <li class="listitem">一个<code class="literal">0</code>值,将使用防止缓存<code class="literal">'Cache-Control: no-store'</code>指令。</li> 
      <li class="listitem">一个<code class="literal">n > 0</code>值将缓存对于给定的响应<code class="literal">n</code>使用秒 <code class="literal">'Cache-Control: max-age=n'</code>指令。</li> 
     </ul> 
    </div> 
    <p>该<code class="literal">CacheControl</code>生成器类简单地描述了可用的“缓存控制”指令,并使其更容易建立自己的HTTP缓存策略。一旦构建,一个<code class="literal">CacheControl</code>实例就可以被接受为几个Spring Web MVC API中的参数。</p> 
    <pre class="programlisting"><span class="hl-comment">//缓存一小时 - “Cache-Control:max-age = 3600”</span> 
   CacheControl ccCacheOneHour = CacheControl.maxAge(<span class="hl-number">1</span>,TimeUnit.HOURS);

   <span class="hl-comment">//防止缓存 - “Cache-Control:no-store”</span>
   CacheControl ccNoStore = CacheControl.noStore();

   <span class="hl-comment">//在公共和私有缓存中缓存十天,</span>
   <span class="hl-comment">//公共缓存不应该转换响应</span>
   <span class="hl-comment">//“Cache-Control:max-age = 864000,public,no-transform”</span> 
   CacheControl ccCustom = CacheControl.maxAge(<span class="hl-number">10</span>,TimeUnit 。天)
   									。.noTransform()cachePublic();</pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.14.2 HTTP缓存支持静态资源</h3> 
      </div> 
     </div> 
    </div> 
    <p>应使用适当的<code class="literal">'Cache-Control'</code>条件标题来提供静态资源,以获得最佳性能。 配置一个<code class="literal">ResourceHttpRequestHandler</code>用于提供静态资源的功能不仅<code class="literal">'Last-Modified'</code>通过读取文件的元数据来定位<code class="literal">'Cache-Control'</code>头文件,而且还可以正确配置头文件。</p> 
    <p>您可以设置<code class="literal">cachePeriod</code>属性<code class="literal">ResourceHttpRequestHandler</code>或使用<code class="literal">CacheControl</code>实例,该实例支持更具体的指令:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(<span class="hl-string">“/ resources / **”</span>)
				.addResourceLocations(<span class="hl-string">“/ public-resources /”</span>)
				.setCacheControl(CacheControl.maxAge(<span class="hl-number">1</span>,TimeUnit.HOURS).cachePublic());
	}

}</pre> 
    <p>在XML中:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:resources </span> <span class="hl-attribute">mapping</span> = <span class="hl-value">“/ resources / **” </span> <span class="hl-attribute">location</span> = <span class="hl-value">“/ public-resources /” </span><span class="hl-tag">> </span>
	<span class="hl-tag"><mvc:cache-control </span> <span class="hl-attribute">max-age</span> = <span class="hl-value">“3600” </span> <span class="hl-attribute">cache-public</span> = <span class="hl-value">“true” </span><span class="hl-tag">/> </span>
<span class="hl-tag"></ mvc:resources ></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.14.3支持控制器中的Cache-Control,ETag和Last-Modified响应头</h3> 
      </div> 
     </div> 
    </div> 
    <p>控制器可以支持<code class="literal">'Cache-Control'</code>,<code class="literal">'ETag'</code>和/或<code class="literal">'If-Modified-Since'</code>HTTP请求; 如果<code class="literal">'Cache-Control'</code>要在响应中设置标题,则建议这样做。这涉及计算<code class="literal">long</code>给定请求的lastModified 和/或Etag值,将其与<code class="literal">'If-Modified-Since'</code>请求头值进行比较,并可能返回具有状态代码304(未修改)的响应。</p> 
    <p>如“使用HttpEntity”一节所述,控制器可以使用<code class="literal">HttpEntity</code>类型与请求/响应进行交互 。返回的控制器<code class="literal">ResponseEntity</code>可以包括响应中的HTTP缓存信息,如下所示:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@GetMapping(“/ book / {id}”)</span></em>
<span class="hl-keyword"> public</span> ResponseEntity <Book><em><span class="hl-annotation"> showBook</span></em>(<em><span class="hl-annotation"> @PathVariable</span></em> Long id){

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

	<span class="hl-keyword">返回</span> ResponseEntity
				。好()
				.cacheControl(CacheControl.maxAge(<span class="hl-number">30</span>,TimeUnit.DAYS))
				.eTag(version)<span class="hl-comment">// lastModified也可用</span>
				。体(书);
}</pre> 
    <p>如果客户端发送的条件标头与控制器设置的缓存信息匹配,这样做将不仅包括响应中的头<code class="literal">'ETag'</code>和<code class="literal">'Cache-Control'</code>头,<span class="strong"><strong>还会将响应转换<code class="literal">HTTP 304 Not Modified</code>为空体</strong></span>。</p> 
    <p>一种<code class="literal">@RequestMapping</code>方法也不妨支持相同的行为。这可以实现如下:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@RequestMapping</span></em>
<span class="hl-keyword"> public</span> String myHandleMethod(WebRequest webRequest,Model model){

	<span class="hl-keyword">long</span> lastModified = <span class="hl-comment">// 1.应用程序特定的计算</span>

	<span class="hl-keyword">if</span>(request.checkNotModified(lastModified)){
		 <span class="hl-comment">// 2.快捷方式退出 - 无需进一步处理必需</span>
		<span class="hl-keyword">返回</span> null;
	}

	<span class="hl-comment">// 3.否则进一步请求处理,实际准备内容</span>
	model.addAttribute(...);
	<span class="hl-keyword">返回</span> <span class="hl-string">“myViewName”</span> ;
}</pre> 
    <p>这里有两个关键元素:呼叫<code class="literal">request.checkNotModified(lastModified)</code>和返回<code class="literal">null</code>。前者在返回之前设置适当的响应状态和标题<code class="literal">true</code>。后者与前者相结合,导致Spring MVC不再进一步处理请求。</p> 
    <p>请注意,有3种变体:</p> 
    <div class="itemizedlist"> 
     <ul class="itemizedlist"> 
      <li class="listitem"><code class="literal">request.checkNotModified(lastModified)</code>与比较上次更改时间 <code class="literal">'If-Modified-Since'</code>或<code class="literal">'If-Unmodified-Since'</code>请求头</li> 
      <li class="listitem"><code class="literal">request.checkNotModified(eTag)</code>将eTag与<code class="literal">'If-None-Match'</code>请求标头 进行比较</li> 
      <li class="listitem"><code class="literal">request.checkNotModified(eTag, lastModified)</code> 这两者都意味着两个条件都应该是有效的</li> 
     </ul> 
    </div> 
    <p>当接收到条件<code class="literal">'GET'</code>/ <code class="literal">'HEAD'</code>请求时,<code class="literal">checkNotModified</code>将检查资源是否未被修改,如果是这样,它将导致<code class="literal">HTTP 304 Not Modified</code> 响应。在有条件的<code class="literal">'POST'</code>/ <code class="literal">'PUT'</code>/ <code class="literal">'DELETE'</code>请求的情况下,<code class="literal">checkNotModified</code> 将检查资源是否尚未被修改,如果已经被修改,它将导致 <code class="literal">HTTP 409 Precondition Failed</code>响应以防止并发修改。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.14.4浅层ETag支持</h3> 
      </div> 
     </div> 
    </div> 
    <p>支持ETag由Servlet过滤器提供<code class="literal">ShallowEtagHeaderFilter</code>。它是一个简单的Servlet过滤器,因此可以与任何Web框架结合使用。该 <code class="literal">ShallowEtagHeaderFilter</code>滤波器产生所谓的浅ETag的(相对于深的ETag,稍后详细说明)。该过滤器缓存呈现JSP(或其他内容)的内容,产生超过其MD5哈希,并返回作为ETag头在回应中。下一次客户端发送对同一资源的请求时,它将使用该哈希作为该<code class="literal">If-None-Match</code>值。过滤器检测到这一点,再次渲染视图,并比较两个散列。如果它们相等,<code class="literal">304</code>则返回a。</p> 
    <p>请注意,此策略可节省网络带宽但不节省CPU,因为必须为每个请求计算完整的响应。控制器级别的其他策略(如上所述)可以节省网络带宽并避免计算。</p> 
    <p>此过滤器具有一个<code class="literal">writeWeakETag</code>参数,可将过滤器配置为写入弱符号ETags,如RFC 7232第2.3节<code class="literal">W/"02a2d595e6ed9a0b24f027f2b63b134d6"</code>中所定义 。</p> 
    <p>您配置<code class="literal">ShallowEtagHeaderFilter</code>的<code class="literal">web.xml</code>:</p> 
    <pre class="programlisting"><span class="hl-tag"><filter> </span>
	<span class="hl-tag"><filter-name></span> etagFilter <span class="hl-tag"></ filter-name> </span>
	<span class="hl-tag"><filter-class></span> org.springframework.web.filter.ShallowEtagHeaderFilter <span class="hl-tag"></ filter-class> </span>
	<span class="hl-comment"><! - 配置过滤器写入弱ETag的可选参数
	<INIT-PARAM>
       	<PARAM名称> writeWeakETag </ PARAM名称>
       	<PARAM值>真</ PARAM值>
	</ INIT-param>
	- > </span>
<span class="hl-tag"></ filter></span>

<span class="hl-tag"><filter-mapping> </span>
	<span class="hl-tag"><filter-name></span> etagFilter <span class="hl-tag"></ filter-name> </span>
	<span class="hl-tag"><servlet-name></span> petclinic <span class="hl-tag"></ servlet-name> </span>
<span class="hl-tag"></ filter-mapping></span></pre> 
    <p>或者在Servlet 3.0+环境中,</p> 
    <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> MyWebAppInitializer <span class="hl-keyword">extends</span> AbstractDispatcherServletInitializer {

	<span class="hl-comment">// ...</span>

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> Filter [] getServletFilters(){
		<span class="hl-keyword"> return </span> <span class="hl-keyword">new</span> Filter [] {<span class="hl-keyword"> new</span> ShallowEtagHeaderFilter()};
	}

}</pre> 
    <p>有关详细信息,请参见第18.15节“基于代码的Servlet容器初始化”。</p> 
   </div> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.15基于代码的Servlet容器初始化</h2> 
     </div> 
    </div> 
   </div> 
   <p>在Servlet 3.0+环境中,您可以选择以编程方式配置Servlet容器作为替代方法或与<code class="literal">web.xml</code>文件组合使用。下面是一个注册一个例子<code class="literal">DispatcherServlet</code>:</p> 
   <pre class="programlisting"><span class="hl-keyword">import</span> org.springframework.web.WebApplicationInitializer;

<span class="hl-keyword">public </span> <span class="hl-keyword">class</span> MyWebApplicationInitializer <span class="hl-keyword">实现</span> WebApplicationInitializer {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> onStartup(ServletContext container){
		XmlWebApplicationContext appContext = <span class="hl-keyword">new</span> XmlWebApplicationContext();
		appContext.setConfigLocation(<span class="hl-string">“/WEB-INF/spring/dispatcher-config.xml”</span>);

		ServletRegistration.Dynamic注册= container.addServlet(<span class="hl-string">“dispatcher”</span>,<span class="hl-keyword">新的</span> DispatcherServlet(appContext));
		registration.setLoadOnStartup(<span class="hl-number">1</span>);
		registration.addMapping(<span class="hl-string">“/”</span>);
	}

}</pre> 
   <p><code class="literal">WebApplicationInitializer</code>是由Spring MVC提供的接口,可确保您的实现被检测并自动用于初始化任何Servlet 3容器。<code class="literal">WebApplicationInitializer</code>命名 的抽象基类实现通过简单地覆盖方法来指定servlet映射和配置的位置<code class="literal">AbstractDispatcherServletInitializer</code>更容易注册 。<code class="literal">DispatcherServlet</code><code class="literal">DispatcherServlet</code></p> 
   <p>推荐使用基于Java的Spring配置的应用程序:</p> 
   <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> MyWebAppInitializer <span class="hl-keyword">extends</span> AbstractAnnotationConfigDispatcherServletInitializer {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> Class <?> [] getRootConfigClasses(){
		<span class="hl-keyword"> return</span> null;
	}

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> Class <?> [] getServletConfigClasses(){
		<span class="hl-keyword"> return </span> <span class="hl-keyword">new</span> Class [] {MyWebConfig。<span class="hl-keyword">类</span> };
	}

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> String [] getServletMappings(){
		<span class="hl-keyword"> return </span> <span class="hl-keyword">new</span> String [] {<span class="hl-string"> “/”</span> };
	}

}</pre> 
   <p>如果使用基于XML的Spring配置,您应该直接从 <code class="literal">AbstractDispatcherServletInitializer</code>:</p> 
   <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> MyWebAppInitializer <span class="hl-keyword">extends</span> AbstractDispatcherServletInitializer {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> WebApplicationContext createRootApplicationContext(){
		<span class="hl-keyword"> return</span> null;
	}

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> WebApplicationContext createServletApplicationContext(){
		XmlWebApplicationContext cxt = <span class="hl-keyword">new</span> XmlWebApplicationContext();
		cxt.setConfigLocation(<span class="hl-string">“/WEB-INF/spring/dispatcher-config.xml”</span>);
		<span class="hl-keyword">返回</span> cxt;
	}

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> String [] getServletMappings(){
		<span class="hl-keyword"> return </span> <span class="hl-keyword">new</span> String [] {<span class="hl-string"> “/”</span> };
	}

}</pre> 
   <p><code class="literal">AbstractDispatcherServletInitializer</code>还提供了一种方便的方法来添加<code class="literal">Filter</code> 实例并让它们自动映射到<code class="literal">DispatcherServlet</code>:</p> 
   <pre class="programlisting"><span class="hl-keyword">public </span> <span class="hl-keyword">class</span> MyWebAppInitializer <span class="hl-keyword">extends</span> AbstractDispatcherServletInitializer {

	<span class="hl-comment">// ...</span>

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> protected</span> Filter [] getServletFilters(){
		<span class="hl-keyword"> return </span> <span class="hl-keyword">new</span> Filter [] {<span class="hl-keyword"> new</span> HiddenHttpMethodFilter(),<span class="hl-keyword"> new</span> CharacterEncodingFilter()};
	}

}</pre> 
   <p>每个过滤器根据具体类型添加默认名称并自动映射到<code class="literal">DispatcherServlet</code>。</p> 
   <p>提供单一位置的<code class="literal">isAsyncSupported</code>受保护方法<code class="literal">AbstractDispatcherServletInitializer</code>可以在<code class="literal">DispatcherServlet</code>映射到它的所有过滤器上启用异步支持。默认情况下,该标志设置为<code class="literal">true</code>。</p> 
   <p>最后,如果需要进一步自定义<code class="literal">DispatcherServlet</code>自身,可以覆盖该<code class="literal">createDispatcherServlet</code>方法。</p> 
  </div> 
  <div class="section"> 
   <div class="titlepage"> 
    <div> 
     <div> 
      <h2 class="title">18.16配置Spring MVC</h2> 
     </div> 
    </div> 
   </div> 
   <p>第18.2.1节“WebApplicationContext中的特殊Bean类型”和第18.2.2节“默认DispatcherServlet配置”解释了Spring MVC的特殊bean以及该使用的默认实现<code class="literal">DispatcherServlet</code>。在本节中,您将了解配置Spring MVC的两种其他方法。即MVC Java配置和MVC XML命名空间。</p> 
   <p>MVC Java配置和MVC命名空间提供了类似的默认配置,可以覆盖<code class="literal">DispatcherServlet</code>默认配置。目标是使大多数应用程序不必创建相同的配置,并提供更高级别的构造,用于配置作为简单起始点的Spring MVC,并且需要很少或根本没有底层配置的知识。</p> 
   <p>您可以根据自己的喜好选择MVC Java配置或MVC命名空间。另外,您将在下面进一步看到,使用MVC Java配置,可以更容易地查看底层配置,以及直接对创建的Spring MVC bean进行细粒度的自定义。但是让我们从一开始就开始。</p> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.1启用MVC Java Config或MVC XML命名空间</h3> 
      </div> 
     </div> 
    </div> 
    <p>要启用MVC Java配置,请将注释添加<code class="literal">@EnableWebMvc</code>到您的一个 <code class="literal"> @Configuration</code>类中:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig {

}</pre> 
    <p>要在XML中实现相同的使用<code class="literal">mvc:annotation-driven</code>DispatcherServlet上下文中的元素(如果您没有定义DispatcherServlet上下文,则在根上下文中):</p> 
    <pre class="programlisting"><span class="hl-directive"><?xml version =“1.0”encoding =“UTF-8”?> </span>
<span class="hl-tag"><beans </span> <span class="hl-attribute">xmlns</span> = <span class="hl-value">“http://www.springframework.org/schema/beans” </span>
	<span class="hl-attribute">xmlns:mvc</span> = <span class="hl-value">“http://www.springframework.org / schema / mvc“ </span>
	<span class="hl-attribute">xmlns:xsi</span> = <span class="hl-value">”http://www.w3.org/2001/XMLSchema-instance“ </span>
	<span class="hl-attribute">xsi:schemaLocation</span> = <span class="hl-value">”
		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“ </span><span class="hl-tag">></span>

	<span class="hl-tag"><MVC:注释驱动/></span>

<span class="hl-tag"></豆></span></pre> 
    <p>在上述寄存器一<code class="literal">RequestMappingHandlerMapping</code>,一个<code class="literal">RequestMappingHandlerAdapter</code>,和<code class="literal">ExceptionHandlerExceptionResolver</code>在支持带注释控制器方法的处理请求(等等)使用注释诸如<code class="literal">@RequestMapping</code>, <code class="literal">@ExceptionHandler</code>和其他。</p> 
    <p>它还支持以下功能:</p> 
    <div class="orderedlist"> 
     <ol class="orderedlist" type="1"> 
      <li class="listitem">除了用于数据绑定的JavaBeans PropertyEditor之外,还可以 通过ConversionService实例进行Spring 3样式类型转换。</li> 
      <li class="listitem">支持格式使用数字字段<code class="literal">@NumberFormat</code>通过注释<code class="literal">ConversionService</code>。</li> 
      <li class="listitem">支持的格式 <code class="literal">Date</code>,<code class="literal">Calendar</code>,<code class="literal">Long</code>,和乔达时间使用的字段 <code class="literal">@DateTimeFormat</code>注解。</li> 
      <li class="listitem">支持验证 <code class="literal">@Controller</code>输入<code class="literal">@Valid</code>,如果JSR-303提供程序存在于类路径中。</li> 
      <li class="listitem"> <p class="simpara"><code class="literal">HttpMessageConverter</code>支持<code class="literal">@RequestBody</code>方法参数和<code class="literal">@ResponseBody</code> 方法返回值<code class="literal">@RequestMapping</code>或<code class="literal">@ExceptionHandler</code>方法。</p> <p class="simpara">这是由mvc设计的HttpMessageConverters的完整列表:annotation-driven:</p> 
       <div class="orderedlist"> 
        <ol class="orderedlist" type="a"> 
         <li class="listitem"><code class="literal">ByteArrayHttpMessageConverter</code> 转换字节数组。</li> 
         <li class="listitem"><code class="literal">StringHttpMessageConverter</code> 转换字符串。</li> 
         <li class="listitem"><code class="literal">ResourceHttpMessageConverter</code>转换为/从 <code class="literal">org.springframework.core.io.Resource</code>为所有媒体类型。</li> 
         <li class="listitem"><code class="literal">SourceHttpMessageConverter</code>转换到/从a <code class="literal">javax.xml.transform.Source</code>。</li> 
         <li class="listitem"><code class="literal">FormHttpMessageConverter</code>将表单数据转换为/从a转换<code class="literal">MultiValueMap<String, String></code>。</li> 
         <li class="listitem"><code class="literal">Jaxb2RootElementHttpMessageConverter</code> 将Java对象转换为/从XML中添加 – 如果存在JAXB2,并且类别路径中不存在Jackson 2 XML扩展名。</li> 
         <li class="listitem"><code class="literal">MappingJackson2HttpMessageConverter</code> 转换为/从JSON中添加,如果杰克逊2存在于类路径。</li> 
         <li class="listitem"><code class="literal">MappingJackson2XmlHttpMessageConverter</code>转换为/从XML中添加,如果 Jackson 2 XML扩展存在于类路径中。</li> 
         <li class="listitem"><code class="literal">MappingJackson2SmileHttpMessageConverter</code>转换为/从微笑(二进制JSON) – 添加如果 杰克逊2微笑扩展 存在于类路径。</li> 
         <li class="listitem"><code class="literal">MappingJackson2CborHttpMessageConverter</code>转换为/从CBOR – 添加如果 杰克逊2 CBOR扩展 存在于类路径。</li> 
         <li class="listitem"><code class="literal">AtomFeedHttpMessageConverter</code> 转换Atom Feed – 如果罗马存在于类路径中则添加。</li> 
         <li class="listitem"><code class="literal">RssChannelHttpMessageConverter</code> 转换RSS提要 – 如果罗马存在于类路径中,则会添加。</li> 
        </ol> 
       </div> </li> 
     </ol> 
    </div> 
    <p>有关如何自定义这些默认转换器的更多信息,请参见第18.16.12节“消息转换器”。</p> 
    <div class="note"> 
     <table summary="Note" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">使用<code class="literal">ObjectMapper</code>创建的实例创建 Jackson JSON和XML转换器<code class="literal">Jackson2ObjectMapperBuilder</code> ,以提供更好的默认配置。此构建器使用以下命令自定义Jackson的默认属性: 
         <div class="orderedlist"> 
          <ol class="orderedlist" type="1"> 
           <li class="listitem"><code class="literal">DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES</code> 被禁用。</li> 
           <li class="listitem"><code class="literal">MapperFeature.DEFAULT_VIEW_INCLUSION</code> 被禁用。</li> 
          </ol> 
         </div> <p>如果在类路径中检测到它们,它也会自动注册以下众所周知的模块:</p> 
         <div class="orderedlist"> 
          <ol class="orderedlist" type="1"> 
           <li class="listitem">jackson-datatype-jdk7:支持Java 7类型<code class="literal">java.nio.file.Path</code>。</li> 
           <li class="listitem">jackson-datatype-joda:支持Joda-Time类型。</li> 
           <li class="listitem">jackson-datatype-jsr310:支持Java 8 Date&Time API类型。</li> 
           <li class="listitem">jackson-datatype-jdk8:支持其他Java 8类型<code class="literal">Optional</code>。</li> 
          </ol> 
         </div> </td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.2定制提供的配置</h3> 
      </div> 
     </div> 
    </div> 
    <p>要定制Java中的默认配置,您只需实现该 <code class="literal">WebMvcConfigurer</code>接口,或者更有可能扩展该类<code class="literal">WebMvcConfigurerAdapter</code> 并覆盖所需的方法:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<span class="hl-comment">//覆盖配置方法...</span>

}</pre> 
    <p>要自定义默认配置,<code class="literal"><mvc:annotation-driven/></code>检查它支持哪些属性和子元素。您可以查看 Spring MVC XML模式或使用IDE的代码完成功能来发现哪些属性和子元素可用。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.3转换和格式化</h3> 
      </div> 
     </div> 
    </div> 
    <p>默认情况下,已安装格式化程序<code class="literal">Number</code>和<code class="literal">Date</code>类型,包括对<code class="literal">@NumberFormat</code>和<code class="literal">@DateTimeFormat</code>注释的支持。如果Joda时间存在于类路径上,则还将完全支持Joda Time格式化库。要注册自定义格式化程序和转换器,请覆盖该<code class="literal">addFormatters</code>方法:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addFormatters(FormatterRegistry registry){
		<span class="hl-comment"> //添加格式化程序和/或转换器</span>
	}

}</pre> 
    <p>在MVC命名空间<code class="literal"><mvc:annotation-driven></code>中,添加相同的默认值。注册自定义格式化器和转换器只需提供一个<code class="literal">ConversionService</code>:</p> 
    <pre class="programlisting"><span class="hl-directive"><?xml version =“1.0”encoding =“UTF-8”?> </span>
<span class="hl-tag"><beans </span> <span class="hl-attribute">xmlns</span> = <span class="hl-value">“http://www.springframework.org/schema/beans” </span>
	<span class="hl-attribute">xmlns:mvc</span> = <span class="hl-value">“http://www.springframework.org / schema / mvc“ </span>
	<span class="hl-attribute">xmlns:xsi</span> = <span class="hl-value">”http://www.w3.org/2001/XMLSchema-instance“ </span>
	<span class="hl-attribute">xsi:schemaLocation</span> = <span class="hl-value">”
		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“ </span><span class="hl-tag">></span>

	<span class="hl-tag"><mvc:annotation-driven </span> <span class="hl-attribute">conversion-service</span> = <span class="hl-value">“conversionService” </span><span class="hl-tag">/></span>

	<span class="hl-tag"><豆</span> <span class="hl-attribute">ID</span> = <span class="hl-value">“conversionService” </span>
			<span class="hl-attribute">类</span> = <span class="hl-value">“org.springframework.format.support.FormattingConversionServiceFactoryBean” </span><span class="hl-tag">> </span>
		<span class="hl-tag"><属性</span> <span class="hl-attribute">名</span> = <span class="hl-value">“转换器” </span><span class="hl-tag">> </span>
			<span class="hl-tag"><设定> </span>
				<span class="hl-tag"><豆</span> <span class="hl-attribute">类</span> = <span class="hl-value">“org.example.MyConverter” </span><span class="hl-tag">/> </span>
			<span class="hl-tag"></设定> </span>
		<span class="hl-tag">< / property> </span>
		<span class="hl-tag"><property </span> <span class="hl-attribute">name</span> = <span class="hl-value">“formatters” </span><span class="hl-tag">> </span>
			<span class="hl-tag"><set> </span>
				<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.example.MyFormatter” </span><span class="hl-tag">/> </span>
				<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.example。MyAnnotationFormatterFactory“ </span><span class="hl-tag">/> </span>
			<span class="hl-tag"></ set> </span>
		<span class="hl-tag"></ property> </span>
		<span class="hl-tag"><property </span> <span class="hl-attribute">name</span> = <span class="hl-value">”formatterRegistrars“ </span><span class="hl-tag">> </span>
			<span class="hl-tag"><set> </span>
				<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">”org.example.MyFormatterRegistrar“ </span><span class="hl-tag">/> </span>
			<span class="hl-tag"></ set> </span>
		<span class="hl-tag"></ property> </span>
	<span class="hl-tag"></ bean></span>

<span class="hl-tag"></豆></span></pre> 
    <div class="note"> 
     <table summary="Note" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">参见5.6.4节,“FormatterRegistrar SPI”和<code class="literal">FormattingConversionServiceFactoryBean</code> 关于何时使用FormatterRegistrars更多的信息。</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.4验证</h3> 
      </div> 
     </div> 
    </div> 
    <p>Spring提供了一个验证器接口,可用于在应用程序的所有层中进行验证。在Spring MVC中,您可以将其配置为用作全局<code class="literal">Validator</code>实例,以在遇到<code class="literal">@Valid</code>或<code class="literal">@Validated</code>控制器方法参数时使用,和/或<code class="literal">Validator</code>通过<code class="literal">@InitBinder</code>方法作为控制器中的本地 使用。可以组合全局和本地验证器实例以提供复合验证。</p> 
    <p>Spring还支持JSR-303 / JSR-349 Bean验证,通过<code class="literal">LocalValidatorFactoryBean</code>它可以将Spring <code class="literal">org.springframework.validation.Validator</code> 界面适应Bean验证<code class="literal">javax.validation.Validator</code>合同。这个类可以插入Spring MVC作为下面描述的全局验证器。</p> 
    <p>默认情况下,通过在类路径中检测到一个Bean验证提供程序(如Hibernate Validator)时,可以在Spring MVC中使用<code class="literal">@EnableWebMvc</code>或<code class="literal"><mvc:annotation-driven></code>自动注册Bean验证支持<code class="literal">LocalValidatorFactoryBean</code>。</p> 
    <div class="note"> 
     <table summary="Note" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">有时,将<code class="literal">LocalValidatorFactoryBean</code>注入到控制器或其他类中是很方便的。最简单的方法是声明自己的<code class="literal">@Bean</code>,并且标记它,<code class="literal">@Primary</code>以避免与MVC Java配置提供的冲突。如果你喜欢使用MVC的Java的配置之一,你需要重写 <code class="literal">mvcValidator</code>的方法<code class="literal">WebMvcConfigurationSupport</code>,并声明明确回报的方法<code class="literal">LocalValidatorFactory</code>,而不是<code class="literal">Validator</code>。有关 如何切换扩展提供的配置的信息,请参见第18.16.13节“使用MVC Java Config进行高级定制”。</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
    <p>或者,您可以配置自己的全局<code class="literal">Validator</code>实例:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public</span> Validator getValidator(); {
		<span class="hl-comment"> //返回“全局”验证器</span>
	}

}</pre> 
    <p>和XML:</p> 
    <pre class="programlisting"><span class="hl-directive"><?xml version =“1.0”encoding =“UTF-8”?> </span>
<span class="hl-tag"><beans </span> <span class="hl-attribute">xmlns</span> = <span class="hl-value">“http://www.springframework.org/schema/beans” </span>
	<span class="hl-attribute">xmlns:mvc</span> = <span class="hl-value">“http://www.springframework.org / schema / mvc“ </span>
	<span class="hl-attribute">xmlns:xsi</span> = <span class="hl-value">”http://www.w3.org/2001/XMLSchema-instance“ </span>
	<span class="hl-attribute">xsi:schemaLocation</span> = <span class="hl-value">”
		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“ </span><span class="hl-tag">></span>

	<span class="hl-tag"><mvc:annotation-driven </span> <span class="hl-attribute">validator</span> = <span class="hl-value">“globalValidator” </span><span class="hl-tag">/></span>

<span class="hl-tag"></豆></span></pre> 
    <p>要将全局和本地验证结合起来,只需添加一个或多个本地验证器:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Controller</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> MyController {

	<em><span class="hl-annotation">@InitBinder</span></em>
	<span class="hl-keyword"> protected </span> <span class="hl-keyword">void</span> initBinder(WebDataBinder binder){
		binder.addValidators(<span class="hl-keyword">new</span> FooValidator());
	}

}</pre> 
    <p>使用这个最小配置,任何时候遇到一个<code class="literal">@Valid</code>或<code class="literal">@Validated</code>方法参数,它将被配置的验证器验证。任何验证违规将自动<code class="literal">BindingResult</code>作为方法参数可访问的错误公开,并且也可以在Spring MVC HTML视图中呈现。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.5拦截器</h3> 
      </div> 
     </div> 
    </div> 
    <p>您可以配置<code class="literal">HandlerInterceptors</code>或<code class="literal">WebRequestInterceptors</code>应用于所有传入的请求或限制在特定的URL路径模式。</p> 
    <p>在Java中注册拦截器的示例:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addInterceptors(InterceptorRegistry registry){
		registry.addInterceptor(<span class="hl-keyword">new</span> LocaleInterceptor());
		registry.addInterceptor(<span class="hl-keyword">new</span> ThemeInterceptor())。addPathPatterns(<span class="hl-string">“/ **”</span>).excludePathPatterns(<span class="hl-string">“/ admin / **”</span>);
		registry.addInterceptor(<span class="hl-keyword">new</span> SecurityInterceptor())。addPathPatterns(<span class="hl-string">“/ secure / *”</span>);
	}

}</pre> 
    <p>而在XML中使用<code class="literal"><mvc:interceptors></code>元素:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:interceptors> </span>
	<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.web.servlet.i18n.LocaleChangeInterceptor” </span><span class="hl-tag">/> </span>
	<span class="hl-tag"><mvc:interceptor> </span>
		<span class="hl-tag"><mvc:mapping </span> <span class="hl-attribute">path</span> = <span class="hl-value">“/ **” </span><span class="hl-tag">/> </span>
		<span class="hl-tag"><mvc:exclude-mapping </span> <span class="hl-attribute">path</span> = <span class="hl-value">“/ admin / **” </span><span class="hl-tag">/> </span>
		<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.web.servlet.theme.ThemeChangeInterceptor” </span><span class="hl-tag">/> </span>
	<span class="hl-tag"></ mvc:interceptor> </span>
	<span class="hl-tag"><mvc:interceptor> </span>
		<span class="hl-tag"><mvc:mapping </span> <span class="hl-attribute">path</span> = <span class="hl-value">“/ secure / “ </span><span class="hl-tag">/> </span>
		<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">”org.example.SecurityInterceptor“</span><span class="hl-tag">/> </span>
	<span class="hl-tag"></ mvc:interceptor> </span>
<span class="hl-tag"></ mvc:interceptors></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.6 Content Negotiation</h3> 
      </div> 
     </div> 
    </div> 
    <p>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.</p> 
    <p>The MVC Java config and the MVC namespace register <code class="literal">json</code>, <code class="literal">xml</code>, <code class="literal">rss</code>, <code class="literal"> atom</code> 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).</p> 
    <p>Below is an example of customizing content negotiation options through the MVC Java config:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration</span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword">public</span> <span class="hl-keyword">class</span> WebConfig <span class="hl-keyword">extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword">public</span> <span class="hl-keyword">void</span> configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.mediaType(<span class="hl-string">"json"</span>, MediaType.APPLICATION_JSON);
	}
}</pre> 
    <p>In the MVC namespace, the <code class="literal"><mvc:annotation-driven></code> element has a <code class="literal">content-negotiation-manager</code> attribute, which expects a <code class="literal">ContentNegotiationManager</code>that in turn can be created with a <code class="literal">ContentNegotiationManagerFactoryBean</code>:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:annotation-driven</span> <span class="hl-attribute">content-negotiation-manager</span>=<span class="hl-value">"contentNegotiationManager"</span><span class="hl-tag">/></span>

<span class="hl-tag"><bean</span> <span class="hl-attribute">id</span>=<span class="hl-value">"contentNegotiationManager"</span> <span class="hl-attribute">class</span>=<span class="hl-value">"org.springframework.web.accept.ContentNegotiationManagerFactoryBean"</span><span class="hl-tag">></span>
	<span class="hl-tag"><property</span> <span class="hl-attribute">name</span>=<span class="hl-value">"mediaTypes"</span><span class="hl-tag">></span>
		<span class="hl-tag"><value></span>
			json=application/json
			xml=application/xml
		<span class="hl-tag"></value></span>
	<span class="hl-tag"></property></span>
<span class="hl-tag"></bean></span></pre> 
    <p>If not using the MVC Java config or the MVC namespace, you’ll need to create an instance of <code class="literal">ContentNegotiationManager</code> and use it to configure <code class="literal"> RequestMappingHandlerMapping</code> for request mapping purposes, and <code class="literal"> RequestMappingHandlerAdapter</code> and <code class="literal">ExceptionHandlerExceptionResolver</code> for content negotiation purposes.</p> 
    <p>Note that <code class="literal">ContentNegotiatingViewResolver</code> now can also be configured with a <code class="literal">ContentNegotiationManager</code>, so you can use one shared instance throughout Spring MVC.</p> 
    <p>In more advanced cases, it may be useful to configure multiple <code class="literal"> ContentNegotiationManager</code> instances that in turn may contain custom<code class="literal">ContentNegotiationStrategy</code> implementations. For example you could configure <code class="literal">ExceptionHandlerExceptionResolver</code> with a <code class="literal"> ContentNegotiationManager</code>that always resolves the requested media type to <code class="literal"> "application/json"</code>. 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.</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.7 View Controllers</h3> 
      </div> 
     </div> 
    </div> 
    <p>This is a shortcut for defining a <code class="literal">ParameterizableViewController</code> 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.</p> 
    <p>An example of forwarding a request for <code class="literal">"/"</code> to a view called <code class="literal">"home"</code> in Java:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addViewController(ViewControllerRegistry registry){
		registry.addViewController(<span class="hl-string">“/”</span>).setViewName(<span class="hl-string">“home”</span>);
	}

}</pre> 
    <p>而且在XML中使用相同的<code class="literal"><mvc:view-controller></code>元素:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:view-controller </span> <span class="hl-attribute">path</span> = <span class="hl-value">“/” </span> <span class="hl-attribute">view-name</span> = <span class="hl-value">“home” </span><span class="hl-tag">/></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.8查看解决方案</h3> 
      </div> 
     </div> 
    </div> 
    <p>MVC配置简化了视图解析器的注册。</p> 
    <p>以下是一个Java配置示例,它使用FreeMarker HTML模板配置内容协商视图分辨率,并将Jackson作为<code class="literal">View</code>JSON渲染的默认值:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(<span class="hl-keyword">new</span> MappingJackson2JsonView());
		registry.jsp();
	}

}</pre> 
    <p>和XML一样:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:view-resolvers> </span>
	<span class="hl-tag"><mvc:content-negotiation> </span>
		<span class="hl-tag"><mvc:default-views> </span>
			<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.web.servlet.view.json.MappingJackson2JsonView” </span><span class="hl-tag">/> </span>
		<span class="hl-tag"></ mvc:default-views> </span>
	<span class="hl-tag"></ mvc:content-negotiation> </span>
	<span class="hl-tag"><mvc:jsp /> </span>
<span class="hl-tag"></ mvc:view-resolvers></span></pre> 
    <p>不过请注意,FreeMarker,Tiles,Groovy Markup和脚本模板也需要配置底层视图技术。</p> 
    <p>MVC命名空间提供专用元素。例如与FreeMarker:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:view-resolvers> </span>
	<span class="hl-tag"><mvc:content-negotiation> </span>
		<span class="hl-tag"><mvc:default-views> </span>
			<span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.web.servlet.view.json.MappingJackson2JsonView” </span><span class="hl-tag">/> </span>
		<span class="hl-tag"></ mvc:default-views> </span>
	<span class="hl-tag"></ mvc:content-negotiation> </span>
	<span class="hl-tag"><mvc:freemarker </span> <span class="hl-attribute">cache</span> = <span class="hl-value">“false” </span><span class="hl-tag">/> </span>
<span class="hl-tag"></ mvc:view-resolvers></span>

<span class="hl-tag"><mvc:freemarker-configurer> </span>
	<span class="hl-tag"><mvc:template-loader-path </span> <span class="hl-attribute">location</span> = <span class="hl-value">“/ freemarker” </span><span class="hl-tag">/> </span>
<span class="hl-tag"></ mvc:freemarker-configurer></span></pre> 
    <p>在Java配置中,只需添加相应的“Configurer”bean:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> configureViewResolvers(ViewResolverRegistry registry){
		registry.enableContentNegotiation(<span class="hl-keyword">new</span> MappingJackson2JsonView());
		。registry.freeMarker()高速缓存(假);
	}

	<em><span class="hl-annotation">@Bean</span></em>
	<span class="hl-keyword"> public</span> FreeMarkerConfigurer freeMarkerConfigurer(){
		FreeMarkerConfigurer configurer = <span class="hl-keyword">new</span> FreeMarkerConfigurer();
		configurer.setTemplateLoaderPath(<span class="hl-string">“/ WEB-INF /”</span>);
		<span class="hl-keyword">return</span> configurer;
	}

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

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(<span class="hl-string">“/ resources / **”</span>).addResourceLocations(<span class="hl-string">“/ public-resources /”</span>);
	}

}</pre> 
    <p>和XML一样:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:resources </span> <span class="hl-attribute">mapping</span> = <span class="hl-value">“/ resources / **” </span> <span class="hl-attribute">location</span> = <span class="hl-value">“/ public-resources /” </span><span class="hl-tag">/></span></pre> 
    <p>为了在未来1年的时间内为这些资源提供服务,以确保最大限度地利用浏览器缓存并减少浏览器发出的HTTP请求:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(<span class="hl-string">“/资源/ **”</span>).addResourceLocations(<span class="hl-string">“/公共资源/”</span>).setCachePeriod(<span class="hl-number">31556926</span>);
	}

}</pre> 
    <p>在XML中:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:resources </span> <span class="hl-attribute">mapping</span> = <span class="hl-value">“/ resources / **” </span> <span class="hl-attribute">location</span> = <span class="hl-value">“/ public-resources /” </span> <span class="hl-attribute">cache-period</span> = <span class="hl-value">“31556926” </span><span class="hl-tag">/></span></pre> 
    <p>有关详细信息,请参阅静态资源的HTTP缓存支持。</p> 
    <p>该<code class="literal">mapping</code>属性必须是可以被使用的蚂蚁图案 <code class="literal">SimpleUrlHandlerMapping</code>,并且<code class="literal">location</code>属性必须指定一个或多个有效的资源目录位置。可以使用逗号分隔的值列表来指定多个资源位置。指定的位置将按照指定的顺序检查是否存在任何给定请求的资源。例如,要启用来自Web应用程序根目录和类路径<code class="literal">/META-INF/public-web-resources/</code>中任何jar中已知路径 的资源的使用,请执行以下操作:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@EnableWebMvc </span>
<span class="hl-annotation">@Configuration</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(<span class="hl-string">“/ resources / **”</span>)
				.addResourceLocations(<span class="hl-string">“/”</span>,<span class="hl-string">“classpath:/ META-INF / public-web-resources /”</span>);
	}

}</pre> 
    <p>在XML中:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:resources </span> <span class="hl-attribute">mapping</span> = <span class="hl-value">“/ resources / **” </span> <span class="hl-attribute">location</span> = <span class="hl-value">“/,classpath:/ META-INF / public-web-resources /” </span><span class="hl-tag">/></span></pre> 
    <p>当部署新版本的应用程序时可能会更改的资源时,建议您将版本字符串合并到用于请求资源的映射模式中,以便您可以强制客户端请求新部署的应用程序资源版本。版本化URL的支持内置在框架中,可以通过在资源处理程序上配置资源链来启用。该链由<code class="literal">ResourceResolver</code> 一个或多个<code class="literal">ResourceTransformer</code>实例之后的一个实例组成。他们一起可以提供任意解决和资源转型。</p> 
    <p>内置<code class="literal">VersionResourceResolver</code>可配置不同的策略。例如,<code class="literal">FixedVersionStrategy</code>可以使用属性,日期或其他作为版本。A <code class="literal">ContentVersionStrategy</code>使用从资源的内容计算的MD5哈希(称为“指纹识别”URL)。请注意,<code class="literal">VersionResourceResolver</code>在服务资源时,会自动将解析的版本字符串用作HTTP ETag标头值。</p> 
    <p><code class="literal">ContentVersionStrategy</code>是一个很好的默认选择,除非不能使用(例如使用JavaScript模块加载器)的情况。您可以针对不同的模式配置不同的版本策略,如下所示。请记住,计算基于内容的版本是昂贵的,因此在生产中应该启用资源链缓存。</p> 
    <p>Java配置示例</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler(<span class="hl-string">“/ resources / **”</span>)
				.addResourceLocations(<span class="hl-string">“/ public-resources /”</span>)
				.resourceChain(真).addResolver(
					<span class="hl-keyword">new</span> VersionResourceResolver()。addContentVersionStrategy(<span class="hl-string">“/ **”</span>));
	}

}</pre> 
    <p>XML示例:</p> 
    <pre class="programlisting"><span class="hl-tag"><MVC:资源</span> <span class="hl-attribute">映射</span> = <span class="hl-value">“/资源/ **” </span> <span class="hl-attribute">位置</span> = <span class="hl-value">“/公共资源/” </span><span class="hl-tag">> </span>
	<span class="hl-tag"><MVC:资源链> </span>
		<span class="hl-tag"><MVC:资源高速缓存/> </span>
		<span class="hl-tag"><MVC:解析器> </span>
			<span class="hl-tag"><MVC:版本-解析器> </span>
				<span class="hl-tag"><mvc:content-version-strategy </span> <span class="hl-attribute">patterns</span> = <span class="hl-value">“/ **” </span><span class="hl-tag">/> </span>
			<span class="hl-tag"></ mvc:version-resolver> </span>
		<span class="hl-tag"></ mvc:resolvers> </span>
	<span class="hl-tag"></ mvc:resource-chain> </span>
<span class="hl-tag"></ mvc:resources></span></pre> 
    <p>为了使上述工作,应用程序还必须使用版本来呈现URL。最简单的方法是配置 <code class="literal">ResourceUrlEncodingFilter</code>其中包含响应并覆盖其<code class="literal">encodeURL</code>方法。这将在JSP,FreeMarker以及调用响应<code class="literal">encodeURL</code>方法的任何其他视图技术中起作用 。或者,应用程序还可以直接注入和使用 <code class="literal">ResourceUrlProvider</code>bean,该bean将自动使用MVC Java配置和MVC命名空间声明。</p> 
    <p>还支持Webjars <code class="literal">WebJarsResourceResolver</code>,当<code class="literal">"org.webjars:webjars-locator"</code>库位于类路径时,Webjars会自动注册。该解析器允许资源链从HTTP GET请求中解析版本不可知库 <code class="literal">"GET /jquery/jquery.min.js"</code>将返回资源<code class="literal">"/jquery/1.2.0/jquery.min.js"</code>。它也可以通过在模板中重写资源URL来实现<code class="literal"><script src="/jquery/jquery.min.js"/> → <script src="/jquery/1.2.0/jquery.min.js"/></code>。</p> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.10回退在“默认”Servlet服务资源</h3> 
      </div> 
     </div> 
    </div> 
    <p>这允许映射<code class="literal">DispatcherServlet</code>到“/”(从而覆盖容器的默认Servlet的映射),同时仍允许静态资源请求由容器的默认Servlet处理。它<code class="literal">DefaultServletHttpRequestHandler</code>使用“/ **”的URL映射和相对于其他URL映射的最低优先级来配置。</p> 
    <p>该处理程序将所有请求转发到默认Servlet。因此,重要的是按照所有其他URL的顺序保持最后<code class="literal">HandlerMappings</code>。如果您使用<code class="literal"><mvc:annotation-driven></code>或者如果您正在设置自己的自定义<code class="literal">HandlerMapping</code>实例,请确保将其<code class="literal">order</code>属性设置为低于该值的值<code class="literal">DefaultServletHttpRequestHandler</code>,否则将是这种情况<code class="literal">Integer.MAX_VALUE</code>。</p> 
    <p>要使用默认设置启用该功能,请使用:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable();
	}

}</pre> 
    <p>或在XML中:</p> 
    <pre class="programlisting"><span class="hl-tag"><MVC:默认servlet的处理程序/></span></pre> 
    <p>覆盖“/”Servlet映射的注意事项是,<code class="literal">RequestDispatcher</code>默认的Servlet必须通过名称而不是路径检索。该 <code class="literal">DefaultServletHttpRequestHandler</code>会尝试自动检测在启动时容器中的默认的Servlet,使用大多数主要的Servlet容器(包括软件Tomcat,Jetty的GlassFish,JBoss和树脂中,WebLogic和WebSphere)已知名称的列表。如果默认的Servlet已经使用不同的名称自定义配置,或者如果使用了默认Servlet名称未知的其他Servlet容器,那么必须明确地提供默认的Servlet名称,如下例所示:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
		configurer.enable(<span class="hl-string">“myCustomDefaultServlet”</span>);
	}

}</pre> 
    <p>或在XML中:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:default-servlet-handler </span> <span class="hl-attribute">default-servlet-name</span> = <span class="hl-value">“myCustomDefaultServlet” </span><span class="hl-tag">/></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.11路径匹配</h3> 
      </div> 
     </div> 
    </div> 
    <p>这允许自定义与URL映射和路径匹配相关的各种设置。有关各个选项的详细信息,请查看 PathMatchConfigurer API。</p> 
    <p>以下是Java配置中的一个示例:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration </span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> configurePathMatch(PathMatchConfigurer configurer){
		配置者
		    .setUseSuffixPatternMatch(真)
		    .setUseTrailingSlashMatch(假)
		    .setUseRegisteredSuffixPatternMatch(真)
		    .setPathMatcher(antPathMatcher())
		    .setUrlPathHelper(urlPathHelper());
	}

	<em><span class="hl-annotation">@Bean</span></em>
	<span class="hl-keyword"> public</span> UrlPathHelper urlPathHelper(){
	    <span class="hl-comment"> // ...</span>
	}

	<em><span class="hl-annotation">@Bean</span></em>
	<span class="hl-keyword"> public</span> PathMatcher antPathMatcher(){
	    <span class="hl-comment"> // ...</span>
	}

}</pre> 
    <p>和XML一样,使用<code class="literal"><mvc:path-matching></code>元素:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:annotation-driven></span>
    <span class="hl-tag"><mvc:path-matching</span>
        <span class="hl-attribute">suffix-pattern</span>=<span class="hl-value">"true"</span>
        <span class="hl-attribute">trailing-slash</span>=<span class="hl-value">"false"</span>
        <span class="hl-attribute">registered-suffixes-only</span>=<span class="hl-value">"true"</span>
        <span class="hl-attribute">path-helper</span>=<span class="hl-value">"pathHelper"</span>
        <span class="hl-attribute">path-matcher</span>=<span class="hl-value">"pathMatcher"</span><span class="hl-tag">/></span>
<span class="hl-tag"></mvc:annotation-driven></span>

<span class="hl-tag"><bean</span> <span class="hl-attribute">id</span>=<span class="hl-value">"pathHelper"</span> <span class="hl-attribute">class</span>=<span class="hl-value">"org.example.app.MyPathHelper"</span><span class="hl-tag">/></span>
<span class="hl-tag"><bean</span> <span class="hl-attribute">id</span>=<span class="hl-value">"pathMatcher"</span> <span class="hl-attribute">class</span>=<span class="hl-value">"org.example.app.MyPathMatcher"</span><span class="hl-tag">/></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <h3 class="title">18.16.12消息转换器</h3> 
    </div> 
    <p>如果要替换由Spring MVC创建的默认转换器,或者 如果您只想自定义它们或将额外的转换器添加到默认转换器,<code class="literal">HttpMessageConverter</code>则可以通过重写Java配置来实现 定制。<code class="literal">configureMessageConverters()</code><code class="literal">extendMessageConverters()</code></p> 
    <p>以下是使用自定义<code class="literal">ObjectMapper</code>而不是默认值添加Jackson JSON和XML转换器的示例 :</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration</span>
<span class="hl-annotation">@EnableWebMvc</span></em>
<span class="hl-keyword">public</span> <span class="hl-keyword">class</span> WebConfiguration <span class="hl-keyword">extends</span> WebMvcConfigurerAdapter {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword">public</span> <span class="hl-keyword">void</span> configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		Jackson2ObjectMapperBuilder builder = <span class="hl-keyword">new</span> Jackson2ObjectMapperBuilder()
				.indentOutput(true)
				.dateFormat(<span class="hl-keyword">new</span> SimpleDateFormat(<span class="hl-string">"yyyy-MM-dd"</span>))
				.modulesToInstall(<span class="hl-keyword">new</span> ParameterNamesModule());
		converters.add(<span class="hl-keyword">new</span> MappingJackson2HttpMessageConverter(builder.build()));
		converters.add(<span class="hl-keyword">new</span> MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
	}在此示例中,<code class="literal">Jackson2ObjectMapperBuilder</code>用于为两者创建通用配置,<code class="literal">MappingJackson2HttpMessageConverter</code>并<code class="literal">MappingJackson2XmlHttpMessageConverter</code>启用缩进,自定义日期格式以及 jackson-module-parameter-name的注册 ,以增加对访问参数名称的支持(Java 8中添加的功能)。<code class="literal">ngJackson2XmlHttpMessageConverter</code><span style="font-family:Georgia,'Times New Roman','Bitstream Charter',Times,serif"> with indentation enabled, a customized date format and the registration of </span>jackson-module-parameter-names<span style="font-family:Georgia,'Times New Roman','Bitstream Charter',Times,serif"> that adds support for accessing parameter names (feature added in Java 8).</span>
</pre> 
    <div class="note"> 
     <table summary="Note" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">使用Jackson XML支持启用缩进功能需要 <code class="literal">woodstox-core-asl</code> 除依赖之外<code class="literal">jackson-dataformat-xml</code>。</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
    <p>其他有趣的杰克逊模块可用:</p> 
    <div class="orderedlist"> 
     <ol class="orderedlist" type="1"> 
      <li class="listitem">jackson-datatype-money:支持<code class="literal">javax.money</code>类型(非官方模块)</li> 
      <li class="listitem">jackson-datatype-hibernate:支持Hibernate的特定类型和属性(包括延迟加载方面)</li> 
     </ol> 
    </div> 
    <p>也可以在XML中执行相同的操作:</p> 
    <pre class="programlisting"><span class="hl-tag"><mvc:annotation-driven> </span>
    <span class="hl-tag"><mvc:message-converters> </span>
        <span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.http.converter.json.MappingJackson2HttpMessageConverter” </span><span class="hl-tag">> </span>
            <span class="hl-tag"><property </span> <span class="hl-attribute">name</span> = <span class="hl-value">“objectMapper” </span> <span class="hl-attribute">ref</span> = <span class="hl-value">“objectMapper” </span><span class="hl-tag">/> </span>
        <span class="hl-tag"></ bean> </span>
        <span class="hl-tag"><bean </span> <span class="hl-attribute">class</span> = <span class="hl-value">“org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter” </span><span class="hl-tag">> </span>
            <span class="hl-tag"><property </span> <span class="hl-attribute">name</span> = <span class="hl-value">“objectMapper” </span> <span class="hl-attribute">ref</span> = <span class="hl-value">“xmlMapper” </span><span class="hl-tag">/> </span>
        <span class="hl-tag"></ bean> </span>
    <span class="hl-tag"></ mvc:message-converters> </span>
<span class="hl-tag"></ mvc:注解驱动></span>

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

<span class="hl-tag"><bean </span> <span class="hl-attribute">id</span> = <span class="hl-value">“xmlMapper” </span> <span class="hl-attribute">parent</span> = <span class="hl-value">“objectMapper” </span> <span class="hl-attribute">p:createXmlMapper</span> = <span class="hl-value">“true” </span><span class="hl-tag">/></span></pre> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.13使用MVC Java Config进行高级定制</h3> 
      </div> 
     </div> 
    </div> 
    <p>从上述示例可以看出,MVC Java配置和MVC命名空间提供了不需要深入了解为您创建的基础bean的更高级别的构造。相反,它可以帮助您专注于您的应用程序需求。但是,在某些时候,您可能需要更细致的控制,或者您可能只想了解底层配置。</p> 
    <p>更精细控制的第一步是查看为您创建的基础bean。在MVC Java配置中,您可以看到javadoc和其中的<code class="literal">@Bean</code>方法 <code class="literal">WebMvcConfigurationSupport</code>。此类中的配置通过注释自动导入<code class="literal">@EnableWebMvc</code>。其实如果你打开<code class="literal">@EnableWebMvc</code>你可以看到<code class="literal">@Import</code>声明。</p> 
    <p>更精细控制的下一步是定制一个在其中创建的bean之一<code class="literal">WebMvcConfigurationSupport</code>或可能提供您自己的实例的属性。这需要两件事情 – 删除<code class="literal">@EnableWebMvc</code>注释,以防止导入,然后从<code class="literal">DelegatingWebMvcConfiguration</code>一个子类 扩展<code class="literal">WebMvcConfigurationSupport</code>。这是一个例子:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Configuration</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> WebConfig<span class="hl-keyword"> extends</span> DelegatingWebMvcConfiguration {

	<em><span class="hl-annotation">@Override</span></em>
	<span class="hl-keyword"> public </span> <span class="hl-keyword">void</span> addInterceptors(InterceptorRegistry registry){
		<span class="hl-comment"> // ...</span>
	}

	<em><span class="hl-annotation">@Override </span>
	<span class="hl-annotation">@Bean</span></em>
	<span class="hl-keyword"> public</span> RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
		<span class="hl-comment"> //创建或让“超级”创建适配器</span>
		<span class="hl-comment">//然后自定义其属性之一</span>
	}

}</pre> 
    <div class="note"> 
     <table summary="Note" border="0"> 
      <tbody> 
       <tr> 
        <td rowspan="2" valign="top" width="25" align="center"></td> 
       </tr> 
       <tr> 
        <td valign="top" align="left">应用程序应该只有一个配置扩展<code class="literal">DelegatingWebMvcConfiguration</code> 或单个<code class="literal">@EnableWebMvc</code>注释类,因为它们都注册了相同的底层bean。以这种方式修改bean不会阻止您使用本节前面显示的任何更高级别的构造。<code class="literal">WebMvcConfigurerAdapter</code>子类和 <code class="literal">WebMvcConfigurer</code>实现仍在使用中。</td> 
       </tr> 
      </tbody> 
     </table> 
    </div> 
   </div> 
   <div class="section"> 
    <div class="titlepage"> 
     <div> 
      <div> 
       <h3 class="title">18.16.14使用MVC命名空间进行高级自定义</h3> 
      </div> 
     </div> 
    </div> 
    <p>对您创建的配置的细粒度控制对于MVC命名空间来说有点困难。</p> 
    <p>如果您确实需要这样做,而不是复制其提供的配置,请考虑配置一个<code class="literal">BeanPostProcessor</code>检测要按类型自定义的bean,然后根据需要修改其属性。例如:</p> 
    <pre class="programlisting"><em><span class="hl-annotation">@Component</span></em>
<span class="hl-keyword"> public </span> <span class="hl-keyword">class</span> MyPostProcessor<span class="hl-keyword">实现</span> BeanPostProcessor {

	<span class="hl-keyword">public</span> Object postProcessBeforeInitialization(Object bean,String name)<span class="hl-keyword">throws</span> BeansException {
		 <span class="hl-keyword">if</span>(Bean <span class="hl-keyword">instanceof</span> RequestMappingHandlerAdapter){
			 <span class="hl-comment">//修改适配器的属性</span>
		}
	}

}</pre> 
    <p>请注意,<code class="literal">MyPostProcessor</code>需要将其包含在其中<code class="literal"><component scan/></code>以便检测到它,或者如果您喜欢,可以使用XML bean声明明确声明它。</p> 
   </div> 
  </div> 
  <div style="margin-top:15px; font-style:italic"> 
   <p><strong>原创文章,转载请注明:</strong> 转载自并发编程网 – ifeve.com<strong>本文链接地址:</strong> 《Spring 5 官方文档》18. Web MVC 框架</p> 
  </div> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1281099046785662976"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(spring5)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1831763855328505856.htm"
                           title="SpringCloud-Gateway介绍和工作流程(Day8)" target="_blank">SpringCloud-Gateway介绍和工作流程(Day8)</a>
                        <span class="text-muted">web18484626332</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>GateWay简介Gateway是在Spring生态系统之上构建的API网关服务,基于Spring5,SpringBoot2和ProjectReactor等技术。SpringCloudGateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。SpringCloudGateway是SpringCloud的一个全新项目,基于Spring5.0</div>
                    </li>
                    <li><a href="/article/1831200055613288448.htm"
                           title="一、Spring Cloud Gateway-引用介绍" target="_blank">一、Spring Cloud Gateway-引用介绍</a>
                        <span class="text-muted">侯文_ad82</span>

                        <div>2.2.0.BUILD-SNAPSHOT这个项目提供了构建在Spring生态系统之上API网关,包括Spring5,SpringBoot2和Reator项目。SpringCloudGateway目标是用一个简单、有效的方式路由到API,并且提供横切的一些关注点,例如:安全、监控、系统性能和弹性等。如何引用SpringCloudGatewaymaven坐标为:org.springframework.</div>
                    </li>
                    <li><a href="/article/1829827348011249664.htm"
                           title="springboot2.x到spring3.x的一些变化和示例说明" target="_blank">springboot2.x到spring3.x的一些变化和示例说明</a>
                        <span class="text-muted">闫小甲</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cloud/1.htm">cloud</a>
                        <div>最近在做微服务版本升级工作,整理springboot2.x到spring3.x(spring5.x到spring6.x)的一些变化和示例说明。1.Java版本要求SpringBoot2.5.0:支持Java8及以上版本。SpringBoot3.3.0:要求Java17及以上版本,并且支持Java21。这意味着在升级过程中,首先需要确保JDK版本符合要求,并可能需要进行JDK的升级。2.底层依赖更新</div>
                    </li>
                    <li><a href="/article/1828910615129845760.htm"
                           title="Spring Cloud全解析:网关之GateWay简介" target="_blank">Spring Cloud全解析:网关之GateWay简介</a>
                        <span class="text-muted">拾光师</span>
<a class="tag" taget="_blank" href="/search/springcloud/1.htm">springcloud</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>GateWay简介由于zuul升级为zuul2时,netflix公司内部出现了分歧,所以springcloud自己研发了一套网关gateway,提供一种简单有效的方式来对API进行路由,以及提供一些强大的过滤器功能,如:熔断、限流、重试等,基于WebFlux框架实现的,底层使用了高性能的Reactor模式通信框架Netty特性基于spring5和springboot2构建动态路由:能够匹配任何请求</div>
                    </li>
                    <li><a href="/article/1827835317315530752.htm"
                           title="深入解析 Linux 网络管理:带宽查询、防火墙配置与端口管理" target="_blank">深入解析 Linux 网络管理:带宽查询、防火墙配置与端口管理</a>
                        <span class="text-muted">码农阿豪@新空间代码工作室</span>
<a class="tag" taget="_blank" href="/search/%E5%8C%85%E7%BD%97%E4%B8%87%E8%B1%A1/1.htm">包罗万象</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a>
                        <div>个人名片作者简介:java领域优质创作者个人主页:码农阿豪工作室:新空间代码工作室(提供各种软件服务)个人邮箱:[2435024119@qq.com]个人微信:15279484656个人导航网站:www.forff.top座右铭:总有人要赢。为什么不能是我呢?专栏导航:码农阿豪系列专栏导航面试专栏:收集了java相关高频面试题,面试实战总结️Spring5系列专栏:整理了Spring5重要知识点与</div>
                    </li>
                    <li><a href="/article/1759827417511325696.htm"
                           title="Spring6学习技术|简要介绍+安装环境+入门案例+log4j2日志" target="_blank">Spring6学习技术|简要介绍+安装环境+入门案例+log4j2日志</a>
                        <span class="text-muted">半夜下雨</span>
<a class="tag" taget="_blank" href="/search/Java%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0/1.htm">Java技术学习</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                        <div>学习材料尚硅谷Spring零基础入门到进阶,一套搞定spring6全套视频教程(源码级讲解)碎碎念一下吧,javaWeb跟完了全程。还是感觉啥也不知道,啥也没学会。2025年春天能找到实习吗?真的好担心。环境安装纠结跟spring5还是spring6,思索一下,还是跟spring6吧。原因是spring5的教程里面我找不到视频里的spring安装路径。spring6要求的环境如下:(1)IDEA开</div>
                    </li>
                    <li><a href="/article/1757882079552487424.htm"
                           title="珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册" target="_blank">珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册</a>
                        <span class="text-muted">Java架构</span>

                        <div>一、源码阅读常用设计模式Spring5源码Mybatis源码二、分布式架构总纲珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册消息通信分布式缓存珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册后台服务高性能框架珍藏收集,献出这份年薪50W的阿里Java高级开发内训手册分布式应用场景解决方案三、微服务架构纲要SpringB</div>
                    </li>
                    <li><a href="/article/1757858780403482624.htm"
                           title="06-采用注解开发bean" target="_blank">06-采用注解开发bean</a>
                        <span class="text-muted">干净_79db</span>

                        <div>对于javabean的定义和依赖配置,使用xml文件真心是不方便。到Spring5.0,已经大量使用,注解的使用可以省却大量的xml文件常用注解:1、自动注入:@Resources,@Autowired2、Bean定义:@Component、@Repository、@Service和@Constroller@Component是个泛化概念,可以用在任何层次。如果是web开发,尽量用@Reposit</div>
                    </li>
                    <li><a href="/article/1757816875401625600.htm"
                           title="Gateway服务网关" target="_blank">Gateway服务网关</a>
                        <span class="text-muted">钢铁小宝宝</span>
<a class="tag" taget="_blank" href="/search/gateway/1.htm">gateway</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cloud/1.htm">cloud</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                        <div>SpringCloudGateway是SpringCloud的一个全新项目,该项目是基于Spring5.0,SpringBoot2.0和ProjectReactor等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。3.1.为什么需要网关Gateway网关是我们服务的守门神,所有微服务的统一入口。网关的核心功能特性:将用户请求路由到微服务、并实现负载均</div>
                    </li>
                    <li><a href="/article/1757762446174470144.htm"
                           title="Webflux 函数式编程web框架" target="_blank">Webflux 函数式编程web框架</a>
                        <span class="text-muted">janlle</span>

                        <div>Spring5.0Spring-webflux是一个全新的非堵塞的函数式ReactiveWeb框架,可以用来构建异步的、非堵塞的、事件驱动的服务。springboot2.0发布不久,最近研究了一下springboot2.0的新特性,其中就发现了webflux。下面是spring-flux的一个demo话不多少上代码使用webflux和MVC的区别就是在artifacId后面加上fluxorg.sp</div>
                    </li>
                    <li><a href="/article/1757517092724162560.htm"
                           title="Gateway微服务网关" target="_blank">Gateway微服务网关</a>
                        <span class="text-muted">杀了小惠</span>
<a class="tag" taget="_blank" href="/search/gateway/1.htm">gateway</a><a class="tag" taget="_blank" href="/search/%E5%BE%AE%E6%9C%8D%E5%8A%A1/1.htm">微服务</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a>
                        <div>SpringCloudGatewaySpringCloudGateway是SpringCloud生态系统中的网关,它是基于Spring5.0、SpringBoot2.0和ProjectReactor等技术开发的,旨在为微服务架构提供一种简单有效的、统一的API路由管理方式,并为微服务架构提供安全、监控、指标和弹性等功能。其目标是替代ZuulGateway网关的核心功能请求路由权限控制限流权限控制:</div>
                    </li>
                    <li><a href="/article/1757386716202680320.htm"
                           title="手撕Spring5框架(三)IOC操作Bean管理(基于XML)" target="_blank">手撕Spring5框架(三)IOC操作Bean管理(基于XML)</a>
                        <span class="text-muted">不善^</span>
<a class="tag" taget="_blank" href="/search/%E6%89%8B%E6%92%95Spring5%E6%A1%86%E6%9E%B6/1.htm">手撕Spring5框架</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a>
                        <div>什么是Bean管理?通常是指依据Spring进行的两个操作:1)Spring创建对象2)Spring注入属性Bean管理的两种实现方式:基于XML方式基于注解方式IOC操作Bean管理(基于XML)通过Spring去管理Bean我们下面具体讲解实现管理的两种方式:基于xml方式创建对象在之前入门案例章节我们已经初步认识了Spring基于xml方式创建对象。基本描述:1)在Spring配置文件中,使</div>
                    </li>
                    <li><a href="/article/1757246478885076992.htm"
                           title="撸了35天,奉上最强干货“全家桶”:Spring+SpringCloud+SpringSecurity+SpringBoot+SpringMVC+Spring5!" target="_blank">撸了35天,奉上最强干货“全家桶”:Spring+SpringCloud+SpringSecurity+SpringBoot+SpringMVC+Spring5!</a>
                        <span class="text-muted">废柴程序员</span>

                        <div>撸了35天,奉上最强“全家桶”脑图+面试+进阶学习:Spring+Cloud+Security+Boot+MVC+Spring5,且全篇分为以下三个部分:脑图篇面试篇进阶学习篇阅读前请注意:这是迄今最全的Spring相关全家桶,脑图+面试+进阶学习,全文篇幅有点长,但干货满满,请仔细阅读!且提供全部手绘脑图、面试解析、进阶学习的笔记PDF等☛原件第一篇:脑图篇1.1手绘Spring架构脑图imag</div>
                    </li>
                    <li><a href="/article/1756651871457525760.htm"
                           title="Springboot2快速集成" target="_blank">Springboot2快速集成</a>
                        <span class="text-muted">码道功臣</span>

                        <div>通过一个实例快速搭建SSM。通过实例来看看各个环节的最佳实践。如何使用业务异常。如何设计一个对前端友好的接口。通过单元测试使你的工作更加轻松和安全。简单聊聊代码规范。利用CI/CD来帮助你处理繁杂的重复性工作。源码地址https://github.com/bestaone/Mybatis4SpringbootSpring5、springboot2近况spring5最大的亮点是Springwebfl</div>
                    </li>
                    <li><a href="/article/1756556833813708800.htm"
                           title="Spring5新特性" target="_blank">Spring5新特性</a>
                        <span class="text-muted">不减30斤不改名_TC</span>

                        <div>Spring5新特性简述Spring5兼容Java8和JDK9,集成了反应式流,以方便后续提供一种颠覆性方法来实现端点和Web应用程序开发。反应式编程不仅是此版本的主题,还是令许多程序员激动不已的重大特性。人们对能够针对负载波动进行无缝扩展的容灾和响应式服务的需求在不断增加,Spring5很好地满足了这一需求。本文将介绍JavaSE8和JavaEE7API升级的基本内容、Spring5的新反应式编</div>
                    </li>
                    <li><a href="/article/1756188572538388480.htm"
                           title="[AIGC] Spring Gateway:一个简单 yet powerful API 网关" target="_blank">[AIGC] Spring Gateway:一个简单 yet powerful API 网关</a>
                        <span class="text-muted">程序员三木</span>
<a class="tag" taget="_blank" href="/search/AI/1.htm">AI</a><a class="tag" taget="_blank" href="/search/AIGC/1.htm">AIGC</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/gateway/1.htm">gateway</a>
                        <div>SpringGateway(SpringCloudGateway)是Spring基金会下的一个开源项目,它是一个基于Spring5、ProjectReactor、SpringBoot2和SpringWebFlux的简单yetpowerfulAPI网关。SpringGateway可以用来管理and控制API的生命周期and流量,并提供多种插件and特性。SpringGateway已经成为当今最受欢迎</div>
                    </li>
                    <li><a href="/article/1756069263874998272.htm"
                           title="CVE-2022-22947 Spring Cloud Gateway RCE漏洞复现" target="_blank">CVE-2022-22947 Spring Cloud Gateway RCE漏洞复现</a>
                        <span class="text-muted">才俊同学</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cloud/1.htm">cloud</a><a class="tag" taget="_blank" href="/search/gateway/1.htm">gateway</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                        <div>SpringCloudGateway简介Gateway是在Spring生态系统之上构建的API网关服务,基于Spring5,SpringBoot2和ProjectReactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。在Spring应用里面启用Gateway服务只需要在pom文件里面引入关于网关的依赖:org.sp</div>
                    </li>
                    <li><a href="/article/1755142922052845568.htm"
                           title="Spring MVC 请求执行流程的源码深度解析【两万字】" target="_blank">Spring MVC 请求执行流程的源码深度解析【两万字】</a>
                        <span class="text-muted">刘Java</span>
<a class="tag" taget="_blank" href="/search/Spring/1.htm">Spring</a><a class="tag" taget="_blank" href="/search/MVC/1.htm">MVC</a><a class="tag" taget="_blank" href="/search/5.x/1.htm">5.x</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81/1.htm">源码</a><a class="tag" taget="_blank" href="/search/%E6%96%B0%E6%98%9F%E8%AE%A1%E5%88%92/1.htm">新星计划</a><a class="tag" taget="_blank" href="/search/springmvc%E8%AF%B7%E6%B1%82%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B/1.htm">springmvc请求执行流程</a><a class="tag" taget="_blank" href="/search/springmvc%E6%BA%90%E7%A0%81/1.htm">springmvc源码</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>  基于最新Spring5.x,详细介绍了SpringMVC请求的执行流程源码,给出了更加详细的SpringMVC请求执行流程步骤总结,以及详细的执行流程图。  我正在参与CSDN《新程序员》有奖征文,活动地址:https://marketing.csdn.net/p/52c37904f6e1b69dc392234fff425442。  此前,我们学习了SpringMVC项目启动初始化过程中的部分</div>
                    </li>
                    <li><a href="/article/1754545937138597888.htm"
                           title="【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析" target="_blank">【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析</a>
                        <span class="text-muted">洛神灬殇</span>

                        <div>SpringCloudGateway简介SpringCloudGateway是SpringCloud体系的第二代网关组件,基于Spring5.0的新特性WebFlux进行开发,底层网络通信框架使用的是Netty,所以其吞吐量高、性能强劲,未来将会取代第一代的网关组件Zuul。SpringCloudGateway可以通过服务发现组件自动转发请求,默认集成了Ribbon做负载均衡,以及默认使用Hyst</div>
                    </li>
                    <li><a href="/article/1754090390652207104.htm"
                           title="【Spring】Spring 概述" target="_blank">【Spring】Spring 概述</a>
                        <span class="text-muted">QX_Java_Learner</span>
<a class="tag" taget="_blank" href="/search/Spring/1.htm">Spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                        <div>一、Spring八大模块注意:Spring5版本之后是8个模块,在Spring5中新增了WebFlux模块1、SpringCore模块这是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理核心容器的主要组件是BeanFactory,BeanFactory是工厂模式的一个实现,是任何Spring应用的核心它使用IoC将应用配置和依赖</div>
                    </li>
                    <li><a href="/article/1753839836642426880.htm"
                           title="Activiti6.0 java项目框架 spring5 SSM 工作流引擎 审批流程" target="_blank">Activiti6.0 java项目框架 spring5 SSM 工作流引擎 审批流程</a>
                        <span class="text-muted">3ce4724dfda0</span>

                        <div>工作流模块----------------------------------------------------------------------------------------------------------1.模型管理:web在线流程设计器、预览流程xml、导出xml、部署流程2.流程管理:导入导出流程资源文件、查看流程图、根据流程实例反射出流程模型、激活挂起3.运行中流程:查看流</div>
                    </li>
                    <li><a href="/article/1753718327580049408.htm"
                           title="学习笔记(狂神Spring5 P1-P13)" target="_blank">学习笔记(狂神Spring5 P1-P13)</a>
                        <span class="text-muted">广而不精zhu小白</span>
<a class="tag" taget="_blank" href="/search/Spring/1.htm">Spring</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/java-ee/1.htm">java-ee</a>
                        <div>学习笔记源码下载地址1、IocSpring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。官网:http://spring.io/Spring框架是一个分层架构,由7个定义良好的模块组成。(核心容器,Spring上下文,SpringAOP,SpringDAO,SpringORM,SpringWeb模块,SpringMVC框架)Spring模块构建在核心容器之上,核心容器定义了创</div>
                    </li>
                    <li><a href="/article/1753683665579163648.htm"
                           title="Spring5框架基础详解(四) (AOP概念、AOP术语、AOP底层原理、JDK动态代理实现、AOP操作)" target="_blank">Spring5框架基础详解(四) (AOP概念、AOP术语、AOP底层原理、JDK动态代理实现、AOP操作)</a>
                        <span class="text-muted">大黄烽</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/1.htm">代理模式</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a>
                        <div>文章目录一、什么是AOP二、AOP术语三、AOP底层原理四、AOP(JDK动态代理实现)五、AOP操作5.1AOP操作(基于AspectJ注解方式)5.2完全注解开发一、什么是AOP面向切面编程,不通过修改源代码方式,在主干功能里面添加新功能。二、AOP术语1.连接点:类里面哪些方法可以被增强,这些方法称为连接点2.切入点:实际被真正增强的方法,称为切入点3.通知(增强):(1)实际增强的逻辑部分</div>
                    </li>
                    <li><a href="/article/1753683150250196992.htm"
                           title="Spring5学习笔记------3、AOP" target="_blank">Spring5学习笔记------3、AOP</a>
                        <span class="text-muted">lyy_sss</span>
<a class="tag" taget="_blank" href="/search/Java_Spring/1.htm">Java_Spring</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/1.htm">代理模式</a>
                        <div>AOP基本概念及原理AOP(AspectOrientedProgramming):面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗描述就是:不通过修改变源代码的方式,在主干功能里面添加新功能AOP底层原理AOP底层使用动态代理方式(1)有</div>
                    </li>
                    <li><a href="/article/1753683148962545664.htm"
                           title="Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))" target="_blank">Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))</a>
                        <span class="text-muted">码农阿豪</span>
<a class="tag" taget="_blank" href="/search/Spring5%E7%B3%BB%E5%88%97/1.htm">Spring5系列</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/aop/1.htm">aop</a><a class="tag" taget="_blank" href="/search/spring5/1.htm">spring5</a>
                        <div>目录AOP概念AOP底层原理AOP(JDK动态代理)使用JDK动态代理,使用Proxy类里面的方法创建代理对象**编写****JDK**动态代理代码AOP(术语)AOP操作(准备工作)**AOP****操作(**AspectJ注解)**AOP****操作(**AspectJ**配置文件)**开篇:欢迎再次来到Spring5学习系列!在这个博客中,我们将深入研究Spring框架的AOP概念+原理+动</div>
                    </li>
                    <li><a href="/article/1753270219360452608.htm"
                           title="Spring面试" target="_blank">Spring面试</a>
                        <span class="text-muted">最小的帆也能远航</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/sqlserver/1.htm">sqlserver</a>
                        <div>1.Spring是什么?特性?有哪些模块?SpringLogo一句话概括:Spring是一个轻量级、非入侵式的控制反转(IoC)和面向切面(AOP)的框架。2003年,一个音乐家RodJohnson决定发展一个轻量级的Java开发框架,Spring作为Java战场的龙骑兵渐渐崛起,并淘汰了EJB这个传统的重装骑兵。Spring重要版本到了现在,企业级开发的标配基本就是Spring5+SpringB</div>
                    </li>
                    <li><a href="/article/1753257838886535168.htm"
                           title="spring5.3.x源码:Could not resolve: com.ibm.websphere:uow:6.0.2.17" target="_blank">spring5.3.x源码:Could not resolve: com.ibm.websphere:uow:6.0.2.17</a>
                        <span class="text-muted">泗水长流</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/gradle/1.htm">gradle</a>
                        <div>在搭建spring5.3.x的源码时,会报Couldnotresolve:com.ibm.websphere:uow:6.0.2.17的异常。找了一下,发现是下载不下来。网有人说需要将仓库地址换成https://repo.spring.io/libs-release,但是换了之后,直接报401,没有权限;还有人说是将仓库地址换成阿里云的https://maven.aliyun.com/reposi</div>
                    </li>
                    <li><a href="/article/1753257455376154624.htm"
                           title="idea搭建spring5.3.x源码环境" target="_blank">idea搭建spring5.3.x源码环境</a>
                        <span class="text-muted">泗水长流</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/spring5.3.x%E6%BA%90%E7%A0%81/1.htm">spring5.3.x源码</a><a class="tag" taget="_blank" href="/search/gradle%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE/1.htm">gradle安装配置</a><a class="tag" taget="_blank" href="/search/spring5.3.x%E6%BA%90%E7%A0%81%E7%8E%AF%E5%A2%83/1.htm">spring5.3.x源码环境</a><a class="tag" taget="_blank" href="/search/spring%E6%BA%90%E7%A0%81%E6%90%AD%E5%BB%BA/1.htm">spring源码搭建</a>
                        <div>1.写在前面的话碰到了不少想阅读或者学习spring源码的同学,但是第一步搭建这个源码阅读环境就能难倒了一大批人。下面我就以spring5.3.x这个源码分支,来具体演示一下搭建过程。2.下载源码下载源码这一步,说实话,由于某些原因,会导致我们用git做clone的时候,老是中断,clone不下来。所以我们可以先把github的源码,复制到gitee上,然后从gitee上来下载。打开spring的</div>
                    </li>
                    <li><a href="/article/1752606623743885312.htm"
                           title="spring5.0以上解决Log4jConfigListener不可用问题" target="_blank">spring5.0以上解决Log4jConfigListener不可用问题</a>
                        <span class="text-muted">↘"LYong</span>
<a class="tag" taget="_blank" href="/search/jar/1.htm">jar</a><a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                        <div>升级到spring5.0,发现之前web.xml里配置的log监听Log4jConfigListener被spring废弃了,spring建议用log4j2来替换这个是web.xml之前的配置**(移除)**:log4jConfigLocationclasspath:log4j.xmlorg.springframework.web.util.Log4jConfigListener移除旧的Jar包依</div>
                    </li>
                    <li><a href="/article/1751864295039188992.htm"
                           title="springBoot集成mybatis+mysql项目搭建" target="_blank">springBoot集成mybatis+mysql项目搭建</a>
                        <span class="text-muted">FromNowOnUntilTheEnd</span>
<a class="tag" taget="_blank" href="/search/SpringBoot/1.htm">SpringBoot</a>
                        <div>大家都知道,现在Spring框架几乎无处不用,目前最新版本据说是Spring5,现在常用的基本还是Spring4.X,很多公司甚至用的还是Spring3.x,而Spring领域其中最好用的莫过于SpringBoot,这是从SpringMVC衍变出来的,本质还是SpringMVC,但是要比SpringMVC好用的多,因为很好用,所以笔者闲暇之际用SpringBoot从头至尾摸索着搭建了一个案例,分享</div>
                    </li>
                                <li><a href="/article/80.htm"
                                       title="java杨辉三角" target="_blank">java杨辉三角</a>
                                    <span class="text-muted">3213213333332132</span>
<a class="tag" taget="_blank" href="/search/java%E5%9F%BA%E7%A1%80/1.htm">java基础</a>
                                    <div>
package com.algorithm;

/**
 * @Description 杨辉三角
 * @author FuJianyong
 * 2015-1-22上午10:10:59
 */
public class YangHui {
	public static void main(String[] args) {
		//初始化二维数组长度
		int[][] y</div>
                                </li>
                                <li><a href="/article/207.htm"
                                       title="《大话重构》之大布局的辛酸历史" target="_blank">《大话重构》之大布局的辛酸历史</a>
                                    <span class="text-muted">白糖_</span>
<a class="tag" taget="_blank" href="/search/%E9%87%8D%E6%9E%84/1.htm">重构</a>
                                    <div>《大话重构》中提到“大布局你伤不起”,如果企图重构一个陈旧的大型系统是有非常大的风险,重构不是想象中那么简单。我目前所在公司正好对产品做了一次“大布局重构”,下面我就分享这个“大布局”项目经验给大家。 
  
 
 背景 
 
        公司专注于企业级管理产品软件,企业有大中小之分,在2000年初公司用JSP/Servlet开发了一套针对中</div>
                                </li>
                                <li><a href="/article/334.htm"
                                       title="电驴链接在线视频播放源码" target="_blank">电驴链接在线视频播放源码</a>
                                    <span class="text-muted">dubinwei</span>
<a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81/1.htm">源码</a><a class="tag" taget="_blank" href="/search/%E7%94%B5%E9%A9%B4/1.htm">电驴</a><a class="tag" taget="_blank" href="/search/%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">播放器</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91/1.htm">视频</a><a class="tag" taget="_blank" href="/search/ed2k/1.htm">ed2k</a>
                                    <div>本项目是个搜索电驴(ed2k)链接的应用,借助于磁力视频播放器(官网: 
http://loveandroid.duapp.com/ 开放平台),可以实现在线播放视频,也可以用迅雷或者其他下载工具下载。 
项目源码: 
http://git.oschina.net/svo/Emule,动态更新。也可从附件中下载。 
项目源码依赖于两个库项目,库项目一链接: 
http://git.oschina.</div>
                                </li>
                                <li><a href="/article/461.htm"
                                       title="Javascript中函数的toString()方法" target="_blank">Javascript中函数的toString()方法</a>
                                    <span class="text-muted">周凡杨</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/toString/1.htm">toString</a><a class="tag" taget="_blank" href="/search/function/1.htm">function</a><a class="tag" taget="_blank" href="/search/object/1.htm">object</a>
                                    <div>简述 
    The toString() method returns a string representing the source code of the function. 
    简译之,Javascript的toString()方法返回一个代表函数源代码的字符串。 
句法 
    function.</div>
                                </li>
                                <li><a href="/article/588.htm"
                                       title="struts处理自定义异常" target="_blank">struts处理自定义异常</a>
                                    <span class="text-muted">g21121</span>
<a class="tag" taget="_blank" href="/search/struts/1.htm">struts</a>
                                    <div>很多时候我们会用到自定义异常来表示特定的错误情况,自定义异常比较简单,只要分清是运行时异常还是非运行时异常即可,运行时异常不需要捕获,继承自RuntimeException,是由容器自己抛出,例如空指针异常。 
非运行时异常继承自Exception,在抛出后需要捕获,例如文件未找到异常。 
此处我们用的是非运行时异常,首先定义一个异常LoginException: 
/**
 * 类描述:登录相</div>
                                </li>
                                <li><a href="/article/715.htm"
                                       title="Linux中find常见用法示例" target="_blank">Linux中find常见用法示例</a>
                                    <span class="text-muted">510888780</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                                    <div>Linux中find常见用法示例 
 
·find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \; 
 
 
 
 
find命令的参数;</div>
                                </li>
                                <li><a href="/article/842.htm"
                                       title="SpringMVC的各种参数绑定方式" target="_blank">SpringMVC的各种参数绑定方式</a>
                                    <span class="text-muted">Harry642</span>
<a class="tag" taget="_blank" href="/search/springMVC/1.htm">springMVC</a><a class="tag" taget="_blank" href="/search/%E7%BB%91%E5%AE%9A/1.htm">绑定</a><a class="tag" taget="_blank" href="/search/%E8%A1%A8%E5%8D%95/1.htm">表单</a>
                                    <div>1. 基本数据类型(以int为例,其他类似): 
Controller代码: 
 

    @RequestMapping("saysth.do")
    public void test(int count) {
    }
 
表单代码: 
 

<form action="saysth.do" method="post&q</div>
                                </li>
                                <li><a href="/article/969.htm"
                                       title="Java 获取Oracle ROWID" target="_blank">Java 获取Oracle ROWID</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a>
                                    <div>  
A ROWID is an identification tag unique for each row of an Oracle Database table. The ROWID can be thought of as a virtual column, containing the ID for each row. 
The oracle.sql.ROWID class i</div>
                                </li>
                                <li><a href="/article/1096.htm"
                                       title="java获取方法的参数名" target="_blank">java获取方法的参数名</a>
                                    <span class="text-muted">antlove</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a><a class="tag" taget="_blank" href="/search/parameter/1.htm">parameter</a><a class="tag" taget="_blank" href="/search/method/1.htm">method</a><a class="tag" taget="_blank" href="/search/reflect/1.htm">reflect</a>
                                    <div>reflect.ClassInformationUtil.java 
package reflect;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAtt</div>
                                </li>
                                <li><a href="/article/1223.htm"
                                       title="JAVA正则表达式匹配 查找 替换 提取操作" target="_blank">JAVA正则表达式匹配 查找 替换 提取操作</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a><a class="tag" taget="_blank" href="/search/%E6%9B%BF%E6%8D%A2/1.htm">替换</a><a class="tag" taget="_blank" href="/search/%E6%8F%90%E5%8F%96/1.htm">提取</a><a class="tag" taget="_blank" href="/search/%E6%9F%A5%E6%89%BE/1.htm">查找</a>
                                    <div>正则表达式的查找;主要是用到String类中的split(); 
      String str; 
     str.split();方法中传入按照什么规则截取,返回一个String数组 
  
常见的截取规则: 
str.split("\\.")按照.来截取

str.</div>
                                </li>
                                <li><a href="/article/1350.htm"
                                       title="Java中equals()与hashCode()方法详解" target="_blank">Java中equals()与hashCode()方法详解</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/set/1.htm">set</a><a class="tag" taget="_blank" href="/search/equals%28%29/1.htm">equals()</a><a class="tag" taget="_blank" href="/search/hashCode%28%29/1.htm">hashCode()</a>
                                    <div>一.equals()方法详解 
    equals()方法在object类中定义如下:  
public boolean equals(Object obj) {
    return (this == obj);
}
 
   很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们知道,String 、Math、I</div>
                                </li>
                                <li><a href="/article/1477.htm"
                                       title="精通Oracle10编程SQL(4)使用SQL语句" target="_blank">精通Oracle10编程SQL(4)使用SQL语句</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/plsql/1.htm">plsql</a>
                                    <div>--工资级别表
create table SALGRADE
(
  GRADE    NUMBER(10),
  LOSAL    NUMBER(10,2),
  HISAL    NUMBER(10,2)
)

insert into SALGRADE values(1,0,100);
insert into SALGRADE values(2,100,200);
inser</div>
                                </li>
                                <li><a href="/article/1604.htm"
                                       title="【Nginx二】Nginx作为静态文件HTTP服务器" target="_blank">【Nginx二】Nginx作为静态文件HTTP服务器</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/HTTP%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">HTTP服务器</a>
                                    <div> Nginx作为静态文件HTTP服务器 
 
  在本地系统中创建/data/www目录,存放html文件(包括index.html) 
 创建/data/images目录,存放imags图片 
 在主配置文件中添加http指令 
 
  
http {
    server {
        listen       80;
        server_name  </div>
                                </li>
                                <li><a href="/article/1731.htm"
                                       title="kafka获得最新partition offset" target="_blank">kafka获得最新partition offset</a>
                                    <span class="text-muted">blackproof</span>
<a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a><a class="tag" taget="_blank" href="/search/partition/1.htm">partition</a><a class="tag" taget="_blank" href="/search/offset/1.htm">offset</a><a class="tag" taget="_blank" href="/search/%E6%9C%80%E6%96%B0/1.htm">最新</a>
                                    <div>kafka获得partition下标,需要用到kafka的simpleconsumer 
  
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.</div>
                                </li>
                                <li><a href="/article/1858.htm"
                                       title="centos 7安装docker两种方式" target="_blank">centos 7安装docker两种方式</a>
                                    <span class="text-muted">ronin47</span>

                                    <div>      第一种是采用yum 方式 
             yum install -y docker 
          </div>
                                </li>
                                <li><a href="/article/1985.htm"
                                       title="java-60-在O(1)时间删除链表结点" target="_blank">java-60-在O(1)时间删除链表结点</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>

public class DeleteNode_O1_Time {

	/**
	 * Q 60 在O(1)时间删除链表结点
	 * 给定链表的头指针和一个结点指针(!!),在O(1)时间删除该结点
	 * 
	 * Assume the list is:
	 * head->...->nodeToDelete->mNode->nNode->..</div>
                                </li>
                                <li><a href="/article/2112.htm"
                                       title="nginx利用proxy_cache来缓存文件" target="_blank">nginx利用proxy_cache来缓存文件</a>
                                    <span class="text-muted">cfyme</span>
<a class="tag" taget="_blank" href="/search/cache/1.htm">cache</a>
                                    <div>user  zhangy users;
worker_processes 10;
error_log  /var/vlogs/nginx_error.log  crit;
pid        /var/vlogs/nginx.pid;
#Specifies the value for ma</div>
                                </li>
                                <li><a href="/article/2239.htm"
                                       title="[JWFD开源工作流]JWFD嵌入式语法分析器负号的使用问题" target="_blank">[JWFD开源工作流]JWFD嵌入式语法分析器负号的使用问题</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F/1.htm">嵌入式</a>
                                    <div> 
    假如我们需要用JWFD的语法分析模块定义一个带负号的方程式,直接在方程式之前添加负号是不正确的,而必须这样做: 
 
    string str01 = "a=3.14;b=2.71;c=0;c-((a*a)+(b*b))" 
 
    定义一个0整数c,然后用这个整数c去</div>
                                </li>
                                <li><a href="/article/2366.htm"
                                       title="如何集成支付宝官方文档" target="_blank">如何集成支付宝官方文档</a>
                                    <span class="text-muted">dai_lm</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>官方文档下载地址 
 
https://b.alipay.com/order/productDetail.htm?productId=2012120700377310&tabId=4#ps-tabinfo-hash 
 
集成的必要条件 
1. 需要有自己的Server接收支付宝的消息 
2. 需要先制作app,然后提交支付宝审核,通过后才能集成 
 
调试的时候估计会真的扣款,请注意 
</div>
                                </li>
                                <li><a href="/article/2493.htm"
                                       title="应该在什么时候使用Hadoop" target="_blank">应该在什么时候使用Hadoop</a>
                                    <span class="text-muted">datamachine</span>
<a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a>
                                    <div>原帖地址:http://blog.chinaunix.net/uid-301743-id-3925358.html 
 
存档,某些观点与我不谋而合,过度技术化不可取,且hadoop并非万能。 
 
--------------------------------------------万能的分割线-------------------------------- 
有人问我,“你在大数据和Hado</div>
                                </li>
                                <li><a href="/article/2620.htm"
                                       title="在GridView中对于有外键的字段使用关联模型进行搜索和排序" target="_blank">在GridView中对于有外键的字段使用关联模型进行搜索和排序</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/yii/1.htm">yii</a>
                                    <div>在GridView中使用关联模型进行搜索和排序 
首先我们有两个模型它们直接有关联:   
class Author extends CActiveRecord {
...
}
 
class Post extends CActiveRecord {
...
    function relations() {
        return array(
            '</div>
                                </li>
                                <li><a href="/article/2747.htm"
                                       title="使用NSString 的格式化大全" target="_blank">使用NSString 的格式化大全</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/Objective-C/1.htm">Objective-C</a>
                                    <div>格式定义The format specifiers supported by the NSString formatting methods and CFString formatting functions follow the IEEE printf specification; the specifiers are summarized in Table 1. Note that you c</div>
                                </li>
                                <li><a href="/article/2874.htm"
                                       title="使用activeX插件对象object滚动有重影" target="_blank">使用activeX插件对象object滚动有重影</a>
                                    <span class="text-muted">蕃薯耀</span>
<a class="tag" taget="_blank" href="/search/activeX%E6%8F%92%E4%BB%B6/1.htm">activeX插件</a><a class="tag" taget="_blank" href="/search/%E6%BB%9A%E5%8A%A8%E6%9C%89%E9%87%8D%E5%BD%B1/1.htm">滚动有重影</a>
                                    <div>    
使用activeX插件对象object滚动有重影       <object style="width:0;" id="abc" classid="CLSID:D3E3970F-2927-9680-BBB4-5D0889909DF6" codebase="activex/OAX339.CAB#</div>
                                </li>
                                <li><a href="/article/3001.htm"
                                       title="SpringMVC4零配置" target="_blank">SpringMVC4零配置</a>
                                    <span class="text-muted">hanqunfeng</span>
<a class="tag" taget="_blank" href="/search/springmvc4/1.htm">springmvc4</a>
                                    <div>基于Servlet3.0规范和SpringMVC4注解式配置方式,实现零xml配置,弄了个小demo,供交流讨论。 
  
  
项目说明如下: 
1.db.sql是项目中用到的表,数据库使用的是oracle11g 
2.该项目使用mvn进行管理,私服为自搭建nexus,项目只用到一个第三方 jar,就是oracle的驱动; 
3.默认项目为零配置启动,如果需要更改启动方式,请</div>
                                </li>
                                <li><a href="/article/3128.htm"
                                       title="《开源框架那点事儿16》:缓存相关代码的演变" target="_blank">《开源框架那点事儿16》:缓存相关代码的演变</a>
                                    <span class="text-muted">j2eetop</span>
<a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%BA%90%E6%A1%86%E6%9E%B6/1.htm">开源框架</a>
                                    <div>问题引入 
上次我参与某个大型项目的优化工作,由于系统要求有比较高的TPS,因此就免不了要使用缓冲。 
该项目中用的缓冲比较多,有MemCache,有Redis,有的还需要提供二级缓冲,也就是说应用服务器这层也可以设置一些缓冲。 
当然去看相关实现代代码的时候,大致是下面的样子。    
[java] 
view plain 
copy 
print 
?   
 
 public vo</div>
                                </li>
                                <li><a href="/article/3255.htm"
                                       title="AngularJS浅析" target="_blank">AngularJS浅析</a>
                                    <span class="text-muted">kvhur</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>概念 
 
 AngularJS is a structural framework for dynamic web apps. 
 了解更多详情请见原文链接:http://www.gbtags.com/gb/share/5726.htm 
 Directive 
扩展html,给html添加声明语句,以便实现自己的需求。对于页面中html元素以ng为前缀的属性名称,ng是angular的命名空间</div>
                                </li>
                                <li><a href="/article/3382.htm"
                                       title="架构师之jdk的bug排查(一)---------------split的点号陷阱" target="_blank">架构师之jdk的bug排查(一)---------------split的点号陷阱</a>
                                    <span class="text-muted">nannan408</span>
<a class="tag" taget="_blank" href="/search/split/1.htm">split</a>
                                    <div>1.前言. 
   jdk1.6的lang包的split方法是有bug的,它不能有效识别A.b.c这种类型,导致截取长度始终是0.而对于其他字符,则无此问题.不知道官方有没有修复这个bug. 
2.代码 
 

String[] paths = "object.object2.prop11".split("'");
System.ou</div>
                                </li>
                                <li><a href="/article/3509.htm"
                                       title="如何对10亿数据量级的mongoDB作高效的全表扫描" target="_blank">如何对10亿数据量级的mongoDB作高效的全表扫描</a>
                                    <span class="text-muted">quentinXXZ</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a>
                                    <div>  本文链接: 
http://quentinXXZ.iteye.com/blog/2149440  
一、正常情况下,不应该有这种需求 
首先,大家应该有个概念,标题中的这个问题,在大多情况下是一个伪命题,不应该被提出来。要知道,对于一般较大数据量的数据库,全表查询,这种操作一般情况下是不应该出现的,在做正常查询的时候,如果是范围查询,你至少应该要加上limit。 
说一下,</div>
                                </li>
                                <li><a href="/article/3636.htm"
                                       title="C语言算法之水仙花数" target="_blank">C语言算法之水仙花数</a>
                                    <span class="text-muted">qiufeihu</span>
<a class="tag" taget="_blank" href="/search/c/1.htm">c</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                                    <div>/**
* 水仙花数
*/
#include <stdio.h>
#define N 10
int main()
{
    int x,y,z;
    for(x=1;x<=N;x++)
    	for(y=0;y<=N;y++)
    		for(z=0;z<=N;z++)
    			if(x*100+y*10+z == x*x*x</div>
                                </li>
                                <li><a href="/article/3763.htm"
                                       title="JSP指令" target="_blank">JSP指令</a>
                                    <span class="text-muted">wyzuomumu</span>
<a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a>
                                    <div> 
 jsp指令的一般语法格式: <%@ 指令名 属性 =”值 ” %> 
  常用的三种指令: page,include,taglib 
 page指令语法形式: <%@ page 属性 1=”值 1” 属性 2=”值 2”%> 
 include指令语法形式: <%@include file=”relative url”%> (jsp可以通过 include</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>