您的位置:首页 > 编程语言 > Java开发

spring mvc参数绑定大全

2013-12-02 17:42 344 查看
spring mvc是非常好用的mvc框架,不仅因为与spring的无缝集成,还因为每个细节都被考虑得及其周到。而且spring也是代码的典范,所有的开源项目中,spring的代码是我见过的层次最为清晰,结构最为规范,注释最为全面的开源项目。

相信用过springmvc的人都对springmvc强大的参数绑定能力印象深刻,但是用的时候总是有些心有余悸,因为功能太多太强大,导致用的时候不清楚这样用可不可以,也不知道还有没有更强大的功能没被发掘。

这篇文章主要是讲spring的参数绑定,各种annotation,用法及其原理。

大家知道,处理请求的过程是在DispatchServlet中的doDispatch方法中,通过HandlerAdapter的handle方法进行的。

spring默认的组件全都配置在DispatcherServlet.properties中,而关于我们Controller使用annotation注解的HandlerAdapter目前(3.2)是配置了AnnotationMethodHandlerAdapter,它的handle方法会调用invokeHandlerMethod方法,最终会调到HandlerMethodInvoker的invokeHandlerMethod。

这个方法比较重要,主要做了这样几件事

1. 将session中的参数放到Model里去,这样可以方便的用EL表达式暴露session中的变量。从这里我们可以得知spring的el表达式里天然可以使用session中的参数

2. 处理有ModelAttribute这个annotation的方法,并将其结果放到对应的Model中去。从这里我们可以得知ModelAttribute的作用是帮助Model初始化参数,举个非常简单的例子,如果有一个计算item总数是每个页面都需要显示的,通常的做法是在每一个返回的页面之前都将该计算结果加入Model,这样会出现很多重复,如下。

@RequestMapping(value = "/doSomething1")
public String doSomething1(Model model,
HttpServletRequest request) {
model.addAttribute("maxCount", service.maxCount());
return "result1";
}
@RequestMapping(value = "/doSomething2")
public String doSomething2(Model model,
HttpServletRequest request) {
model.addAttribute("maxCount", service.maxCount());
return "result2";
}
实际上更好的用法是,因为在进每个RequestMapping前,所有的ModelAttribute都会被自动扫描并将结果添加到Model里去,所以实际上可以省略每个方法里的设置参数。

@ModelAttribute("maxCount")
public int maxCount(){
return service.maxCount();
}
@RequestMapping(value = "/doSomething1")
public String doSomething1(Model model,
HttpServletRequest request) {
return "result1";
}
@RequestMapping(value = "/doSomething2")
public String doSomething2(Model model,
HttpServletRequest request) {
return "result2";
}


3. 解析映射方法的参数。这个方法非常重要,因为所有的待解析的参数,springmvc是如何为我们做的数据绑定都在这里了。这个方法是resolveHandlerArguments,篇幅原因这里就不列出它的代码了。但是从这个方法可以发现springmvc所有对参数绑定的用法。

下面一一介绍

@RequestHeader:绑定一个http头,比如想要知道他的referer,可以这样写

public String test(@RequestHeader(value="Referer") String referer){
return "";
}
@RequestParam:绑定一个请求参数,这个是最常见的要求了,但是通常也可以省略它,这个参数可以是从url来,也可以从post中来,比如

public String test(@RequestParam(value="id") String id){
return "";
}


@RequestBody:绑定一个Post请求的content,这个方法通常可以用来做一些自定义的活动,比如post请求中放的是自定义格式的数据,或者是json数据要自己手动解析,比如

public String test(@RequestBody String body){
return "";
}
@CookieValue:绑定一个请求中的cookie,这个也是让我们省不少事的方法

public String test(@CookieValue(value="loginId") String loginId){
return "";
}


@PathVariable:绑定一个restful风格的变量

@RequestMapping(value = "/test/{userName}")
public String test(@PathVariable(value="userName") String userName){
return "";
}
@ModelAttribute:将入参直接放到Model中

public String test(@ModelAttribute(value="userName") String userName){
return "";
}
@Value:spring还支持将配置的值作为入参传入,方法就是使用@Value注解

public String test(@Value(value="${mysql.connectionUrl}") String url){
return "";
}
@Valid*:有意思的是,只要注解是Valid开头的,spring都会试图帮你去做验证,这意味着约定大于配置

if (paramAnn.annotationType().getSimpleName().startsWith("Valid"))


spring最牛的能力是你根本不需要写注释,它会根据你的需要去自动注入常见的参数。比如HttpServletRequest, HttpServletResponse, HttpSession, Principal, Locale, InputStream, Reader, OutputStream, Writer,你只要将这些写到方法的参数中,spring便会自动将这些参数在运行时自动当做参数调用,这是非常方便的地方,要注意的是,除了上面这些已知的参数,暂时不支持其他的参数。关于这部分代码,在ServletHandlerMethodInvoker的resolveStandardArgument中。

而spring能够注入一些支持的参数,比如常用的Model(用来当做EL表达式的一个map),SessionStatus,HttpEntity(将http的head和body封装到一起)
另外,对于未写注解@RequestParam简单类型,会自动从request中去获取并注入,这些简单类型包括原生类型及包装类,枚举,CharSequence的所有子类(所以包括常见的字符串,StringBuffer等),所有Number类及其子类(包括BigDecimal,BigInteger,Atomic*等常见类),另外,所有Date,URL,URI,Locale,Class都在简单类型之列,不得不感慨spring的强大。简单类型的定义在BeanUtils.isSimpleValueType

那么,对于自定义的POJO类型,spring又是怎么处理的呢?在resolveModelAttribute中也给出了他的做法,首先去Model里找,找不到就去Session中找,还是找不到就自己新建一个。新建完之后一个主要动作是去调用被注解为@InitBinder的所有方法,它的作用是规定一些自定义的映射器,比如如下的例子,就是将multpart对象封装成byte数组。

@InitBinder
protected void initBinder(WebDataBinder binder) throws ServletException {
binder.registerCustomEditor(byte[].class,
new ByteArrayMultipartFileEditor());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(
dateFormat, false));
}


当InitBinder调用完之后,由doBind这个方法来完成POJO对象中参数的注入,这个地方主要的做法就是将request中的参数拿出来注入到POJO对象中,从代码实现来看,不支持POJO嵌套的注入,所以使用上要注意。

4. 真正的去调用该方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: