<mvc:annotation-driven>新增標簽
以下為spring mvc 3.1中annotation-driven所支持的全部配置
<mvc:annotation-driven message-codes-resolver ="bean ref" validator="" conversion-service=""> <mvc:return-value-handlers> <bean></bean> </mvc:return-value-handlers> <mvc:argument-resolvers> </mvc:argument-resolvers> <mvc:message-converters> </mvc:message-converters> </mvc:annotation-driven>
其中3.1新增部分如下
return-value-handlers
允許註冊實現了HandlerMethodReturnValueHandler接口的bean,來對handler method的特定的返回類型做處理。
HandlerMethodReturnValueHandler接口中定義了兩個方法
supportsReturnType 方法用來確定此實現類是否支持對應返回類型。
handleReturnValue 則用來處理具體的返回類型。
例如以下的handlerMethod
@RequestMapping("/testReturnHandlers") public User testHandlerReturnMethod(){ User u = new User(); u.setUserName("test"); return u; }
所返回的類型為一個pojo,正常情況下spring mvc無法解析,將轉由DefaultRequestToViewNameTranslator 解析出一個缺省的view name,轉到 testReturnHandlers.jsp,
我們增加以下配置
<mvc:annotation-drivenvalidator="validator"><mvc:return-value-handlers> <bean class="net.zhepu.web.handlers.returnHandler.UserHandlers"></bean> </mvc:return-value-handlers>[/color] </mvc:annotation-driven>
及如下實現類
public class UserHandlers implements HandlerMethodReturnValueHandler { Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public boolean supportsReturnType(MethodParameter returnType) { Class<?> type = returnType.getParameterType(); if(User.class.equals(type)) { return true; } return false; } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { logger.info("handler for return type users "); mavContainer.setViewName("helloworld"); } }
此時再訪問 http://localhost:8080/springmvc/testReturnHandlers ,將交由 UserHandlers來處理返回類型為User的返回值。
argument-resolvers
允許註冊實現了WebArgumentResolver接口的bean,來對handlerMethod中的用戶自定義的參數或annotation進行解析
例如
<mvc:annotation-driven validator="validator"> <mvc:argument-resolvers> <bean class="net.zhepu.web.handlers.argumentHandler.MyCustomerWebArgumentHandler" /> </mvc:argument-resolvers> </mvc:annotation-driven>
對應java代碼如下
public class MyCustomerWebArgumentHandler implements WebArgumentResolver { @Override public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception { if (methodParameter.getParameterType().equals(MyArgument.class)) { MyArgument argu = new MyArgument(); argu.setArgumentName("winzip"); argu.setArgumentValue("123456"); return argu; } return UNRESOLVED; } }
這裏我們定義了一個 customer webArgumentHandler,當handler method中參數類型為 MyArgument時生成對參數的類型綁定操作。
註意新註冊的webArgumentHandler的優先級最低,即如果系統缺省註冊的ArgumentHandler已經可以解析對應的參數類型時,就不會再調用到新註冊的customer ArgumentHandler了。
message-converters
允許註冊實現了HttpMessageConverter接口的bean,來對requestbody 或responsebody中的數據進行解析
例如
假設我們使用 text/plain格式發送一串字符串來表示User對象,各個屬性值使用”|”來分隔。例如 winzip|123456|13818888888,期望轉為user對象,各屬性內容為user.username = winzip,user.password=123456;user.mobileNO = 13818888888
以下代碼中supports表示此httpmessageConverter實現類針對 User類進行解析。
構造函數中調用 super(new MediaType("text", "plain"));以表示支持 text/plain格式的輸入。
public class MyCustomerMessageConverter extends AbstractHttpMessageConverter<Object> { @Override protected boolean supports(Class<?> clazz) { if (clazz.equals(User.class)) { return true; } return false; } public MyCustomerMessageConverter() { super(new MediaType("text", "plain")); } @Override protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { Charset charset; MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType != null && contentType.getCharSet() != null) { charset = contentType.getCharSet(); } else { charset = Charset.forName("UTF-8"); } String input = FileCopyUtils.copyToString(new InputStreamReader( inputMessage.getBody(), charset)); logger.info(input); String[] s = input.split("\\|"); User u = new User(); u.setUserName(s[0]); u.setPassword(s[1]); u.setMobileNO(s[2]); return u; } @Override protected void writeInternal(Object t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { }
修改servlet context xml配置文件,增加message-converters的相應配置如下
<mvc:message-converters> <bean class="net.zhepu.web.handlers.messageConverterHandler.MyCustomerMessageConverter"></bean> </mvc:message-converters>
message-codes-resolver
先看看spring mvc中對於messageCodeResolver的用法。
spring mvc中使用DefaultMessageCodesResolver作為缺省的MessageCodesResolver的實現類,其作用是對valid errors中的errorcode進行解析。其解析方式如下
當解析error global object註冊的errorcode時,errorcode的查找順序為
1:errorcode.validationobjectname
2:errorcode
例如
以下聲明中
public String helloWorld2(@ModelAttribute("user") User u, BindingResult result)
當使用 result.reject("testFlag");來註冊一個globat error object時,spring mvc將在messageSource中先查找 testFlag.user這個errorcode,當找不到時再查找testFlag這個errorcode。
當解析fields error時,將按以下順序生成error code
1.: code + "." + object name + "." + field
2.: code + "." + field
3.: code + "." + field type
4.: code
還是以上面的代碼為例,當使用 result.rejectValue("userName", "testFlag");來註冊一個針對user.UserName屬性的錯誤描述時,errors對象中將生成以下的error code list,
1.: testFlag.user.userName
2.: testFlag.userName
3.: testFlag.java.lang.String
4.: testFlag
而mvc:annotation-driven新增的屬性message-codes-resolver則提供了註冊自定義的MessageCodesResolver的手段。
例如上面想要在所有的error code前增加前綴validation.的話,可以這麽來做
<mvc:annotation-driven validator="validator" message-codes-resolver="messageCodeResolver"> </mvc:annotation-driven>
新增messageCodeResolver bean定義如下
<bean id="messageCodeResolver" class="org.springframework.validation.DefaultMessageCodesResolver"> <property name="prefix" value="validation."></property> </bean>
此時,所有的errorcode都會生成缺省前綴 validation.
例如前面的 result.reject("testFlag"); 生成的error code list就變為了
validation.testFlag.user 和 validation.testFlag了
2: @RequestMapping 新增參數Consumes 和Produces
前面介紹過@RequestMapping的參數中有一個header的參數,來指定handler method能接受的http request 請求的header內容。
而consumes和produces則更進一步,直接指定所能接受或產生的request請求的content type。
例如
@RequestMapping(value="/testMsgConverter",consumes="text/plain",produces="application/json")
表示handlermethod接受的請求的header中的 Content-Type為text/plain;
Accept為application/json
3: URI Template 新增功能
這部分的例子直接照抄Spring 3.1 M2: Spring MVC Enhancements中的示例
1: @PathVariable 聲明的參數可自動加入到model中。
例如
@RequestMapping("/develop/apps/edit/{slug}") public String editForm(@PathVariable String slug, Model model) { model.addAttribute("slug", slug); // ... }
現在可以寫為
@RequestMapping("/develop/apps/edit/{slug}") public String editForm(@PathVariable String slug, Model model) { // model contains "slug" variable }
2:handler method中的redirect string可支持url template了
例如
@RequestMapping( value="/groups/{group}/events/{year}/{month}/{slug}/rooms", method=RequestMethod.POST) public String createRoom( @PathVariable String group, @PathVariable Integer year, @PathVariable Integer month, @PathVariable String slug) { // ... return "redirect:/groups/" + group + "/events/" + year + "/" + month + "/" + slug; }
現在可寫為
@RequestMapping( value="/groups/{group}/events/{year}/{month}/{slug}/rooms", method=RequestMethod.POST) public String createRoom( @PathVariable String group, @PathVariable Integer year, @PathVariable Integer month, @PathVariable String slug) { // ... return "redirect:/groups/{group}/events/{year}/{month}/{slug}"; }
3:url template中可支持databinding 了
例如
@RequestMapping("/people/{firstName}/{lastName}/SSN") public String find(Person person, @PathVariable String firstName, @PathVariable String lastName) { person.setFirstName(firstName); person.setLastName(lastName); // ... }
現在可以寫成
@RequestMapping("/people/{firstName}/{lastName}/SSN") public String search(Person person) { // person.getFirstName() and person.getLastName() are populated // ... }
4: Validation For @RequestBody
@RequestBody現在直接支持@valid標註了,如果validation失敗,將拋出
RequestBodyNotValidException。
具體處理邏輯可見 spring 中的RequestResponseBodyMethodProcessor中的以下代碼。
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Object arg = readWithMessageConverters(webRequest, parameter, parameter.getParameterType()); if (shouldValidate(parameter, arg)) { String argName = Conventions.getVariableNameForParameter(parameter); WebDataBinder binder = binderFactory.createBinder(webRequest, arg, argName); binder.validate(); Errors errors = binder.getBindingResult(); if (errors.hasErrors()) { throw new RequestBodyNotValidException(errors); } } return arg; }
5:annotation-driven缺省註冊類的改變
Spring 3.0.x中使用了annotation-driven後,缺省使用DefaultAnnotationHandlerMapping 來註冊handler method和request的mapping關系。
AnnotationMethodHandlerAdapter來在實際調用handlermethod前對其參數進行處理。
並在dispatcherServlet中,當用戶未註冊自定義的ExceptionResolver時,註冊AnnotationMethodHandlerExceptionResolver來對使用@ExceptionHandler標註的異常處理函數進行解析處理(這也導致當用戶註冊了自定義的exeptionResolver時將可能導致無法處理@ExceptionHandler)。
在spring mvc 3.1中,對應變更為
DefaultAnnotationHandlerMapping -> RequestMappingHandlerMapping
AnnotationMethodHandlerAdapter -> RequestMappingHandlerAdapter
AnnotationMethodHandlerExceptionResolver -> ExceptionHandlerExceptionResolver
以上都在使用了annotation-driven後自動註冊。
而且對應分別提供了AbstractHandlerMethodMapping , AbstractHandlerMethodAdapter和 AbstractHandlerMethodExceptionResolver以便於讓用戶更方便的實現自定義的實現類。
<mvc:annotation-driven>新增標簽