1. 程式人生 > ><mvc:annotation-driven>新增標簽

<mvc:annotation-driven>新增標簽

rst 我們 mod param acc redirect tro adapter tpi

以下為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-driven 
validator="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>新增標簽