spring mvc 3.2 使用总结

jopen 11年前
用spring mvc有一段时间了,今天有时间对这个框架的一些使用进行一些总结。
官网上面对spring mvc有一个很详细的demo,地址:
就拿这个例子作为总结的代码。
1.首先介绍一下WebApplicationInitializer,可以通过下面的代码来实现对spring servlet的配置
public class MyWebApplicationInitializer implements WebApplicationInitializer {      @Override      public void onStartup(ServletContext container) {          ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet());          registration.setLoadOnStartup(1);          registration.addMapping("/example/*");      }  }
上面的代码功能等价于
<web-app>      <servlet>          <servlet-name>example</servlet-name>          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>          <load-on-startup>1</load-on-startup>      </servlet>      <servlet-mapping>          <servlet-name>example</servlet-name>          <url-pattern>/example/*</url-pattern>      </servlet-mapping>  </web-app>
2.定义一个controller
@Controller  public class SimpleController {         @RequestMapping("/simple")      public @ResponseBody String simple() {          return "Hello world!";      }     }
这样就完成了一个controller,@RequestMapping( "/simple")定义访问的url,其中可以使用一个参数“method”, 取值可以是RequestMethod.POST,RequestMethod.GET, RequestMethod.DEL等,用来限定访问的方法
3.使用hibernate validation作为验证
定义一个JavaBean:
public class JavaBean {         @NotNull      @Max(5)      private Integer number;         @NotNull      @Future      @DateTimeFormat(iso=ISO.DATE)      private Date date;         public Integer getNumber() {          return number;      }         public void setNumber(Integer number) {          this.number = number;      }         public Date getDate() {          return date;      }         public void setDate(Date date) {          this.date = date;      }     }
定义一个controller:
@Controller  public class ValidationController {         // enforcement of constraints on the JavaBean arg require a JSR-303 provider on the classpath         @RequestMapping("/validate")      public @ResponseBody String validate(@Valid JavaBean bean, BindingResult result) {          if (result.hasErrors()) {              return "Object has validation errors";          } else {              return "No errors";          }      }     }
@Valid说明需要对JavaBean这个参数进行验证,验证的规则由hibernate的validation限定,里面有最基本的@NotNull,@Max,@Min,@reg等等,强大的验证规则。其中BindingResult会返回验证的信息。
4.定义返回值类型:
@Controller  public class MappingController {         @RequestMapping("/mapping/path")      public @ResponseBody String byPath() {          return "Mapped by path!";//最简单的,只定义了request的url      }         @RequestMapping(value="/mapping/path/*", method=RequestMethod.GET)      public @ResponseBody String byPathPattern(HttpServletRequest request) {//拿到的HttpServletRequest基本上什么都可以做到了          return "Mapped by path pattern ('" + request.getRequestURI() + "')";      }         @RequestMapping(value="/mapping/method", method=RequestMethod.GET)      public @ResponseBody String byMethod() {          return "Mapped by path + method";      }         @RequestMapping(value="/mapping/parameter", method=RequestMethod.GET, params="foo")      public @ResponseBody String byParameter() {          return "Mapped by path + method + presence of query parameter!";//定义了必须符合url+方法+参数三个规则的请求才可以被捕捉      }         @RequestMapping(value="/mapping/parameter", method=RequestMethod.GET, params="!foo")      public @ResponseBody String byParameterNegation() {          return "Mapped by path + method + not presence of query parameter!";//params="!foo",不能有foo这个参数的url+方法的请求      }         @RequestMapping(value="/mapping/header", method=RequestMethod.GET, headers="FooHeader=foo")      public @ResponseBody String byHeader() {          return "Mapped by path + method + presence of header!";//附加header的规则      }         @RequestMapping(value="/mapping/header", method=RequestMethod.GET, headers="!FooHeader")      public @ResponseBody String byHeaderNegation() {          return "Mapped by path + method + absence of header!";//没有FooHeader的headers      }         @RequestMapping(value="/mapping/consumes", method=RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)      public @ResponseBody String byConsumes(@RequestBody JavaBean javaBean) {          return "Mapped by path + method + consumable media type (javaBean '" + javaBean + "')";//这个在后面的convert再说      }         @RequestMapping(value="/mapping/produces", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)      public @ResponseBody JavaBean byProducesJson() {//返回JavaBean的json数据          return new JavaBean();      }         @RequestMapping(value="/mapping/produces", method=RequestMethod.GET, produces=MediaType.APPLICATION_XML_VALUE)      public @ResponseBody JavaBean byProducesXml() {//返回JavaBean的xml数据          return new JavaBean();      }  }
5.请求参数的处理
@Controller  @RequestMapping("/data")  public class RequestDataController {         @RequestMapping(value="param", method=RequestMethod.GET)      public @ResponseBody String withParam(@RequestParam String foo) {          return "Obtained 'foo' query parameter value '" + foo + "'";//RequestParam ,获得一个foo的参数,还可以指定参数的名字,例如下面的的      }         @RequestMapping(value="group", method=RequestMethod.GET)      public @ResponseBody String withParamGroup(JavaBean bean) {          return "Obtained parameter group " + bean;      }         @RequestMapping(value="path/{var}", method=RequestMethod.GET)      public @ResponseBody String withPathVariable(@PathVariable String var) {//使用@PathVariable ,参数需要在url中出现例如  xxx/path/hello          return "Obtained 'var' path variable value '" + var + "'";// "hello"这个参数就会被获取到      }         @RequestMapping(value="{path}/simple", method=RequestMethod.GET)// url=/pets;foo=11/simple      public @ResponseBody String withMatrixVariable(@PathVariable String path, @MatrixVariable String foo) {          //path=pets; foo=11          return "Obtained matrix variable 'foo=" + foo + "' from path segment '" + path + "'";      }         @RequestMapping(value="{path1}/{path2}", method=RequestMethod.GET)// url = /42;foo1=11/21;foo2=22      public @ResponseBody String withMatrixVariablesMultiple (              @PathVariable String path1, @MatrixVariable(value="foo", pathVar="path1") String foo1,              @PathVariable String path2, @MatrixVariable(value="foo", pathVar="path2") String foo2) {//类似上面的,分块分解参数             return "Obtained matrix variable foo=" + foo1 + " from path segment '" + path1                  + "' and variable 'foo=" + foo2 + " from path segment '" + path2 + "'";      }        @RequestMapping("/owners/{ownerId}")// 例如url:/owners/42/pets/21.  public class RelativePathUriTemplateController {    @RequestMapping("/pets/{petId}")    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {      // implementation omitted    }  }     // GET /owners/42;q=11/pets/21;q=22  @RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)  public void findPet(      @MatrixVariable(value="q", pathVar="ownerId") int q1,      @MatrixVariable(value="q", pathVar="petId") int q2) {    // q1 == 11    // q2 == 22  }     // GET /owners/42;q=11;r=12/pets/21;q=22;s=23  @RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)//@MatrixVariable 允许组合所有参数成为一个    public void findPet(          @MatrixVariable Map < String, String > matrixVars,          @MatrixVariable(pathVar = "petId" ) Map<String, String> petMatrixVars) {      // matrixVars: [" q " : [11,22], " r " : 12, " s " : 23]      // petMatrixVars: [" q " : 11, " s " : 23]   }  /*  Host                    localhost:8080  Accept                  text/html,application/xhtml+xml,application/xml;q=0.9  Accept-Language         fr,en-gb;q=0.7,en;q=0.3  Accept-Encoding         gzip,deflate  Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7  Keep-Alive              300  */      @RequestMapping(value="header", method=RequestMethod.GET)      public @ResponseBody String withHeader(@RequestHeader String Accept) {//@RequestHeader 可以指定你想或者header中的哪个属性的信息          return "Obtained 'Accept' header '" + Accept + "'";//例如@RequestHeader("Keep-Alive") 获得 300       }         // cookie中有一个值openid_provider=415A4AC178C59DACE0B2C9CA727CDD84 ,也可以指定变量名withCookie(@CookieValue("openid_provider") param);      @RequestMapping(value="cookie", method=RequestMethod.GET)      public @ResponseBody String withCookie(@CookieValue String openid_provider) {          return "Obtained 'openid_provider' cookie '" + openid_provider + "'";      }     /**  *响应流媒体,一般是xml和json数据,可以使用consumes="application/json"指定接受的是json还是xml  *spring可以把POST的数据转换成为一个bean  *例如有个class JavaBean implements Serializable {  *                private Interger id;  *                private Stirng name;  *                private String status;  *                public JavaBean (){  *  *                }  *            }  *    @RequestMapping(value="body", method=RequestMethod.POST)  *    public @ResponseBody String withBody(@RequestBody JavaBean bean) {//响应流媒体  *        return "Posted request body '" + bean + "'";  *    }  *    $.post("/body",{'id':1, 'name': 'people', 'status': 'yes'},function(result){  *        alert(result);  *    });  *    spring 会把post过来的数据组成一个bean,这种方式尽量少用  */      @RequestMapping(value="body", method=RequestMethod.POST)      public @ResponseBody String withBody(@RequestBody String body) {//响应流媒体          return "Posted request body '" + body + "'";      }         @RequestMapping(value="entity", method=RequestMethod.POST)      public @ResponseBody String withEntity(HttpEntity<String> entity) {          return "Posted request body '" + entity.getBody() + "'; headers = " + entity.getHeaders();      }//这个就牛b了,把整个http请求的数据都给你了,有HttpEntity来传递请求数据当然有一个返回应答的数据流类        @RequestMapping("/something")  public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws  UnsupportedEncodingException {    String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"));    byte[] requestBody = requestEntity.getBody();    // do something with request header and body    HttpHeaders responseHeaders = new HttpHeaders();    responseHeaders.set("MyResponseHeader", "MyValue");//设置好header后和body-〉hello world组成ResponseEntity返回    return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);  }      /**      *@RequestBody @ResponseBody分别是HttpEntity和ResponseEntity的去掉header的body部分      */     }
在这里还有一种参数的形式@ModelAttribute,@ModelAttribute的定义有两种,一种是定义在方法上,另一种是定义在参数里,当这个annotation定义在方法上,表示controller返回的是一个JavaBean,当annotation定义在参数里,表示post过来的数据会被转换成为JavaBean。
首先定义一个JavaBean的class:
import java.io.Serializable;     public class Person implements Serializable {         private static final long serialVersionUID = -8333984959652704635L;         private Integer id;      private String firstName;      private String lastName;      private String currency;      private Double money;       ......getter/setter     }
有一个controller:
@Controller  @RequestMapping("EDIT")  public class PetSitesEditController {   @ModelAttribute("people")      public Person getPerson() {          return this.person ;      }//对于这个这个controller来说,其他的方法的返回的jsp页面里面都会有一个people属性被返回,因为@ModelAttribute注解的方法会在这个controller的其他方法调用前被调用  }
在jsp页面中可以访问${ people}这个属性,如果在 @ModelAttribute没有指定属性的名字,那么默认就是person
看下面
有一个jsp页面
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>  <%@ page language="java" contentType="text/html; charset=UTF-8"      pageEncoding="UTF-8"%>  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  <title>Insert title here</title>  </head>  <body>     <h1>Edit Person</h1>     <c:url var="saveUrl" value="/main/edit/${personAttribute.id}" />     <form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">      <table>          <tr>              <td><form:label path="id">Id:</form:label></td>              <td><form:input path="id" disabled="true"/></td>          </tr>             <tr>              <td><form:label path="firstName">First Name:</form:label></td>              <td><form:input path="firstName"/></td>          </tr>             <tr>              <td><form:label path="lastName">Last Name</form:label></td>              <td><form:input path="lastName"/></td>          </tr>             <tr>              <td><form:label path="money">Money</form:label></td>              <td><form:input path="money"/></td>          </tr>             <tr>              <td><form:label path="currency">Currency:</form:label></td>              <td><form:select path="currency"  items="${currencies}"/></td>          </tr>      </table>         <input type="submit" value="Save" />  </form:form>     </body>  </html>
效果
post过来的数据
personAttribute.id = 1, personAttribute. firstName = John....
  @RequestMapping(value = "/edit/{id}", method = RequestMethod.POST)      public String saveEdit(@ModelAttribute("personAttribute") Person person,        @PathVariable Integer id) {        //spring会把post过来的person信息组合成为一个JavaBean,这里还获得了id这个信息  }
controller代码
根据@ModelAttribute两种用法,可以这样子实现
    @Controller      public class PersonController {              @ModelAttribute("person")           public Person getPerson() {              return new Person();          }              @RequestMapping(value = "/new")           public String add(@ModelAttribute("person") Person person) {              return "add person";          }     }  /*  * 访问url  /new  * 首先会执行getPerson的方法,然后再执行add方法,其中person这个bean已经被new出来了,所以在add方法中可以被获得,这个有点像下面要说的convert,但是功能要稍微弱一些。  */
6.convert,这是spring mvc最令我佩服的地方
首先定义一个convert
public class AccountConverter implements Converter<String, Account> {         private AccountManager accountManager;         @Autowired      public AccountConverter(AccountManager accountManager) {//ioc一个accoutManager,用来根据id查出accout          this.accountManager = accountManager;      }         @Override      public Account convert(String id) {          return this.accountManager.getAccount(id);      }     }
下面一个controller:
@Controller  @RequestMapping("/accounts")  public class AccountController {        @RequestMapping(value="/{account}/edit", method = RequestMethod.GET)      public String edit(@PathVariable Account account) {          //操作accout类          return "accounts/edit";      }  }
下面描述了convert的运作过程
7.redirect,重点用法在return中
@Controller  @RequestMapping("/redirect")  public class RedirectController {         private final ConversionService conversionService;         @Inject      public RedirectController(ConversionService conversionService) {          this.conversionService = conversionService;      }         @RequestMapping(value="/uriTemplate", method=RequestMethod.GET)      public String uriTemplate(RedirectAttributes redirectAttrs) {          redirectAttrs.addAttribute("account", "a123");  // Used as URI template variable          redirectAttrs.addAttribute("date", new LocalDate(2011, 12, 31));  // Appended as a query parameter          return "redirect:/redirect/{account}";      }         @RequestMapping(value="/uriComponentsBuilder", method=RequestMethod.GET)      public String uriComponentsBuilder() {          String date = this.conversionService.convert(new LocalDate(2011, 12, 31), String.class);          UriComponents redirectUri = UriComponentsBuilder.fromPath("/redirect/{account}").queryParam("date", date)                  .build().expand("a123").encode();          return "redirect:" + redirectUri.toUriString();      }         @RequestMapping(value="/{account}", method=RequestMethod.GET)      public String show(@PathVariable String account, @RequestParam(required=false) LocalDate date) {          return "redirect/redirectResults";      }     }
8.org.springframework.ui.Model,Model这个类中有一些css的配置,可以允许我们使用,有时可能会用到
@Controller  @RequestMapping("/views/*")  public class ViewsController {         @RequestMapping(value="html", method=RequestMethod.GET)      public String prepare(Model model) {          model.addAttribute("foo", "bar");          model.addAttribute("fruit", "apple");          return "views/html";      }  }
9.文件上传、下载:
上传代码
@Controller  @RequestMapping("/fileupload")  public class FileUploadController {         @ModelAttribute      public void ajaxAttribute(WebRequest request, Model model) {          model.addAttribute("ajaxRequest", AjaxUtils.isAjaxRequest(request));      }         @RequestMapping(method=RequestMethod.GET)      public void fileUploadForm() {      }         @RequestMapping(method=RequestMethod.POST)      public void processUpload(@RequestParam MultipartFile file, Model model) throws IOException {//MultipartFile可以指定变量名@MultipartFile("file001") file          //file.isEmpty(),file.getBytes();,file.getInputStream();          model.addAttribute("message", "File '" + file.getOriginalFilename() + "' uploaded successfully");      }  }

文件下载
@RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)  public void getFile(      @PathVariable("file_name") String fileName,      HttpServletResponse response) {      try {        // get your file as InputStream        InputStream is = ...;        // copy it to response's OutputStream        IOUtils.copy(is, response.getOutputStream());        response.flushBuffer();      } catch (IOException ex) {        log.info("Error writing file to output stream. Filename was '" + fileName + "'");        throw new RuntimeException("IOError writing file to output stream");      }  }//本人比较喜欢这个    @RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)  @ResponseBody  public FileSystemResource getFile(@PathVariable("file_name") String fileName) {      return new FileSystemResource(myService.getFileFor(fileName));  }
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
@WebServlet(urlPatterns = "/demo", asyncSupported = true)  public class AsyncDemoServlet extends HttpServlet {      @Override      public void doGet(HttpServletRequest req, HttpServletResponse resp)      throws IOException, ServletException {          resp.setContentType("text/html;charset=UTF-8");          PrintWriter out = resp.getWriter();          out.println("开始时间:" + new Date() + ".");          out.flush();             //在子线程中执行业务调用,并由其负责输出响应,主线程退出          AsyncContext ctx = req.startAsync();          new Thread(new Executor(ctx)).start();             out.println("结束时间:" + new Date() + ".");          out.flush();      }  }     public class Executor implements Runnable {      private AsyncContext ctx = null;      public Executor(AsyncContext ctx){          this.ctx = ctx;      }         public void run(){          try {              //等待十秒钟,以模拟业务方法的执行              Thread.sleep(10000);              PrintWriter out = ctx.getResponse().getWriter();              out.println("业务处理完毕的时间:" + new Date() + ".");              out.flush();              ctx.complete();          } catch (Exception e) {              e.printStackTrace();          }      }  }
输出的结果:
开始时间: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:
    @RequestMapping("/response-body")      public @ResponseBody Callable<String> callable(final @RequestParam(required=false, defaultValue="true") boolean handled) {           //进行一些与处理之后,把最耗时的业务逻辑部分放到Callable中,注意,如果你需要在new Callable中用到从页面传入的参数,需要在参数前加入final          return new Callable<String>() {              @Override              public String call() throws Exception {                  if(handled){                      Thread.sleep(2000);                  }else{                      Thread.sleep(2000*2);                  }                  return "Callable result";              }          };      }
WebAsyncTask:
(一)对于Callable来说会默认使用SimpleAsyncTaskExecutor类来执行,这个类非常简单而且没有重用线程。而在实际中,你将可能会需要使用AsyncTaskExecutor类来针对你所处的环境进行适当的配置。
(二)在servlet中timeout是一个很重要的问题,servlet容器会尝试重用request和response对象,对于一个timeout但是实际上没有结束的异步请求来说,使用同一个request和response对象影响将无法估量。
WebAsyncTask中有一个setTimeout的选项,其中他的核心原理是callable
    @RequestMapping("/custom-timeout-handling")      public @ResponseBody WebAsyncTask<String> callableWithCustomTimeoutHandling() {             Callable<String> callable = new Callable<String>() {              @Override              public String call() throws Exception {                  Thread.sleep(2000);                  return "Callable result";              }          };             return new WebAsyncTask<String>(1000, callable);//允许指定timeout时间      }
最后一个是DeferredResult<?> ,一个DeferredResult<?> 允许应用程序从一个线程中返回,而何时返回则由线程决定
@Controller  @RequestMapping("/async")  public class DeferredResultController {         private final Queue<DeferredResult<String>> responseBodyQueue = new ConcurrentLinkedQueue<DeferredResult<String>>();         private final Queue<DeferredResult<ModelAndView>> mavQueue = new ConcurrentLinkedQueue<DeferredResult<ModelAndView>>();         private final Queue<DeferredResult<String>> exceptionQueue = new ConcurrentLinkedQueue<DeferredResult<String>>();            @RequestMapping("/deferred-result/response-body")      public @ResponseBody DeferredResult<String> deferredResult() {          DeferredResult<String> result = new DeferredResult<String>();          this.responseBodyQueue.add(result);          return result;      }         @RequestMapping("/deferred-result/model-and-view")      public @ResponseBody DeferredResult<ModelAndView> deferredResultWithView() {          DeferredResult<ModelAndView> result = new DeferredResult<ModelAndView>();          this.mavQueue.add(result);          return result;      }         @RequestMapping("/deferred-result/exception")      public @ResponseBody DeferredResult<String> deferredResultWithException() {          DeferredResult<String> result = new DeferredResult<String>();          this.exceptionQueue.add(result);          return result;      }       //上面三个分别接受了请求之后就return了      // 这里允许我们在其他的线程中处理数据,并且无需实时返回      @RequestMapping("/deferred-result/timeout-value")      public @ResponseBody DeferredResult<String> deferredResultWithTimeoutValue() {             // Provide a default result in case of timeout and override the timeout value          // set in src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml             return new DeferredResult<String>(1000L, "Deferred result after timeout");      }       //创建一个计划任务,每2秒处理一次      @Scheduled(fixedRate=2000)      public void processQueues() {          for (DeferredResult<String> result : this.responseBodyQueue) {              result.setResult("Deferred result");              this.responseBodyQueue.remove(result);          }          for (DeferredResult<String> result : this.exceptionQueue) {              result.setErrorResult(new IllegalStateException("DeferredResult error"));              this.exceptionQueue.remove(result);          }          for (DeferredResult<ModelAndView> result : this.mavQueue) {              result.setResult(new ModelAndView("views/html", "javaBean", new JavaBean("bar", "apple")));              this.mavQueue.remove(result);          }      }         @ExceptionHandler      @ResponseBody      public String handleException(IllegalStateException ex) {          return "Handled exception: " + ex.getMessage();      }     }

11. Interceptor,通过继承HandlerInterceptor, 实现preHandle(..)方法来 定义一些列方法执行前后的动作
官方例子
<mvc:interceptors>      <ref bean="officeHoursInterceptor"/>  </mvc:interceptors>  <bean id="officeHoursInterceptor"            class="samples.TimeBasedAccessInterceptor">          <property name="openingTime" value="9"/>          <property name="closingTime" value="18"/>  </bean>
package samples;  public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {      private int openingTime;      private int closingTime;      public void setOpeningTime(int openingTime) {          this.openingTime = openingTime;      }      public void setClosingTime(int closingTime) {          this.closingTime = closingTime;      }      public boolean preHandle(              HttpServletRequest request,              HttpServletResponse response,              Object handler) throws Exception {          Calendar cal = Calendar.getInstance();          int hour = cal.get(HOUR_OF_DAY);          if (openingTime <= hour && hour < closingTime) {              return true;          } else {              response.sendRedirect("http://host.com/outsideOfficeHours.html");              return false;          }      }  }
HandlerInterceptorAdapter 里面有很多方法,下面是api的部分文档
 
 
那些pre,after和post开头的方法基本可以满足interceptor的各种需要
这里基本结束了spring mvc 3.2的总结,在写这个文章的前几天spring已经出4.0了,看了一下,发现里面开始有websocket的支持了。