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

  1. 云栖社区>
  2. 博客>
  3. 正文

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

青衫无名 2017-05-18 14:57:00 浏览1097
展开阅读全文

自定义WebDataBinder初始化

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

使用@InitBinder自定义数据绑定

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

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

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

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

	
	 

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

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

	
	 void initBinder(WebDataBinder binder) {

配置自定义WebBindingInitializer

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

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

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

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

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

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

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

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

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

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



 



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

杰克逊序列化视图支持

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

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

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

	
	
	 

 

	  

	

	

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

	
	 

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

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

@Controller
 public  class UserController extends AbstractController {

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

杰克逊JSONP支持

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

@ControllerAdvice
 public  class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

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

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

18.3.4异步请求处理

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

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

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

}

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

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

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

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

  • 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处理开始异步时。

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

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

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

HTTP流式传输

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

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

这是一个例子:

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

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

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

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

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

HTTP流与服务器发送的事件

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

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

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

HTTP直接流向OutputStream

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

这是一个例子:

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

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

配置异步请求处理

Servlet容器配置

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

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

	...

</ web的应用>

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

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

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

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

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

</ web的应用>

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

Spring MVC配置

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

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

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

18.3.5测试控制器

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

网友评论

登录后评论
0/500
评论
青衫无名
+ 关注