前言
《Spring 揭秘》是一本09年出版的书,当时还在流行Struct 2
,作者大胆预言Spring MVC
有着更广阔的发展前途,并且建议新的项目采用Spring MVC
进行开发。现在回过头来看,当年的Struct 2
之于Spring MVC
就如现今的Spring MVC
之于 Spring Webflux
。当年作者熟练掌握了Struct 2
才悟得Spring MVC
的好,那么如今在进入Spring Webflux
的探索前,还需要夯实Spring MVC
的基础。短时间内Spring MVC
还是很能打的,看完书后,慢慢能体会其架构上的设计哲学。框架上使用的设计模式,虽简洁但有效。
- 本文不会提
Struct 2
的实现 - 关注
Spring MVC
的核心模块、看Interceptor
的作用时机。
2. 从时序图看被解耦的组件
-
DispatcherServlet
有着三个角色- 遵循Servlet规范(间接实现了Servlet接口),可作为web应用的基本响应单元
- 被Spring管理(间接实现ApplicationContextAware)
- 在Spring容器中作为单例存在(Spring Boot自动装配会替我们做)
DispatcherServlet 的一体两面,一为Servlet,二为Spring的上下文,就赐予它整合Servlet与Spring的使命,事实上它也是这么干的。
在只注册了DispatcherServlet 的情况下,所有http请求都会被这个Servlet拦截,然后由它去寻找适合的控制器
(Controller)
-> 根据控制器返回的信息(Model)
和视图逻辑标签 -> 根据视图的逻辑标签转发给视图的处理器进行渲染。这就是常说的MVC模式。// MVC 模式的抽象, 也是Spring的具体代码 public interface Controller { @Nullable ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
-
HandlerMapping
保存Spring IOC中注册的"Handler"
和 客户端请求的映射关系,一般的key为url, 编程中这个key常常使用@RequestMapping("/path/...")
进行声明 -
HandlerExecutionChain
Handler
的包装类。看到了包装逻辑有AOP的影子,并且拥有Servlet规范下的request和responsepublic class HandlerExecutionChain { //实际的请求处理器,用来控制我们的请求到达哪个对象的哪个方法 private final Object handler; // 拦截器 @Nullable private HandlerInterceptor[] interceptors; public Object getHandler() { return this.handler; } //处理实际请求前的一些操作 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {} //请求处理完成后的一些操作 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {} }
到这里如果感到头晕了,可以直接看下面的源码解释,比较直观。
HandlerAdapter
是设计模式的核心,放到后面讲。
2.1 源码解释时序图
DispatcherServlet.doDispatch()
// 内部用HandlerMapping找到请求的Handler封装类, 封装的是Object类型
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
// 获取适配器,内部调用adapter.supports(handler)匹配,返回一个适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 前置拦截逻辑(Interceptors是全局共享的,内部回去get)
mappedHandler.applyPreHandle(processedRequest, response)) {
// 通过适配器去生成MV
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 后置拦截处理(完成了controller的业务逻辑了)
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 获取到mv接着往下走
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
视图处理 processDispatchResult()
// 使用mv渲染视图
render(mv, request, response);
// 终端拦截处理 (整个MVC流程近乎走完,视图也渲染完成)
mappedHandler.triggerAfterCompletion(request, response, null);
2.2 HandlerAdaptor 巧用适配器模式解耦C和MV
Spring 提供的 部分Adapter
SimpleControllerHandlerAdapter
RequestMappingHandlerAdapter
Spring 提供的部分 Handler
Controller
HandlerMethod
列举以上的类,是想突出一个思想,源码部分的Handler声明为Object类型,通过新增Adapter和Handler完成MVC控制器的种类拓展,而不用修改DispatcherServlet
的源码。同时关注到Adapter
和 Handler
还可以存在多对一的关系: 言下之意是,Adapter
在内存的注册顺序需要关注。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
2.3 适配器模式下产生的拓展意义
就上面的Adapter
和 Handler
的举例,产生了响应式编程也就是 Spring Webflux
的影子
- 普通的Adapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
- 响应式的Adapter
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 省略
}
2.4 视图的解耦(MV)
- 上文说到的MV,V并不是已经渲染出来的视图,只作为一个key存在,把渲染的工作延时到上文提到的
processDispatchResult()中的render()
进行处理,具体代码如下
String viewName = mv.getViewName()
if (viewName != null) {
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
} else {
view = mv.getView();
}
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
3. 使用MVC的理由及意义
- 可拔插的视图渲染技术选择,视图渲染可独立于Web容器发展(Servlet)
Thymeleaf
Freemarker
(较老的技术)JSP
(较老的技术)
- 运用
Interceptor
提供的面向切面编程角度,更好得组织公共代码 - 可引用更多
Model
绑定技术,Spring Boot Web Starter
默认是用Jackson
- 通过
Spring MVC
,Filter
也能被管理(相关技术用Spring Sercurity
的模块讲更好)