LOGO SPRING MVC 3.0实战指南.ppt
Spring MVC 3.0实战指南,参考Spring 3.x企业应用开发实战,目录,Spring MVC框架简介,1,Spring MVC 3.0新特性,支持REST风格的URL添加更多注解,可完全注解驱动引入HTTP输入输出转换器(HttpMessageConverter)和数据转换、格式化、验证框架无缝集成对静态资源处理提供特殊支持更加灵活的控制器方法签名,可完全独立于Servlet API,参考Spring 3.x企业应用开发实战,Spring MVC框架结构,DispatcherServlet,HandlerMapping,HandlerAdapter,Handler,View(JSP/XML/PDF,.),2,3,4,ViewResolver,6,8,1,参考Spring 3.x企业应用开发实战,Spring MVC框架结构,package com.baobaotao.web;.Controller 将UserController变成一个HandlerRequestMapping(“/user”)指定控制器映射的URLpublic class UserController RequestMapping(value=“/register”)处理方法对应的URL,相对于 处的URLpublic String register()return“user/register”;返回逻辑视图名,参考Spring 3.x企业应用开发实战,框架的实现者,DispatcherServlet,DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter,UserController,register.jsp,2,3,4,InternalResourceViewResolver,6,8,1,参考Spring 3.x企业应用开发实战,目录,Spring MVC框架简介,1,HTTP请求映射原理,HTTP请求报文,Handler处理方法,SpringMVC框架,WEB容器,参考Spring 3.x企业应用开发实战,Spring MVC进行映射的依据,参考Spring 3.x企业应用开发实战,通过URL限定:URL表达式,RequestMapping不但支持标准的URL,还支持Ant风格(即?、*和*的字符,参见3.3.2节的内容)的和带xxx占位符的URL。以下URL都是合法的:/user/*/createUser 匹配/user/aaa/createUser、/user/bbb/createUser等URL。/user/*/createUser 匹配/user/createUser、/user/aaa/bbb/createUser等URL。/user/createUser?匹配/user/createUseraa、/user/createUserbb等URL。/user/userId 匹配user/123、user/abc等URL。/user/*/userId 匹配user/aaa/bbb/123、user/aaa/456等URL。company/companyId/user/userId/detail 匹配company/123/user/456/detail等的URL。,参考Spring 3.x企业应用开发实战,通过URL限定:绑定xxx中的值,RequestMapping(/userId)public ModelAndView showDetail(PathVariable(userId)String userId)ModelAndView mav=new ModelAndView();mav.setViewName(user/showDetail);mav.addObject(user,userService.getUserById(userId);return mav;,ControllerRequestMapping(/owners/ownerId)public class RelativePathUriTemplateController RequestMapping(/pets/petId)public void findPet(PathVariable String ownerId,PathVariable String petId,Model model),URL中的xxx占位符可以通过PathVariable(xxx)绑定到操作方法的入参中。,如果PathVariable不指定参数名,只有在编译时打开debug开关(javac-debug=no)时才可行!(不建议),参考Spring 3.x企业应用开发实战,通过请求方法限定:请求方法,请求方法,在HTTP中这被叫做动词(verb),除了两个大家熟知的(GET和POST)之外,标准方法集合中还包含PUT、DELETE、HEAD和OPTIONS。这些方法的含义连同行为许诺都一起定义在HTTP规范之中。一般浏览器只支持GET和POST方法。,参考Spring 3.x企业应用开发实战,通过请求方法限定:代码示例,示例1:RequestMapping(value=“/delete”)public String test1(RequestParam(userId)String userId)return user/test1;所有URL为/delete的请求由test1处理(任何请求方法)示例2:RequestMapping(value=/delete,method=RequestMethod.POST)public String test1(RequestParam(userId)String userId)return user/test1;所有URL为/delete 且请求方法为POST 的请求由test1处理,参考Spring 3.x企业应用开发实战,通过请求方法限定:模拟请求方法,通过在web.xml中配置一个org.springframework.web.filter.HiddenHttpMethodFilter通过POST请求的_method参数指定请求方法,HiddenHttpMethodFilter动态更改HTTP头信息。,HiddenHttpMethodFilter,POST HTTP请求,method=PUT&.,Spring MVC,PUT HTTP请求,参考Spring 3.x企业应用开发实战,通过请求/请求头参数限定:示例,RequestMapping(value=/delete,params=userId)public String test1(RequestParam(userId)String userId).,RequestMapping(value=/show,headers=content-type=text/*)public String test2(RequestParam(userId)String userId).,通过请求参数限定:,通过请求头参数限定:,参考Spring 3.x企业应用开发实战,通过请求/请求头参数限定:更多,params和headers分别通过请求参数及报文头属性进行映射,它们支持简单的表达式,下面以params表达式为例说明,headers可以参照params进行理解之。param1:表示请求必须包含名为param1的请求参数。!param1:表示请求不能包含名为param1的请求参数。param1!=value1:表示请求包含名为param1的请求参数,但其值不能为value1。param1=value1,param2:请求必须包含名为param1和param2的两个请求参数,且param1参数的值必须为value1。,参考Spring 3.x企业应用开发实战,目录,Spring MVC框架简介,1,通过注解绑定:示意图,public String handle1(.),RequestParam绑定请求参数,RequestHeader绑定请求头参数,CookieValue绑定Cookie的值,PathVariable绑定URL中的变量,参考Spring 3.x企业应用开发实战,通过注解绑定:示例,RequestMapping(value=/handle1)public String handle1(RequestParam(userName)String userName,RequestParam(password)String password,RequestParam(realName)String realName).,RequestMapping(value=/handle2)public String handle2(CookieValue(JSESSIONID)String sessionId,RequestHeader(Accept-Language)String accpetLanguage).,参考Spring 3.x企业应用开发实战,通过注解绑定:小心抛出异常,RequestParam有以下三个参数。value:参数名。required:是否必需,默认为true,表示请求中必须包含对应的参数名,如果不存在将抛出异常。defaultValue:默认参数名,设置该参数时,自动将required设为false。极少情况需要使用该参数,也不推荐使用该参数。,RequestMapping(value=/handle1)public String handle1(RequestParam(userName)String userName,).,上面的处理方法,如果HTTP请求不包含“userName”参数时,将产生异常!因此,如果不能保证存在”userName”的参数,必须使用:RequestParam(value=userName,required=false),参考Spring 3.x企业应用开发实战,使用命令/表单对象绑定,所谓命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO。Spring MVC按:“HTTP请求参数名=命令/表单对象的属性名”的规则,自动绑定请求数据,支持“级联属性名”,自动进行基本类型数据转换。,RequestMapping(value=/handle14)public String handle14(User user),userName=xxx&password=yyy,class User private String userName;private String password;,参考Spring 3.x企业应用开发实战,使用Servlet API对象作为入参,在Spring MVC中,控制器类可以不依赖任何Servlet API对象,但是Spring MVC并不阻止我们使用Servlet API的类作为处理方法的入参。值得注意的是,如果处理方法自行使用HttpServletResponse返回响应,则处理方法的返回值设置成void即可。,RequestMapping(value=/handle21)public void handle21(HttpServletRequest request,HttpServletResponse response)String userName=WebUtils.findParameterValue(request,userName);response.addCookie(new Cookie(userName,userName);,public String handle23(HttpSession session)session.setAttribute(sessionId,1234);return success;,public String handle24(HttpServletRequest request,RequestParam(userName)String userName)return success;,使用Spring的Servlet API代理类,Spring MVC在org.springframework.web.context.request包中定义了若干个可代理Servlet原生API类的接口,如WebRequest和NativeWebRequest,它们也允许作为处理类的入参,通过这些代理类可访问请求对象的任何信息。,RequestMapping(value=/handle25)public String handle25(WebRequest request)String userName=request.getParameter(userName);return success;,参考Spring 3.x企业应用开发实战,使用IO对象作为入参,Spring MVC允许控制器的处理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream/java.io.Writer作为方法的入参,RequestMapping(value=/handle31)public void handle31(OutputStream os)throws IOException Resource res=new ClassPathResource(/image.jpg);/读取类路径下的图片文件 FileCopyUtils.copy(res.getInputStream(),os);/将图片写到输出流中,Spring MVC将获取ServletRequest的InputStream/Reader或ServletResponse的OutputStream/Writer,然后按类型匹配的方式,传递给控制器的处理方法入参。,参考Spring 3.x企业应用开发实战,其他类型的参数,控制器处理方法的入参除支持以上类型的参数以外,还支持java.util.Locale、java.security.Principal,可以通过Servlet的HttpServletRequest 的getLocale()及getUserPrincipal()得到相应的值。如果处理方法的入参类型为Locale或Principal,Spring MVC自动从请求对象中获取相应的对象并传递给处理方法的入参。,RequestMapping(value=/handle32)public void handle31(Locale locale)throws IOException.,HttpMessageConverter,HttpServletRequest,HttpServletRequest,RequestBody/HttpEntity,ResponseBody/ResponseEntity,HttpMessageConverter,HTTP请求报文,HTTP请求报文,HttpMessageConverter实现类,AnnotationMethodHandlerAdapter,实现类:StringHttpMessageConverterFormHttpMessageConverterXmlAwareFormHttpMessageConverterResourceHttpMessageConverterBufferedImageHttpMessageConverterByteArrayHttpMessageConverterSourceHttpMessageConverterMarshallingHttpMessageConverterJaxb2RootElementHttpMessageConverterMappingJacksonHttpMessageConverterRssChannelHttpMessageConverterAtomFeedHttpMessageConverter,注册到.,HttpMessageConverter接口方法T read(HttpInputMessage httpInputMessage)void write(T t,HttpOutputMessage httpOutputMessage),使用RequestBody/ResponseBody,将HttpServletRequest的getInputStream()内容绑定到入参,将处理方法返回值写入到HttpServletResponse的getOutputStream()中。,RequestMapping(value=/handle41)public String handle41(RequestBody String requestBody)System.out.println(requestBody);return success;,ResponseBodyRequestMapping(value=/handle42/imageId)public byte handle42(PathVariable(imageId)String imageId)throws IOException System.out.println(load image of+imageId);Resource res=new ClassPathResource(/image.jpg);byte fileData=FileCopyUtils.copyToByteArray(res.getInputStream();return fileData;,优点:处理方法签名灵活不受限缺点:只能访问报文体,不能访问报文头,参考Spring 3.x企业应用开发实战,使用HttpEntity/ResponseEntity,RequestMapping(value=/handle43)public String handle43(HttpEntity httpEntity)long contentLen=httpEntity.getHeaders().getContentLength();System.out.println(httpEntity.getBody();return success;,RequestMapping(params=method=login)public ResponseEntity doFirst()HttpHeaders headers=new HttpHeaders();MediaType mt=new MediaType(text,html,Charset.forName(“UTF-8);headers.setContentType(mt);ResponseEntity re=null;String return=new String(test);re=new ResponseEntity(return,headers,HttpStatus.OK);return re;,优点:处理方法签名受限缺点:不但可以访问报文体,还能访问报文头,参考Spring 3.x企业应用开发实战,输出XML和JSON,处理XML转换,处理JSON转换,参考Spring 3.x企业应用开发实战,使用HttpEntity/ResponseEntity,RequestMapping(value=/handle51)public ResponseEntity handle51(HttpEntity requestEntity)User user=requestEntity.getBody();user.setUserId(1000);return new ResponseEntity(user,HttpStatus.OK);,对于服务端的处理方法而言,除使用RequestBody/ResponseBody或HttpEntity/ResponseEntity进行方法签名外,不需要进行任何额外的处理,借由Spring MVC中装配的HttpMessageConverter,它即拥有了处理XML及JSON的能力了。,参考Spring 3.x企业应用开发实战,目录,Spring MVC框架简介,1,数据绑定机理,DataBinder,ConversionService,Validator,3,4,BindingResult,5,1,ServletRequest,处理方法入参对象集,处理方法的签名,2,数据类型转换/格式化,数据校验,数据类型转换,低版本的Spring 只支持标准的PropertyEditor类型体系,不过PropertyEditor存在以下缺陷:只能用于字符串和Java对象的转换,不适用于任意两个Java类型之间的转换;对源对象及目标对象所在的上下文信息(如注解、所在宿主类的结构等)不敏感,在类型转换时不能利用这些上下文信息实施高级转换逻辑。,有鉴于此,Spring 3.0在核心模型中添加了一个通用的类型转换模块,ConversionService是Spring类型转换体系的核心接口。Spring 3.0同时支持PropertyEditor和ConversionService 进行类型转换,在Bean配置、Spring MVC处理方法入参绑定中使用类型转换体系进行工作。,参考Spring 3.x企业应用开发实战,PropertyEditor依然有效,对于简单的类型转换,依然建议使用PropertyEditor。按照PropertyEditor的协议,会自动查找Bean类相同类包是否存在Editor.class,如果存在会使用它作为Bean的编辑器。,com.book.core.cache.expired|_CacheSpace.java|_ CacheSpaceEditor.java,comBookSpace:com/comBook/*bookSpace:com/book/*:100 companySpace:com/company/*,参考Spring 3.x企业应用开发实战,强大的ConversionService,让很多梦想成真,由于ConversionService在进行类型转换时,可以使用到Bean所在宿主类的上下文信息(包括类结构,注解信息),所以可以实施更加高级的类型转换,如注解驱动的格式化等功能。,public class User DateTimeFormat(pattern=yyyy-MM-dd)private Date birthday;,以上User类,通过一个DateTimeFormat注解,为类型转换提供了一些“额外”的信息,即代表日期的“源字符器”格式是“yyyy-MM-dd”,参考Spring 3.x企业应用开发实战,基于ConversionService体系,定义自定义的类型转换器,Converter,StringToUserConverter,org.springframework.core.convert.converter.Converter,注册自定义转换器:,定义自定义转换器:,参考Spring 3.x企业应用开发实战,格式化:带格式字符串内部对象 相互转换,Formatter,Printer,Parser,FormattingConversionServiceFactoryBean,ConversionService,注册内置的格式化器,使用支持格式化的转换器,值得注解的是,标签内部默认创建的ConversionService实例就是一个FormattingConversionServiceFactoryBean,自动支持如下的格式化注解:NumberFormatter:用于数字类型对象的格式化。CurrencyFormatter:用于货币类型对象的格式化。PercentFormatter:用于百分数数字类型对象的格式化。,参见testhandle82(),参考Spring 3.x企业应用开发实战,数据校验框架,Spring 3.0拥有自己独立的数据校验框架,同时支持JSR 303标准的校验框架。Spring 的DataBinder在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC中,则可直接通过注解驱动的方式进行数据校验。Spring的org.springframework.validation是校验框架所在的包,参考Spring 3.x企业应用开发实战,JSR 303,JSR 303是Java为Bean数据合法性校验所提供的标准框架,它已经包含在Java EE 6.0中。JSR 303通过在Bean属性上标注类似于NotNull、Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。你可以通过http:/jcp.org/en/jsr/detail?id=303了解JSR 303的详细内容。,数据校验框架,会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注Valid注解即可让Spring MVC在完成数据绑定后执行数据校验的工作。,public class User Pattern(regexp=w4,30)private String userName;Length(min=2,max=100)private String realName;Past DateTimeFormat(pattern=yyyy-MM-dd)private Date birthday;DecimalMin(value=1000.00)DecimalMax(value=100000.00)NumberFormat(pattern=#,#.#)private long salary;,注意:Spring本身没有提供JSR 303的实现,所以必须将JSR 303的实现者(如Hibernate Validator)的jar文件放到类路径下,Spring将自动加载并装配好JSR 303的实现者。,参考Spring 3.x企业应用开发实战,如何使用注解驱动的校验,ControllerRequestMapping(/user)public class UserController RequestMapping(value=/handle91)public String handle91(Valid User user,BindingResult bindingResult)if(bindingResult.hasErrors()return/user/register3;else return/user/showUser;,在已经标注了JSR 303注解的表单/命令对象前标注一个Valid,Spring MVC框架在将请求数据绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验。,参考Spring 3.x企业应用开发实战,使用校验功能时,处理方法要如何签名?,Spring MVC是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存在其后的入参中,这个保存校验结果的入参必须是BindingResult或Errors类型,这两个类都位于org.springframework.validation包中。,参考Spring 3.x企业应用开发实战,校验错误信息存放在什么地方?,4.Spring MVC将HttpServletRequest对象数据绑定到处理方法的入参对象中(表单/命令对象);5.将绑定错误信息、检验错误信息都保存到隐含模型中;6.本次请求的对应隐含模型数据存放到HttpServletRequest的属性列表中,暴露给视图对象。,参考Spring 3.x企业应用开发实战,页面如何显示错误信息,注册用户.errorClasscolor:red 用户名:,参考Spring 3.x企业应用开发实战,如何对错误信息进行国际化(1),public class User Pattern(regexp=“w4,30”)假设发生错误 private String userName;,一个属性发生校验错误时,Spring MVC会产生一系列对应的错误码键:,Pattern.user.userNamePattern.userNamePattern.StringPattern,如果userName的Pattern校验规则未通过,则会在“隐含模型”中产生如下的错误键,这些错误键可以作为“国际化消息”的属性键。,参考Spring 3.x企业应用开发实战,如何对错误信息进行国际化(2),我们在conf/i18n/下添加基名为messages的国际化资源,一个是默认的messages.properties,另一个是对应中国大陆的messages_zh_CN.properties。来看一下messages_zh_ CN.properties资源文件的内容:,参考Spring 3.x企业应用开发实战,目录,Spring MVC框架简介,1,数据模型访问结构,数据模型key1=value1key2=value2.,接收请求,处理请求,请求响应,ModelAttribute(user),SessionAttributes,ModelAndView,Map及Model,视图对象,暴露给.,参考Spring 3.x企业应用开发实战,访问数据模型:ModelAndView,RequestMapping(method=RequestMethod.POST)public ModelAndView createUser(User user)userService.createUser(user);ModelAndView mav=new ModelAndView();mav.setViewName(user/createSuccess);mav.addObject(user,user);return mav;,通过ModelAndView,参考Spring 3.x企业应用开发实战,访问数据模型:ModelAttribute,RequestMapping(value=/handle61)public String handle61(ModelAttribute(user)User user)user.setUserId(1000);return/user/createSuccess;,1.使用方式一,Spring MVC将HTTP请求数据绑定到user入参中,然后再将user对象添加到数据模型中。,ModelAttribute(user)public User getUser()User user=new User();user.setUserId(1001);return user;RequestMapping(value=/handle62)public String handle62(ModelAttribute(user)User user)user.setUserName(tom);return/user/showUser;,2.使用方式二,访问UserController中任何一个请求处理方法前,Spring MVC先执行该方法,并将返回值以user为键添加到模型中,在此,模型数据会赋给User的入参,然后再根据HTTP请求消息进一步填充覆盖user对象,参考Spring 3.x企业应用开发实战,访问数据模型:Map及Model,RequestMapping(value=/handle63)public String handle63(ModelMap modelMap)modelMap.addAttribute(testAttr,value1);User user=(User)modelMap.get(user);user.setUserName(tom);return/user/showUser;,org.springframework.ui.Model和java.util.Map:,Spring MVC一旦发现处理方法有Map或Model类型的入参,就会将请求内在的隐含模型对象的引用传给这些入参。,参考Spring 3.x企业应用开发实战,访问数据模型:SessionAttributes,ControllerRequestMapping(/user)SessionAttributes(“user”)public class UserController RequestMapping(value=/handle71)public String handle71(ModelAttribute(“user”)User user)user.setUserName(John);return redirect:/user/handle72.html;RequestMapping(value=/handle72)public String handle72(ModelMap modelMap,SessionStatus sessionStatus)User user=(User)modelMap.get(“user”);if(user!=null)user.setUserName(Jetty);sessionStatus.setComplete();return/user/showUser;,如果希望在多个请求之间共用某个模型属性数据,则可以在控制器类标注一个SessionAttributes,Spring MVC会将模型中对应的属性暂存到HttpSession中:,将处的模型属性自动保存到HttpSession中,读取模型中的数据,让Spring MVC清除本处理器对应的会话属性,参考Spring 3.x企业应用开发实战,一场由SessionAttributes引发的血案.,org.springframework.web.HttpSessionRequiredException:Session attribute user required-not found in session.,对入参标注ModelAttribute(“xxx”)的处理方法,Spring MVC按如下流程处理(handle71(ModelAttribute(“user”)User user)):如果隐含模型拥有名为xxx的属性,将其赋给该入参,再用请求消息填充该入参对象直接返回,否则到2步。如果xxx是会话属性,即在处理类定义处标注了SessionAttributes(xxx),则尝试从会话中获取该属性,并将其赋给该入参,然后再用请求消息填充该入参对象。如果在会话中找不到对应的属性,则抛出HttpSessionRequiredException异常。否则到 3。如果隐含模型不存在xxx属性,且xxx也不是会话属性,则创建入参的对象实例,再用请求消息填充该入参。,参考Spring 3.x企业应用开发实战,如何避免SessionAttributes引发的血案,原来也是小Cakes一张.,ControllerRequestMapping(/user)SessionAttributes(“user”)public class UserController ModelAttribute(user)public User getUser()User user=new User();return user;RequestMapping(value=/handle71)public String handle71(ModelAttribute(“user”)User user).RequestMapping(value=/handle72)public String handle72(ModelMap modelMap,SessionStatus sessionStatus).,该方法会往隐含模型中添加一个名为user的模型属性,目录,Spring MVC框架简介,1,Spring