`
starscream
  • 浏览: 285876 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring MVC 学习笔记 十五 what's new in spring mvc 3.1

阅读更多

1: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>[/color]
</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">
[color=red]        <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以便于让用户更方便的实现自定义的实现类。
11
1
分享到:
评论
10 楼 云端月影 2015-05-22  
9 楼 sunhao_java 2015-02-04  
   
8 楼 太阳神喻 2013-08-29  
不顶不行啊
7 楼 finallygo 2013-06-18  
Content-Type 不是response中的么?
6 楼 saintsaya 2013-01-17  
好郁闷啊,看到第二个参数的楼主给解释了
5 楼 saintsaya 2013-01-17  
你妹儿的试了第一个就给我整蒙了,写了一个类实现HandlerMethodReturnValueHandler接口然后在那两个方法里面写了两句话测试了一下,发各种请求死活都走不到我写的这两个方法,后来各种debug代码才知道这个returnHandler是有优先级的,我一直请求的方法返回值都是规规矩矩的ModelAndView,Map,String这几种,实际上spring mvc是先匹配他默认的一堆ModelAndView,Model,Map什么的Handler,最后才匹配自己写的这个,这不坑爹么,把自己写的放在最顶层设计多好,要不怎么去覆盖默认的啊,写在这里,以后有人犯了跟我一样的毛病就知道怎么回事儿了,写这个是用来解析spring mvc无法解析的返回值的,能解析的也无法用不过自己写的方法来覆盖它。
4 楼 dxqrr 2012-09-16  
3 楼 lingjiangzhou0923 2012-06-03  
2 楼 lingjiangzhou0923 2012-06-03  
1 楼 hongyang070 2011-12-15  

相关推荐

Global site tag (gtag.js) - Google Analytics