Spring 学习笔记(二)-- AbstactController
控制器是 MVC 设计模式的一部分,通过定义服务接口提供对应用行为的访问。控制器获取用户输入的数据并作简单的转换处理成符合服务模块的规则并传入服务模块,经过服务模块的处理给用户返回视图。 Spring 的控制器以抽象的方式实现了这种概念,并且有很多样的控制器在不同情况下可供使用,包括了确定视图的控制器,基于命令的控制器,还有执行向导式逻辑的控制器,在这里仅举几个例子。
Spring 的控制器结构的基础是 org.springframework.web.servlet.mvc.Controller 接口。
public interface Controller { /** * Process the request and return a ModelAndView object which the DispatcherServlet * will render. */ ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws Exception; }
可以看到, Controller 接口定义了一个方法,负责处理一个请求并通过合适的模块返回视图。在 Spring 通过 ModelAndView 和 Controller 实现。 Controller 接口是非常抽象的, Spring 提供许多实现了这个接口的控制器,这些控制器包含了许多功能,在你需要的时候可以使用。而 Controller 接口只是定义了一个方法负责最基本的职责。
Spring 定义的控制器并不是直接实现 Controller 接口,而是实现了 AbstractController,AbstractorController 实现了 Controller 接口。
下面表格是 AbstractController 提供的功能点。
Feature |
说明 |
supportMethods |
表明控制器会接收哪些方法。一般会默认设置GET 和POST ,你也可以修改这些方法。如果一个请求携带的方法是这个控制器不支持的,客户端会被告知,抛出ServletException 。 |
requireSession |
表明控制器是否需要HTTP session 。 |
synchronizeOnSession |
确定是否对HTTP session 实行同步。 |
cacheSeconds |
设置cache 时间。默认是-1 ,则不设置。 |
useExpiresHeader |
为了与HTTP1.0 的‘Expires’ 兼容. 默认true |
useCacheHeader |
为了与HTTP1.0 的‘Cache-Control’ 兼容,默认true |
当你用 AbstractController 作为你控制器的父类的时候,你只需要改写 handleRequestInternal(HttpServletRequest, HttpServletResponse) 方法,实现业务逻辑,并返回 ModelAndView 对象。下面是一个下面是一个例子。
public class SampleController extends AbstractController { public ModelAndView handleRequestInternal( HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mav = new ModelAndView("hello"); mav.addObject("message", "Hello World!"); return mav; } }
上面这个控制器产生了 cache 指令告诉客户端 cache 保持 2 分钟。我们看到一个编码的坏习惯,就是返回视图的时候用了硬编码。
上面讲到的是从 spring-reference.pdf 翻译过来的,部分加上自己的理解。现在再结合源码可以更深入的了解到上面的机制是怎么运行的。
首先我们现看一下 AbstractController 的类层次图。
似比较复杂。一步一步来看。
现看 AbstractController 类。 Spring 定义的所有控制器都继承于它。它有一个属性
private boolean synchronizeOnSession = false ;
这个属性的作用在上面有提到过。上面提到的其他属性在 AbstractController 的父类定义。
另外就是实现了 Controller 接口的方法:
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // Delegate to WebContentGenerator for checking and preparing. checkAndPrepare(request, response, this instanceof LastModified); // Execute handleRequestInternal in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return handleRequestInternal(request, response); } } } return handleRequestInternal(request, response); }
首先是委托 WebContentGenerator 作一些准备的工作。作一些什么样的工作呢?我们再看 WebContentGenerator 的 checkAndPrepare () 方法。注意一下, WebContentGenerator 初始化时将默认的处理方法为 get,post,head 。代码如下:
public WebContentGenerator(boolean restrictDefaultSupportedMethods) { if (restrictDefaultSupportedMethods) { this.supportedMethods = new HashSet(4); this.supportedMethods.add(METHOD_GET); this.supportedMethods.add(METHOD_HEAD); this.supportedMethods.add(METHOD_POST); } }
restrictDefaultSupportedMethods 默认为 true 。
protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) { if (this.useExpiresHeader) { // HTTP 1.0 header response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L); } if (this.useCacheControlHeader) { // HTTP 1.1 header String headerValue = "max-age=" + seconds; if (mustRevalidate) { headerValue += ", must-revalidate"; } response.setHeader(HEADER_CACHE_CONTROL, headerValue); } }
this . useExpiresHeader 处理根据上文知道是为了兼容 HTTP1.0.
this . useCacheControlHeader 如果用户有使用 cache 指令,则进行处理,用 Servlet 最基本的 response 设置。
protected final void checkAndPrepare( HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified) throws ServletException { // Check whether we should support the request method. String method = request.getMethod(); if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException( method, StringUtils.toStringArray(this.supportedMethods)); } // Check whether a session is required. if (this.requireSession) { if (request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } } // Do declarative cache control. // Revalidate if the controller supports last-modified. applyCacheSeconds(response, cacheSeconds, lastModified); }
这段代码结合上文就可以很好的理解 supportMethods 抛出异常的相关情况。
applyCacheSeconds(response,cacheSeconds,lastModified) 方法中,参数 lastModified 是在 ApplicationController 的 handleRequest() ,有这样传入 this instanceof LastModified ,判断这个控制类是否实现了 LastModified 接口 , 在这个方法实际上作了 cacheSeconds 属性的工作。
常量 HEADER_CACHE_CONTROL =“ Cache-Control” ,这是对 cache 的处理。
现在回到 AbstractController 的 handleRequests() 方法, checkAndPrepare ()处理完之后,再根据 synchronizeOnSession 判断是否需要同步。 handleRequestInternal(request, response) 方法是真正处理逻辑的地方,这也是前面一个例子为什么要重写这个方法的原因。