SpringMVC(5) - 控制器(3) - @RequestMapping(2) - 定义处理器方法

参考:https://docs.spring.io/spring/docs/4.3.20.RELEASE/spring-framework-reference/htmlsingle/#mvc-ann-methods

@RequestMapping处理器方法可以具有非常灵活的签名。  大多数参数可以按任意顺序使用,唯一例外是BindingResult参数。 

注:Spring 3.1为@RequestMapping方法引入了一组新的支持类,分别称为RequestMappingHandlerMapping和RequestMappingHandlerAdapter。 建议使用它们,甚至需要利用Spring MVC 3.1中的新功能并进一步使用。 默认情况下,从MVC命名空间启用新的支持类,并使用MVC Java配置,但如果不使用,则必须明确配置。

1. 支持的方法参数类型

以下是受支持的方法参数:

  • 请求或响应对象(Servlet API)。 选择任何特定的请求或响应类型,例如ServletRequest或HttpServletRequest。
  • 会话对象(Servlet API):类型为HttpSession。 此类型的参数强制存在相应的会话。 因此,这样的参数永远不为空。

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

  • org.springframework.web.context.request.WebRequest或org.springframework.web.context.request.NativeWebRequest:允许通用请求参数访问以及请求/会话属性访问,而不与本地Servlet / Portlet API绑定。
  • java.util.Locale:对于当前请求区域设置,由最可用的区域设置解析器确定,实际上是在MVC环境中配置的LocaleResolver/LocaleContextResolver
  • java.util.TimeZone(Java6+)/java.time.ZoneId(Java8上):与当前请求关联的时区,由LocaleContextResolver确定
  • java.io.InputStream / java.io.Reade:用于访问请求的内容。此值是Servlet API暴露的原始InputStream / Reader。
  • java.io.OutputStream / java.io.Writer:用于生成响应的内容。此值是Servlet API暴露的原始OutputStream / Writer。
  • org.springframework.http.HttpMethod:对于HTTP请求方法
  • java.security.Principal:包含当前经过身份验证的用户
  • @PathVariable:注解参数,用于访问URI模板变量参数
  • @MatrixVariable:注解参数,用于访问位于URI路径段中 name-value键值对参数
  • @RequestParam:注解参数,用于访问特定Servlet请求中参数。参数值将转换为声明的方法参数类型
  • @RequestHeader:注解参数,用于访问特定Servlet请求中HTTP头参数。参数值将转换为声明的方法参数类型。
  • @RequestBody:注解参数,用于访问HTTP请求体参数。使用HttpMessageConverter将参数值转换为声明的方法参数类型 。
  • @RequestPart:注解参数,用于访问“multipart/form-data”请求部分内容的参数。
  • @SessionAttribute:注解参数,用于访问存在的、永久的会话属性(例如用户认证对象)参数,而不是作为通过控制器工作流临时存储在会话中的模型属性@SessionAttributes。
  • @RequestAttribute:注解参数,用于访问请求属性参数。
  • HttpEntity<?>:用于访问Servlet请求HTTP头和内容的参数。请求流将使用HttpMessageConverter转换为实体主体。
  • java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap:获取暴露于web视图隐含模型
  • org.springframework.web.servlet.mvc.support.RedirectAttributes:指定在重定向的情况下要使用的属性集,以及添加flash属性(临时存储在服务器端的属性,以使其在重定向后可用于请求)。
  • 命令或表单对象将请求参数绑定到bean属性(通过setter)或直接绑定到字段,具有可自定义的类型转换,具体取决于@InitBinder方法 或 HandlerAdapter配置。可查看RequestMappingHandlerAdapter的webBindingInitializer属性。默认情况下,此类对象及其验证结果将作为模型属性公开,使用这种方式获取参数:xxxMethod(some.package.OrderAddress orderAddress)。可以使用ModelAttribute注解方法参数去自定义所使用模型的属性名称。 
  • org.springframework.validation.Errors / org.springframework.validation.BindingResult:前面的命令或表单对象的验证结果(紧接在方法参数后面)。
  • org.springframework.web.bind.support.SessionStatus:用于将表单处理标记为完成的状态句柄,用于触发在处理器类型级别上已经被@SessionAttributes注解标识的会话属性的清除。
  • org.springframework.web.util.UriComponentsBuilder:用于准备相对于当前请求的主机、端口、模式、上下文路径和servlet映射的文字部分的URL的构建器。

Errors或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) { ... }

注:支持JDK 1.8的java.util.Optional作为方法参数类型,其注解具有必需属性(例如@RequestParam,@RequestHeader等。在这些情况下使用java.util.Optional等同于具有required=false。

2. 支持的方法返回类型

以下是支持的返回类型:

  • ModelAndView对象:命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地丰富了model。
  • Model对象:其视图名称通过RequestToViewNameTranslator隐式确定, 命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地丰富了model。
  • Map对象:通过Map对象暴露Model,视图名称通过RequestToViewNameTranslator隐式确定,命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地丰富了model。
  • View对象:命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地决定了model 。处理器方法还可以通过声明Model参数以编程方式丰富模型。
  • String值:被解释为逻辑视图名称,命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地决定了model 。处理器方法还可以通过声明Model参数以编程方式丰富模型。
  • void:如果方法处理响应本身(通过直接编写响应内容,为此目的声明类型ServletResponse/HttpServletResponse 的参数)或者是否应该通过RequestToViewNameTranslator(不在处理器方法签名中声明响应参数)隐式确定视图名称 。
  • 如果方法带有@ResponseBody注解,则返回类型将写入响应HTTP体。返回值将使用HttpMessageConverters 转换为声明的方法参数类型。
  • HttpEntity<?>或ResponseEntity<?>对象:目的是提供访问的Servlet响应HTTP头和内容。实体主体将使用HttpMessageConverters 转换为响应流。
  • HttpHeaders对象:返回没有body的响应。
  • Callable<?>:当应用程序想要在Spring MVC管理的线程中异步生成返回值时,可以返回该对象
  • DeferredResult<?>:当应用程序想要从自己选择​​的线程生成返回值时,可以返回该对象
  • ListenableFuture<?>或CompletableFuture<?>/CompletionStage<?>:当应用程序想要从线程池中生成值时,可以返回这些对象。
  • ResponseBodyEmitter:以异步方式将多个对象写入响应; 也支持作为ResponseEntity体返回。
  • SseEmitter:编写服务器发送的事件来异步响应; 也支持作为ResponseEntity体返回。
  • StreamingResponseBody:以异步方式写入响应OutputStream; 也支持作为ResponseEntity体返回。
  • 任何其他返回类型都被视为要向视图暴露的单个模型属性,使用@ModelAttribute在方法级别指定的属性名称(或基于返回类型类名称的默认属性名称)。命令对象和带@ModelAttribute注解的引用数据访问器方法的结果隐式地丰富了model。

3. 使用@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的required属性设置为false (例如@RequestParam(name="id", required=false))来指定参数是可选的。

如果不是目标方法参数类型,则自动应用类型转换 String。

当@RequestParam注解在Map<String, String> 或 MultiValueMap<String, String>参数上使用时,所有请求参数将会填充进来。

4. 使用@RequestBody注解映射请求体

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

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

可以使用HttpMessageConverter将请求体转换为方法参数。 HttpMessageConverter负责从HTTP请求消息转换为对象并从对象转换为HTTP响应体。RequestMappingHandlerAdapter支持@RequestBody注解使用以下默认HttpMessageConverter:

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

有关这些转换器的更多信息,请参阅消息转换器。注意,如果使用MVC命名空间或MVC Java配置,则默认情况下会注册更多的消息转换器。

如果打算读写XML,则需要MarshallingHttpMessageConverter使用包中的特定Marshaller和Unmarshaller 实现进行配置 org.springframework.oxm。下面的示例显示了如何在配置中直接执行此操作,但如果应用程序是通过MVC命名空间或MVC Java配置配置的。

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <util:list id="beanList">
            <ref bean="stringHttpMessageConverter"/>
            <ref bean="marshallingHttpMessageConverter"/>
        </util:list>
    </property
</bean>

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

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

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

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

就像@ModelAttribute参数一样,Errors参数可以用来检查错误。如果没有声明这样的参数,将会抛出MethodArgumentNotValidException。异常在DefaultHandlerExceptionResolver被处理,它将400错误发送回客户端。

提示:有关通过MVC命名空间或MVC Java配置配置消息转换器和验证器的信息,参考“启用MVC Java配置或MVC XML命名空间”。

5. 使用@ResponseBody注解映射响应体
@ResponseBody注解类似于@RequestBody。此注解可以放在方法上,并指示返回类型应直接写入HTTP响应体(不是放在模型中,或解释为视图名称)。例如:

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

上面的示例将文本Hello World被写入HTTP响应流。和@RequestBody一样,Spring通过使用HttpMessageConverter将返回的对象转换为响应体。

6. 使用@RestController注解创建REST控制器
控制器实现REST API是一个常用的方式,因此只提供JSON、XML或自定义MediaType内容。为方便起见,可以使用@RestController注解控制器类,而不是所有 @RequestMapping方法都通过添加@ResponseBody注解来实现 。

@RestController 组合了@ResponseBody和@Controller。更重要的是,它为Controller提供了更多的意义,并且可能在框架的未来版本中带来额外的语义。

与常规@Controllers一样,@RestController可以由@ControllerAdvice或@RestControllerAdvicebean bean辅助 。更多详细信息,参考“使用@ControllerAdvice和@RestControllerAdvice建言控制器”一节。

7. 使用HttpEntity
HttpEntity类似于@RequestBody和@ResponseBody。除了访问请求和响应体外,HttpEntity(以及特定于响应的子类ResponseEntity)还允许访问请求和响应头,如下所示:

@RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
    String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));
    byte[] requestBody = requestEntity.getBody();

    // do something with request header and body

    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}

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

与@RequestBody和@ResponseBody一样,Spring使用HttpMessageConverter转换请求和响应流。

8. 在方法上使用@ModelAttribute
@ModelAttribute注解可以用在方法或方法的参数上。

用在方法上的@ModelAttribute指示方法的目的是增加一个或多个模型的属性。此类方法支持与@RequestMapping注释的方法相同的参数类型,但不能直接映射到请求。在同一个控制器中,@ModelAttribute注释的方法在@RequestMapping方法之前执行。几个例子:

// 添加一个属性
// 方法返回值被添加到model中,添加的形式为'account=返回值'
// 可以通过@ModelAttribute("myAccount")自定义属性名称
@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));
    // add more ...
}

@ModelAttribute方法用于使用常用属性来填充模型,例如用状态或宠物类型填充下拉列表,或者检索像Account这样的命令对象,以便使用它来表示HTML表单上的数据。

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

控制器可以有任意数量的@ModelAttribute方法。在同一控制器中,在@RequestMapping方法之前调用这些方法。

@ModelAttribute方法也可以在@ControllerAdvice注释的类中定义,这种方法适用于许多控制器。

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

@ModelAttribute注解也可在已经添加了@RequestMapping的方法上使用。在这种情况下,@RequestMapping方法的返回值被解释为模型属性而不是视图名称。然后根据视图名称约定派生视图名称,就像方法返回void一样 。

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

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

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

以上示例,Pet实例可以来自何处?有几种选择:

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

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

@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) {
    // ...
}

在此示例中,模型属性的名称(例如“account”)与URI模板变量的名称匹配。如果注册Converter<String, Account>可以将 'String account'值转换为Account实例,那么上面的示例不需要@ModelAttribute方法即可运行。

下一步是数据绑定。WebDataBinder类匹配请求参数名称,包括查询字符串参数和表单域,然后通过名称设置模型属性字段。在必要时应用类型转换(从String到目标字段类型)后填充匹配字段。

作为数据绑定的结果,可能存在错误,例如缺少必填字段或类型转换错误。要检查此类错误,在@ModelAttribute参数后面添加一个BindingResult参数:

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {

    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

使用BindingResult可以检查在哪种情况下发生错误,通常会在Spring的<errors> 表单标签的帮助下呈现相同的错误表单。

在某些情况下,在没有数据绑定的情况下访问模型中的属性可能很有用。对于这种情况,可以将其Model注入控制器或者在@ModelAttribute注解上使用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("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {

    new PetValidator().validate(pet, result);
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}

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

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

10. 使用@SessionAttributes存储在请求之间的HTTP会话中的模型属性
类级别@SessionAttributes注解声明特定处理器使用的会话属性。这通常会列出模型属性的名称或模型属性的类型,这些属性应该透明地存储在会话或某些会话存储中,作为后续请求之间的表单支持bean。

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

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

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

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

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

12. 使用@RequestAttribute访问请求属性
与@SessionAttribute注解类似,@RequestAttribute注解可以被用于访问由过滤器或拦截器创建的预先存在的请求属性:

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

13. 使用“application/x-www-form-urlencoded”数据
前面几节介绍了如何使用@ModelAttribute来支持来自浏览器客户端的表单提交请求。建议将相同的注解用于非浏览器客户端的请求。但是,在处理HTTP PUT请求时,有一个显着的区别:浏览器可以通过HTTP GET或HTTP POST提交表单数据;非浏览器客户端也会通过HTTP PUT提交表单。这提出了一个挑战,因为Servlet规范要求ServletRequest.getParameter*()一系列方法仅支持HTTP POST的表单字段访问,而不支持HTTP PUT。

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

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

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

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

上面的过滤器截取带有content type为 application/x-www-form-urlencoded的HTTP PUT和PATCH请求,从请求体读取表单数据,并包装ServletRequest保证ServletRequest.getParameter*()方法族可以获取表单数据 。

注:由于HttpPutFormContentFilter消耗了请求体,假如content type 为application/x-www-form-urlencoded的PUT或PATCH 请求URL依赖于其他转换器,则不应配置。这包括@RequestBody MultiValueMap<String, String>和HttpEntity<MultiValueMap<String, String>>。

14. 使用@CookieValue注解映射cookie值
@CookieValue注解允许方法参数绑定到一个HTTP cookie的值。

让我们考虑收到以下cookie并带有http请求:

JSESSIONID = 415A4AC178C59DACE0B2C9CA727CDD84
以下代码示例演示了如何获取JSESSIONIDcookie 的值:

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

如果不是目标方法参数类型,则自动应用类型转换 String。

Servlet和Portlet环境中的处理程序方法支持此注解。

15. 使用@RequestHeader注解映射请求头属性
@RequestHeader注解允许方法参数绑定到请求头。

这是一个示例请求头:

Host                    localhost:8080
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language         fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding         gzip,deflate
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive              300

以下代码示例演示了如何获取Accept-Encoding和 Keep-Alive请求头的值: 

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

如果方法参数不是String,则自动应用类型转换String。

当@RequestHeader注解上被应用到Map<String, String>, MultiValueMap<String, String>或HttpHeaders参数,则map被填充所有的请求头值。

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

Servlet和Portlet环境中的处理器方法支持此注解。

16. 方法参数和类型转换
从请求中提取的基于字符串的值(包括请求参数,路径变量,请求头和cookie值)可能需要转换为方法参数或字段的目标类型(例如,将请求参数绑定到参数中的字段@ModelAttribute)。如果目标类型不是String,Spring会自动转换为适当的类型。支持所有简单类型,如int、long、Date等。可以进一步自定义通过转换过程WebDataBinder,或者通过注册Formatters与FormattingConversionService。

17. 自定义WebDataBinder初始化
要通过Spring的WebDataBinder使用PropertyEditors自定义请求参数绑定 ,可以在控制器中使用@InitBinder注释方法,在@ControllerAdvice类中的@InitBinder注释的方法 ,或者提供自定义 WebBindingInitializer。

18. 使用@InitBinder自定义数据绑定
通过@InitBinder注释的控制器方法,可以直接在控制器类中配置Web数据绑定。@InitBinder标识初始化的方法,WebDataBinder用于填充带注解的处理器方法的命令和表单对象参数。

此类init-binder方法支持所有@RequestMapping方法支持的参数,但命令/表单对象和相应的验证结果对象除外。Init-binder方法不能具有返回值。因此,它们通常被声明为void。典型的参数包括WebDataBinder与WebRequest或 结合使用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(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    // ...
}

或者,从Spring 4.2开始,考虑使用addCustomFormatter指定 Formatter实现而不是PropertyEditor实例。如果碰巧Formatter在共享FormattingConversionService中也有一个基于设置的设置, 这一点特别有用,同样的方法可以重复用于特定于控制器的绑定规则调整。

@Controller
public class MyFormController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }
    // ...
}

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

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

<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注释的类中定义,在这种情况下,它们适用于匹配的控制器。这提供了使用WebBindingInitializer的一种替代方法。

20. 使用@ControllerAdvice和@RestControllerAdvice为控制器提供建言
@ControllerAdvice注解是一个组件注解,允许通过类路径扫描自动检测实现类。使用MVC命名空间或MVC Java配置时会自动启用它。

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

@RestControllerAdvice是一种替代返回值被@ResponseBody注释的@ExceptionHandler方法。

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

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {}

// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class BasePackageAdvice {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class AssignableTypesAdvice {}

查看 @ControllerAdvice 文档以获取更多详细信息(ControllerAdvice.html)。

21. Jackson序列化视图支持
有时可以过滤上下文将要序列化到HTTP响应体的对象。为了提供这样的功能,Spring MVC内置了对Jackson的序列化视图进行渲染的支持。

要将它与返回的@ResponseBody控制器方法或返回ResponseEntity的控制器方法一起使用,只需添加类参数为特定类或接口的@JsonView注解:

@RestController
public class UserController {

    @GetMapping("/user")
    @JsonView(User.WithoutPasswordView.class)
    public User getUser() {
        return new User("eric", "7!jd#h23");
    }
}

public class User {

    public interface WithoutPasswordView {};
    public interface WithPasswordView extends WithoutPasswordView {};

    private String username;
    private String password;

    public User() {
    }

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

    @JsonView(WithoutPasswordView.class)
    public String getUsername() {
        return this.username;
    }

    @JsonView(WithPasswordView.class)
    public String getPassword() {
        return this.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.class.getName(), User.WithoutPasswordView.class);
        return "userView";
    }
}

22. Jackson JSONP的支持
为了启用JSONP支持@ResponseBody 和ResponseEntity方法,声明一个继承于AbstractJsonpResponseBodyAdvice的@ControllerAdvice bean,其中构造方法参数指定JSONP查询参数名称,如下所示扩展:

@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

    public JsonpAdvice() {
        super("callback");
    }
}

对于依赖于视图解析的控制器,当请求具有名为jsonp或callback的查询参数时,将自动启用JSONP。这些名称可以通过jsonpParameterNames属性定制。

注:从Spring Framework 4.3.18开始,JSONP的支持被废弃了,从Spring Framework 5.1开始,将删除JSONP支持,而应使用CORS。

猜你喜欢

转载自blog.csdn.net/mytt_10566/article/details/84075840
今日推荐