SpringMVC之源码分析--Controller(一)
概述
Controller是Spring MVC为我们提供的基础的控制器接口,和HttpServlet一样,接收request和response参数处理用户请求,并返回ModelAndView,从概念上可以类比Struts的Action。
Controller主要实现的如下功能:
- 接收并处理用户请求
- 调用业务方法
- 返回ModelAndView
基于Controller开发请求处理Handler的特点:
- 需要实现Controller接口
- 请求参数需从请求request中获取
- 请求处理Handler与Spring深度耦合
就目前项目开发,几乎不会使用此方式进行开发,除非维护一些历史项目。目前是基于注解进行开发,从Spring2.5及之后开始支持。编写本章内容主要是基于知识点的全面性来考虑,大家可以了解了解。
本系列文章是基于Spring5.0.5RELEASE。
接口Controller
Spring提供的Controller接口,定义了一个方法,作用就是处理用户请求,源码如下:
package org.springframework.web.servlet.mvc; public interface Controller { /** *接收request和response参数,处理用户请求 *参数从request中获取 *方法返回ModelAndView,Model为模型数据,View为视图 */ ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
Spring提供的实现类
Spring提供了如下实现:
AbstractController 控制浏览器缓存、支持的请求方法等等
ServletForwardingController 将Spring Handler接收的请求转发给一个Servlet去执行
ParameterizableViewController 参数化视图控制器,根据参数的逻辑视图名直接选择需要展示的视图
AbstractUrlViewController 根据请求URL路径直接转化为逻辑视图名的支持基类
UrlFilenameViewController 将请求URL路径转换为逻辑视图名并返回的实现类ServletWrappingController Servlet包装控制器
以上是Spring MVC为我们提供Controller接口的默认实现,下面我们接着分析这些实现类。
AbstractController
该抽象类继承WebContentGenerator并实现Controller接口,其中WebContentGenerator类用于浏览器缓存控制、自定义Controller支持的请求方法类型(默认支持:GET/HEAD/POST)等。
WebContentGenerator类的主要属性如下:
/** 支持的请求方法类型,默认支持:GET、HEAD、POST */ @Nullable private Set<String> supportedMethods; @Nullable private String allowHeader; /** 当前请求是否必须有session */ private boolean requireSession = false; @Nullable private CacheControl cacheControl; /** 缓存过期时间,正数表示需要缓存,负数表示不做任何事情 */ private int cacheSeconds = -1; @Nullable private String[] varyByRequestHeaders; /** 是否使用HTTP1.0协议过期响应头:如果true则会在响应头添加“Expires:”;需要配合cacheSeconds使用 */ private boolean useExpiresHeader = false; /** 是否使用HTTP1.1协议的缓存控制响应头,如果true则会在响应头添加;需要配合cacheSeconds使用 */ private boolean useCacheControlHeader = true; /** 是否使用HTTP 1.1协议的缓存控制响应头,如果true则会在响应头添加;需要配合cacheSeconds使用 */ private boolean useCacheControlNoStore = true;
AbstractController类源码如下:
/** 表示该控制器是否在执行时同步session,从而保证该会话的用户串行访问该控制器 */ private boolean synchronizeOnSession = false; /** *实现Controller接口的handleRequest方法 */ @Override @Nullable public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { if (HttpMethod.OPTIONS.matches(request.getMethod())) { response.setHeader("Allow", getAllowHeader()); return null; } // 检查是否支持请求方法以及必须的session checkRequest(request); // 根据设置准备response prepareResponse(response); // 如果必要顺序执行handleRequestInternal方法 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); } /** * 需要子类实现的模板方法 */ @Nullable protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception;
实战
本章实战是通过继承AbstractController类编写用户请求handler,测试如下几点:
- 继承AbstractController抽象类,实现handlerRequestInternal方法处理用户请求
通过前几篇及上面的分析,SimpleControllerHandlerAdapter类可以适配实现了Controller接口的类,AbstractController实现了Controller接口的handleRequest方法,并预留了handleRequestInternal模板方法供子类实现,用户处理用户请求,根据这个思路,我们要使用SimpleControllerHandlerAdapter适配Controller时,只需继承AbstractController类并实现handleRequestInternal方法即可,具体源码如下:
自定义Controller源码:
/** *继承AbstractController并实现handleRequestInternal方法 */ public class HelloWorldController extends AbstractController { /** *通过response直接回写数据,也可通过ModelAndView指定逻辑视图并回写数据 */ @Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { Writer writer = response.getWriter(); writer.write("hello AbstractController"); writer.flush(); return null; } }
配置文件配置如下:
<!-- 使用BeanNameUrlHandlerMapping映射器 可以不用配置,Spring MVC默认支持:BeanNameUrlHanderMapping、RequestMappingHandlerMapping --> <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 指定Spring使用SimpleControllerHandlerAdapter适配器 可以不用配置,Spring MVC默认支持:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter --> <bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- 配置Controller bean --> <bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController"/>
验证结果
代码编写完后,启动应用进行测试,AbstractController默认支持GET、HEAD、POST方法类型,我们使用Postman进行测试,GET请求结果如下:
HEAD、POST也能处理,但如果发起PUT请求则返回不支持方法类型,如下图:
- 指定请求方法类型
AbstractController默认支持GET、HEAD、POST三种请求类型,本例通过设置supportedMethods属性来设置支持的请求方法类型,代码如下:
配置文件配置如下:
<!-- 配置Controller bean --> <bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController"> <!-- 设置支持的请求方法类型,如下支持PUT、POST --> <property name="supportedMethods"> <set> <value>PUT</value> <value>POST</value> </set> </property> </bean>
验证结果
只需在Controller Bean修改supportMethods属性接口,测试PUT请求,结果如下:
- requireSession前置检查
当把requireSession属性设置为true时,访问Controller需检查有无session,如果没有将跑出HttpSessionRequiredException异常,代码如下:
配置文件配置如下:
<!-- 配置Controller bean --> <bean name="/helloWorldController" class="com.github.dalianghe.controller.HelloWorldController"> <!-- 设置支持的请求方法类型,如下支持PUT、POST --> <property name="supportedMethods"> <set> <value>GET</value> <value>POST</value> </set> </property> <!-- 访问此处理器前检查session --> <property name="requireSession" value="true"/> </bean> <!-- 增加一个处理器,用于获取session --> <bean id="/helloWorld2Controller" class="com.github.dalianghe.controller.HelloWorld2Controller"/>
新增的HelloWorld2Controller与HelloWorldController代码基础上增加获取session的代码,如下:
request.getSession(true);
测试验证
首先验证没有session情况下,结果如下:
说明在访问到自定义Controller前检查session没有,抛出HttpSessionRequiredException异常
我们增加了一个新的controller用于设置创建session,此时我们访问一次后,再访问需要验证的session的请求,结果如下:
通过结果可见,测试能正常访问了。
总结
本章主要分析了Controller接口、AbstractController抽象类以及对自定义Controller的几个重要属性进行了测试,希望对大家有帮助,谢谢。
最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!