spring mvc

本文来自:http://my.oschina.net/541996928/blog/145897

用spring mvc有一段时间了,今天有时间对这个框架的一些使用进行一些总结。
官网上面对spring mvc有一个很详细的demo,地址:
https://github.com/SpringSource/spring-mvc-showcase
就拿这个例子作为总结的代码。
1.首先介绍一下WebApplicationInitializer,可以通过下面的代码来实现对spring servlet的配置
1 public classMyWebApplicationInitializer implementsWebApplicationInitializer {
2     @Override
3     publicvoid onStartup(ServletContext container) {
4         ServletRegistration.Dynamic registration = container.addServlet("dispatcher",new DispatcherServlet());
5         registration.setLoadOnStartup(1);
6         registration.addMapping("/example/*");
7     }
8 }
上面的代码功能等价于
01 <web-app>
02     <servlet>
03         <servlet-name>example</servlet-name>
04         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
05         <load-on-startup>1</load-on-startup>
06     </servlet>
07     <servlet-mapping>
08         <servlet-name>example</servlet-name>
09         <url-pattern>/example/*</url-pattern>
10     </servlet-mapping>
11 </web-app>
2.定义一个controller
1 @Controller
2 public classSimpleController {
3   
4     @RequestMapping("/simple")
5     public@ResponseBody String simple() {
6         return"Hello world!";
7     }
8   
9 }
这样就完成了一个controller,@RequestMapping( "/simple")定义访问的url,其中可以使用一个参数“method”, 取值可以是RequestMethod.POST,RequestMethod.GET, RequestMethod.DEL等,用来限定访问的方法
 
3.使用hibernate validation作为验证
定义一个JavaBean:
01 public classJavaBean {
02   
03     @NotNull
04     @Max(5)
05     privateInteger number;
06   
07     @NotNull
08     @Future
09     @DateTimeFormat(iso=ISO.DATE)
10     privateDate date;
11   
12     publicInteger getNumber() {
13         returnnumber;
14     }
15   
16     publicvoid setNumber(Integer number) {
17         this.number = number;
18     }
19   
20     publicDate getDate() {
21         returndate;
22     }
23   
24     publicvoid setDate(Date date) {
25         this.date = date;
26     }
27   
28 }
定义一个controller:
01 @Controller
02 public classValidationController {
03   
04     // enforcement of constraints on the JavaBean arg require a JSR-303 provider on the classpath
05   
06     @RequestMapping("/validate")
07     public@ResponseBody String validate(@ValidJavaBean bean, BindingResult result) {
08         if(result.hasErrors()) {
09             return"Object has validation errors";
10         } else {
11             return"No errors";
12         }
13     }
14   
15 }
@Valid说明需要对JavaBean这个参数进行验证,验证的规则由hibernate的validation限定,里面有最基本的@NotNull,@Max,@Min,@reg等等,强大的验证规则。其中BindingResult会返回验证的信息。
 
4.定义返回值类型:
01 @Controller
02 public classMappingController {
03   
04     @RequestMapping("/mapping/path")
05     public@ResponseBody String byPath() {
06         return"Mapped by path!";//最简单的,只定义了request的url
07     }
08   
09     @RequestMapping(value="/mapping/path/*", method=RequestMethod.GET)
10     public@ResponseBody String byPathPattern(HttpServletRequest request) {//拿到的HttpServletRequest基本上什么都可以做到了
11         return"Mapped by path pattern ('" + request.getRequestURI() +"')";
12     }
13   
14     @RequestMapping(value="/mapping/method", method=RequestMethod.GET)
15     public@ResponseBody String byMethod() {
16         return"Mapped by path + method";
17     }
18   
19     @RequestMapping(value="/mapping/parameter", method=RequestMethod.GET, params="foo")
20     public@ResponseBody String byParameter() {
21         return"Mapped by path + method + presence of query parameter!";//定义了必须符合url+方法+参数三个规则的请求才可以被捕捉
22     }
23   
24     @RequestMapping(value="/mapping/parameter", method=RequestMethod.GET, params="!foo")
25     public@ResponseBody String byParameterNegation() {
26         return"Mapped by path + method + not presence of query parameter!";//params="!foo",不能有foo这个参数的url+方法的请求
27     }
28   
29     @RequestMapping(value="/mapping/header", method=RequestMethod.GET, headers="FooHeader=foo")
30     public@ResponseBody String byHeader() {
31         return"Mapped by path + method + presence of header!";//附加header的规则
32     }
33   
34     @RequestMapping(value="/mapping/header", method=RequestMethod.GET, headers="!FooHeader")
35     public@ResponseBody String byHeaderNegation() {
36         return"Mapped by path + method + absence of header!";//没有FooHeader的headers
37     }
38   
39     @RequestMapping(value="/mapping/consumes", method=RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)
40     public@ResponseBody String byConsumes(@RequestBodyJavaBean javaBean) {
41         return"Mapped by path + method + consumable media type (javaBean '"+ javaBean + "')";//这个在后面的convert再说
42     }
43   
44     @RequestMapping(value="/mapping/produces", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
45     public@ResponseBody JavaBean byProducesJson() {//返回JavaBean的json数据
46         returnnew JavaBean();
47     }
48   
49     @RequestMapping(value="/mapping/produces", method=RequestMethod.GET, produces=MediaType.APPLICATION_XML_VALUE)
50     public@ResponseBody JavaBean byProducesXml() {//返回JavaBean的xml数据
51         returnnew JavaBean();
52     }
53 }
5.请求参数的处理
001 @Controller
002 @RequestMapping("/data")
003 public classRequestDataController {
004   
005     @RequestMapping(value="param", method=RequestMethod.GET)
006     public@ResponseBody String withParam(@RequestParamString foo) {
007         return"Obtained 'foo' query parameter value '" + foo + "'";//RequestParam ,获得一个foo的参数,还可以指定参数的名字,例如下面的的
008     }
009   
010     @RequestMapping(value="group", method=RequestMethod.GET)
011     public@ResponseBody String withParamGroup(JavaBean bean) {
012         return"Obtained parameter group " + bean;
013     }
014   
015     @RequestMapping(value="path/{var}", method=RequestMethod.GET)
016     public@ResponseBody String withPathVariable(@PathVariableString var) {//使用@PathVariable ,参数需要在url中出现例如  xxx/path/hello
017         return"Obtained 'var' path variable value '" + var + "'";// "hello"这个参数就会被获取到
018     }
019   
020     @RequestMapping(value="{path}/simple", method=RequestMethod.GET)// url=/pets;foo=11/simple
021     public@ResponseBody String withMatrixVariable(@PathVariableString path, @MatrixVariableString foo) {
022         //path=pets; foo=11
023         return"Obtained matrix variable 'foo=" + foo + "' from path segment '" + path + "'";
024     }
025   
026     @RequestMapping(value="{path1}/{path2}", method=RequestMethod.GET)// url = /42;foo1=11/21;foo2=22
027     public@ResponseBody String withMatrixVariablesMultiple (
028             @PathVariableString path1, @MatrixVariable(value="foo", pathVar="path1") String foo1,
029             @PathVariableString path2, @MatrixVariable(value="foo", pathVar="path2") String foo2) {//类似上面的,分块分解参数
030   
031         return"Obtained matrix variable foo=" + foo1 +" from path segment '" + path1
032                 +"' and variable 'foo=" + foo2 +" from path segment '" + path2 +"'";
033     }
034      
035 @RequestMapping("/owners/{ownerId}")// 例如url:/owners/42/pets/21.
036 public classRelativePathUriTemplateController {
037   @RequestMapping("/pets/{petId}")
038   publicvoid findPet(@PathVariableString ownerId, @PathVariableString petId, Model model) {
039     // implementation omitted
040   }
041 }
042   
043 // GET /owners/42;q=11/pets/21;q=22
044 @RequestMapping(value ="/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
045 public voidfindPet(
046     @MatrixVariable(value="q", pathVar="ownerId")int q1,
047     @MatrixVariable(value="q", pathVar="petId")int q2) {
048   // q1 == 11
049   // q2 == 22
050 }
051   
052 // GET /owners/42;q=11;r=12/pets/21;q=22;s=23
053 @RequestMapping(value ="/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)//@MatrixVariable 允许组合所有参数成为一个
054   publicvoid findPet(
055         @MatrixVariableMap < String, String > matrixVars,
056         @MatrixVariable(pathVar ="petId" ) Map<String, String> petMatrixVars) {
057     // matrixVars: [" q " : [11,22], " r " : 12, " s " : 23]
058     // petMatrixVars: [" q " : 11, " s " : 23]
059  }
060 /*
061 Host                    localhost:8080
062 Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
063 Accept-Language         fr,en-gb;q=0.7,en;q=0.3
064 Accept-Encoding         gzip,deflate
065 Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
066 Keep-Alive              300
067 */
068     @RequestMapping(value="header", method=RequestMethod.GET)
069     public @ResponseBody String withHeader(@RequestHeader String Accept) {//@RequestHeader 可以指定你想或者header中的哪个属性的信息
070         return "Obtained 'Accept' header '" + Accept + "'";//例如@RequestHeader("Keep-Alive") 获得 300
071     }
072   
073     // cookie中有一个值openid_provider=415A4AC178C59DACE0B2C9CA727CDD84 ,也可以指定变量名withCookie(@CookieValue("openid_provider") param);
074     @RequestMapping(value="cookie", method=RequestMethod.GET)
075     public @ResponseBody String withCookie(@CookieValue String openid_provider) {
076         return "Obtained 'openid_provider' cookie '" + openid_provider + "'";
077     }
078   
079 /**
080 *响应流媒体,一般是xml和json数据,可以使用consumes="application/json"指定接受的是json还是xml
081 *spring可以把POST的数据转换成为一个bean
082 *例如有个class JavaBean implements Serializable {
083 *                private Interger id;
084 *                private Stirng name;
085 *                private String status;
086 *                public JavaBean (){
087 *
088 *                }
089 *            }
090 *    @RequestMapping(value="body", method=RequestMethod.POST)
091 *    public @ResponseBody String withBody(@RequestBody JavaBean bean) {//响应流媒体
092 *        return "Posted request body '" + bean + "'";
093 *    }
094 *    $.post("/body",{'id':1, 'name': 'people', 'status': 'yes'},function(result){
095 *        alert(result);
096 *    });
097 *    spring 会把post过来的数据组成一个bean,这种方式尽量少用
098 */
099     @RequestMapping(value="body", method=RequestMethod.POST)
100     public @ResponseBody String withBody(@RequestBody String body) {//响应流媒体
101         return "Posted request body '" + body + "'";
102     }
103   
104     @RequestMapping(value="entity", method=RequestMethod.POST)
105     public @ResponseBody String withEntity(HttpEntity<String> entity) {
106         return "Posted request body '" + entity.getBody() + "'; headers = " + entity.getHeaders();
107     }//这个就牛b了,把整个http请求的数据都给你了,有HttpEntity来传递请求数据当然有一个返回应答的数据流类
108      
109 @RequestMapping("/something")
110 public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws
111 UnsupportedEncodingException {
112   String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));
113   byte[] requestBody = requestEntity.getBody();
114   // do something with request header and body
115   HttpHeaders responseHeaders = new HttpHeaders();
116   responseHeaders.set("MyResponseHeader", "MyValue");//设置好header后和body-〉hello world组成ResponseEntity返回
117   return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
118 }
119     /**
120     *@RequestBody @ResponseBody分别是HttpEntity和ResponseEntity的去掉header的body部分
121     */
122   
123 }
在这里还有一种参数的形式@ModelAttribute,@ModelAttribute的定义有两种,一种是定义在方法上,另一种是定义在参数里,当这个annotation定义在方法上,表示controller返回的是一个JavaBean,当annotation定义在参数里,表示post过来的数据会被转换成为JavaBean。
首先定义一个JavaBean的class:
01 import java.io.Serializable;
02   
03 public classPerson implements Serializable {
04   
05     privatestatic final long serialVersionUID = -8333984959652704635L;
06   
07     privateInteger id;
08     privateString firstName;
09     privateString lastName;
10     privateString currency;
11     privateDouble money;
12   
13   ......getter/setter
14   
15 }
有一个controller:
1 @Controller
2 @RequestMapping("EDIT")
3 public classPetSitesEditController {
4  @ModelAttribute("people")
5     publicPerson getPerson() {
6         returnthis.person ;
7     }//对于这个这个controller来说,其他的方法的返回的jsp页面里面都会有一个people属性被返回,因为@ModelAttribute注解的方法会在这个controller的其他方法调用前被调用
8 }
在jsp页面中可以访问${ people}这个属性,如果在 @ModelAttribute没有指定属性的名字,那么默认就是person
看下面
有一个jsp页面
01 <%@ taglib uri="http://java.sun.com/jsp/jstl/core"prefix="c" %>
02 <%@ taglib uri="http://www.springframework.org/tags/form"prefix="form" %>
03 <%@ page language="java"contentType="text/html; charset=UTF-8"
04     pageEncoding="UTF-8"%>
05 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
06 <html>
07 <head>
08 <meta http-equiv="Content-Type"content="text/html; charset=UTF-8">
09 <title>Insert title here</title>
10 </head>
11 <body>
12   
13 <h1>Edit Person</h1>
14   
15 <c:url var="saveUrl"value="/main/edit/${personAttribute.id}"/>
16   
17 <form:form modelAttribute="personAttribute"method="POST" action="${saveUrl}">
18     <table>
19         <tr>
20             <td><form:label path="id">Id:</form:label></td>
21             <td><form:input path="id"disabled="true"/></td>
22         </tr>
23   
24         <tr>
25             <td><form:label path="firstName">First Name:</form:label></td>
26             <td><form:input path="firstName"/></td>
27         </tr>
28   
29         <tr>
30             <td><form:label path="lastName">Last Name</form:label></td>
31             <td><form:input path="lastName"/></td>
32         </tr>
33   
34         <tr>
35             <td><form:label path="money">Money</form:label></td>
36             <td><form:input path="money"/></td>
37         </tr>
38   
39         <tr>
40             <td><form:label path="currency">Currency:</form:label></td>
41             <td><form:select path="currency" items="${currencies}"/></td>
42         </tr>
43     </table>
44   
45     <input type="submit"value="Save" />
46 </form:form>
47   
48 </body>
49 </html>
效果
spring mvc_第1张图片
post过来的数据
personAttribute.id = 1, personAttribute. firstName = John....
1   @RequestMapping(value ="/edit/{id}", method = RequestMethod.POST)
2     publicString saveEdit(@ModelAttribute("personAttribute") Person person,
3       @PathVariableInteger id) {
4       //spring会把post过来的person信息组合成为一个JavaBean,这里还获得了id这个信息
5 }
controller代码
根据@ModelAttribute两种用法,可以这样子实现
01     @Controller
02     publicclass PersonController {
03   
04         @ModelAttribute("person")
05         publicPerson getPerson() {
06            returnnew Person();
07         }
08   
09         @RequestMapping(value ="/new")
10         publicString add(@ModelAttribute("person") Person person) {
11            return"add person";
12         }
13    }
14 /*
15 * 访问url  /new
16 * 首先会执行getPerson的方法,然后再执行add方法,其中person这个bean已经被new出来了,所以在add方法中可以被获得,这个有点像下面要说的convert,但是功能要稍微弱一些。
17 */
6.convert,这是spring mvc最令我佩服的地方
首先定义一个convert
01 public classAccountConverter implementsConverter<String, Account> {
02   
03     privateAccountManager accountManager;
04   
05     @Autowired
06     publicAccountConverter(AccountManager accountManager) {//ioc一个accoutManager,用来根据id查出accout
07         this.accountManager = accountManager;
08     }
09   
10     @Override
11     publicAccount convert(String id) {
12         returnthis.accountManager.getAccount(id);
13     }
14   
15 }
下面一个controller:
01 @Controller
02 @RequestMapping("/accounts")
03 public classAccountController {
04  
05     @RequestMapping(value="/{account}/edit", method = RequestMethod.GET)
06     publicString edit(@PathVariable Account account) {
07         //操作accout类
08         return"accounts/edit";
09     }
10 }
下面描述了convert的运作过程
spring mvc_第2张图片
7.redirect,重点用法在return中
01 @Controller
02 @RequestMapping("/redirect")
03 public classRedirectController {
04   
05     privatefinal ConversionService conversionService;
06   
07     @Inject
08     publicRedirectController(ConversionService conversionService) {
09         this.conversionService = conversionService;
10     }
11   
12     @RequestMapping(value="/uriTemplate", method=RequestMethod.GET)
13     publicString uriTemplate(RedirectAttributes redirectAttrs) {
14         redirectAttrs.addAttribute("account","a123");  // Used as URI template variable
15         redirectAttrs.addAttribute("date",new LocalDate(2011,12, 31)); // Appended as a query parameter
16         return"redirect:/redirect/{account}";
17     }
18   
19     @RequestMapping(value="/uriComponentsBuilder", method=RequestMethod.GET)
20     publicString uriComponentsBuilder() {
21         String date =this.conversionService.convert(newLocalDate(2011,12, 31), String.class);
22         UriComponents redirectUri = UriComponentsBuilder.fromPath("/redirect/{account}").queryParam("date", date)
23                 .build().expand("a123").encode();
24         return"redirect:" + redirectUri.toUriString();
25     }
26   
27     @RequestMapping(value="/{account}", method=RequestMethod.GET)
28     publicString show(@PathVariable String account, @RequestParam(required=false) LocalDate date) {
29         return"redirect/redirectResults";
30     }
31   
32 }
8.org.springframework.ui.Model,Model这个类中有一些css的配置,可以允许我们使用,有时可能会用到
01 @Controller
02 @RequestMapping("/views/*")
03 public classViewsController {
04   
05     @RequestMapping(value="html", method=RequestMethod.GET)
06     publicString prepare(Model model) {
07         model.addAttribute("foo","bar");
08         model.addAttribute("fruit","apple");
09         return"views/html";
10     }
11 }
9.文件上传、下载:
上传代码
01 @Controller
02 @RequestMapping("/fileupload")
03 public classFileUploadController {
04   
05     @ModelAttribute
06     publicvoid ajaxAttribute(WebRequest request, Model model) {
07         model.addAttribute("ajaxRequest", AjaxUtils.isAjaxRequest(request));
08     }
09   
10     @RequestMapping(method=RequestMethod.GET)
11     publicvoid fileUploadForm() {
12     }
13   
14     @RequestMapping(method=RequestMethod.POST)
15     publicvoid processUpload(@RequestParamMultipartFile file, Model model) throwsIOException {//MultipartFile可以指定变量名@MultipartFile("file001") file
16         //file.isEmpty(),file.getBytes();,file.getInputStream();
17         model.addAttribute("message","File '" + file.getOriginalFilename() +"' uploaded successfully");
18     }
19 }

文件下载
01 @RequestMapping(value ="/files/{file_name}", method = RequestMethod.GET)
02 public voidgetFile(
03     @PathVariable("file_name") String fileName,
04     HttpServletResponse response) {
05     try{
06       // get your file as InputStream
07       InputStream is = ...;
08       // copy it to response's OutputStream
09       IOUtils.copy(is, response.getOutputStream());
10       response.flushBuffer();
11     } catch (IOException ex) {
12       log.info("Error writing file to output stream. Filename was '"+ fileName + "'");
13       thrownew RuntimeException("IOError writing file to output stream");
14     }
15 }//本人比较喜欢这个
16  
17 @RequestMapping(value ="/files/{file_name}", method = RequestMethod.GET)
18 @ResponseBody
19 public FileSystemResource getFile(@PathVariable("file_name") String fileName) {
20     returnnew FileSystemResource(myService.getFileFor(fileName));
21 }
10.ajax 部分,有关 Servlet 3 async processing feature:
 首先说一下Servlet 3 的一些特性, 一个普通 Servlet 的主要工作流程大致如下:首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;接着,调用业务接口的某些方法,以完成业务处理;最后,根据处理的结果提交响应,Servlet 线程结束。其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源
 
现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:首先,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
一个简单的demo
01 @WebServlet(urlPatterns ="/demo", asyncSupported = true)
02 public classAsyncDemoServlet extends HttpServlet {
03     @Override
04     publicvoid doGet(HttpServletRequest req, HttpServletResponse resp)
05     throwsIOException, ServletException {
06         resp.setContentType("text/html;charset=UTF-8");
07         PrintWriter out = resp.getWriter();
08         out.println("开始时间:"+ new Date() + ".");
09         out.flush();
10   
11         //在子线程中执行业务调用,并由其负责输出响应,主线程退出
12         AsyncContext ctx = req.startAsync();
13         newThread(new Executor(ctx)).start();
14   
15         out.println("结束时间:"+ new Date() + ".");
16         out.flush();
17     }
18 }
19   
20 public classExecutor implements Runnable {
21     privateAsyncContext ctx = null;
22     publicExecutor(AsyncContext ctx){
23         this.ctx = ctx;
24     }
25   
26     publicvoid run(){
27         try{
28             //等待十秒钟,以模拟业务方法的执行
29             Thread.sleep(10000);
30             PrintWriter out = ctx.getResponse().getWriter();
31             out.println("业务处理完毕的时间:"+ new Date() + ".");
32             out.flush();
33             ctx.complete();
34         } catch (Exception e) {
35             e.printStackTrace();
36         }
37     }
38 }
输出的结果:
开始时间:Thu Jul 27 09:49:30 CST 2013
开始时间:Thu Jul 27 09:49:30 CST 2013
业务处理完毕的时间: Thu Jul 27 09:49:40 CST 2013
 
下面是spring  mvc 3.2 对其的支持。对于spring mvc 里面有三种方式的支持,分别是Callable,DeferredResult,WebAsyncTask
Callable:
01 @RequestMapping("/response-body")
02 public @ResponseBodyCallable<String> callable(final@RequestParam(required=false, defaultValue="true")boolean handled) {
03      //进行一些与处理之后,把最耗时的业务逻辑部分放到Callable中,注意,如果你需要在new Callable中用到从页面传入的参数,需要在参数前加入final
04     returnnew Callable<String>() {
05         @Override
06         publicString call() throws Exception {
07             if(handled){
08                 Thread.sleep(2000);
09             }else{
10                 Thread.sleep(2000*2);
11             }
12             return"Callable result";
13         }
14     };
15 }
WebAsyncTask:
(一)对于Callable来说会默认使用SimpleAsyncTaskExecutor类来执行,这个类非常简单而且没有重用线程。而在实际中,你将可能会需要使用AsyncTaskExecutor类来针对你所处的环境进行适当的配置。
(二)在servlet中timeout是一个很重要的问题,servlet容器会尝试重用request和response对象,对于一个timeout但是实际上没有结束的异步请求来说,使用同一个request和response对象影响将无法估量。
WebAsyncTask中有一个setTimeout的选项,其中他的核心原理是callable
01 @RequestMapping("/custom-timeout-handling")
02 public @ResponseBodyWebAsyncTask<String> callableWithCustomTimeoutHandling() {
03  
04     Callable<String> callable =new Callable<String>() {
05         @Override
06         publicString call() throws Exception {
07             Thread.sleep(2000);
08             return"Callable result";
09         }
10     };
11  
12     returnnew WebAsyncTask<String>(1000, callable);//允许指定timeout时间
13 }
最后一个是DeferredResult<?> ,一个DeferredResult<?> 允许应用程序从一个线程中返回,而何时返回则由线程决定
01 @Controller
02 @RequestMapping("/async")
03 public classDeferredResultController {
04   
05     privatefinal Queue<DeferredResult<String>> responseBodyQueue =new ConcurrentLinkedQueue<DeferredResult<String>>();
06   
07     privatefinal Queue<DeferredResult<ModelAndView>> mavQueue =new ConcurrentLinkedQueue<DeferredResult<ModelAndView>>();
08   
09     privatefinal Queue<DeferredResult<String>> exceptionQueue =new ConcurrentLinkedQueue<DeferredResult<String>>();
10   
11   
12     @RequestMapping("/deferred-result/response-body")
13     public@ResponseBody DeferredResult<String> deferredResult() {
14         DeferredResult<String> result =new DeferredResult<String>();
15         this.responseBodyQueue.add(result);
16         returnresult;
17     }
18   
19     @RequestMapping("/deferred-result/model-and-view")
20     public@ResponseBody DeferredResult<ModelAndView> deferredResultWithView() {
21         DeferredResult<ModelAndView> result =new DeferredResult<ModelAndView>();
22         this.mavQueue.add(result);
23         returnresult;
24     }
25   
26     @RequestMapping("/deferred-result/exception")
27     public@ResponseBody DeferredResult<String> deferredResultWithException() {
28         DeferredResult<String> result =new DeferredResult<String>();
29         this.exceptionQueue.add(result);
30         returnresult;
31     }
32      //上面三个分别接受了请求之后就return了
33     // 这里允许我们在其他的线程中处理数据,并且无需实时返回
34     @RequestMapping("/deferred-result/timeout-value")
35     public@ResponseBody DeferredResult<String> deferredResultWithTimeoutValue() {
36   
37         // Provide a default result in case of timeout and override the timeout value
38         // set in src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
39   
40         returnnew DeferredResult<String>(1000L,"Deferred result after timeout");
41     }
42      //创建一个计划任务,每2秒处理一次
43     @Scheduled(fixedRate=2000)
44     publicvoid processQueues() {
45         for(DeferredResult<String> result : this.responseBodyQueue) {
46             result.setResult("Deferred result");
47             this.responseBodyQueue.remove(result);
48         }
49         for(DeferredResult<String> result : this.exceptionQueue) {
50             result.setErrorResult(newIllegalStateException("DeferredResult error"));
51             this.exceptionQueue.remove(result);
52         }
53         for(DeferredResult<ModelAndView> result : this.mavQueue) {
54             result.setResult(newModelAndView("views/html","javaBean", newJavaBean("bar","apple")));
55             this.mavQueue.remove(result);
56         }
57     }
58   
59     @ExceptionHandler
60     @ResponseBody
61     publicString handleException(IllegalStateException ex) {
62         return"Handled exception: " + ex.getMessage();
63     }
64   
65 }

11. Interceptor,通过继承HandlerInterceptor, 实现preHandle(..)方法来 定义一些列方法执行前后的动作
官方例子
1 <mvc:interceptors>
2     <refbean="officeHoursInterceptor"/>
3 </mvc:interceptors>
4 <beanid="officeHoursInterceptor"
5           class="samples.TimeBasedAccessInterceptor">
6         <propertyname="openingTime"value="9"/>
7         <propertyname="closingTime"value="18"/>
8 </bean>
01 package samples;
02 public classTimeBasedAccessInterceptor extendsHandlerInterceptorAdapter {
03     privateint openingTime;
04     privateint closingTime;
05     publicvoid setOpeningTime(intopeningTime) {
06         this.openingTime = openingTime;
07     }
08     publicvoid setClosingTime(intclosingTime) {
09         this.closingTime = closingTime;
10     }
11     publicboolean preHandle(
12             HttpServletRequest request,
13             HttpServletResponse response,
14             Object handler)throws Exception {
15         Calendar cal = Calendar.getInstance();
16         inthour = cal.get(HOUR_OF_DAY);
17         if(openingTime <= hour && hour < closingTime) {
18             returntrue;
19         } else {
20             response.sendRedirect("http://host.com/outsideOfficeHours.html");
21             returnfalse;
22         }
23     }
24 }
HandlerInterceptorAdapter 里面有很多方法,下面是api的部分文档
  spring mvc_第3张图片
 
那些pre,after和post开头的方法基本可以满足interceptor的各种需要
 
这里基本结束了spring mvc 3.2的总结,在写这个文章的前几天spring已经出4.0了,看了一下,发现里面开始有websocket的支持了。

你可能感兴趣的:(spring mvc)