深入浅出Spring MVC
摘要
本文旨在详细分析SpringMVC工作原理以及作为开发者如何基于SpringMVC做扩展。因为SpringMVC分析的文章比较多,所以本文重点讲解如何利用SpringMVC的扩展点实现我们的需求。
什么是Spring MVC
SpringMVC的作用是什么呢?需要解决什么问题呢?
下图是一个客户端与服务端的交互
在之前的详解http报文(2)-web容器是如何解析http报文的一文中我也提到过。
这次再更细致的分析一遍。一个请求如何中客户端发到服务端,再从服务端返回内容。干的这件事在web中叫请求动态内容,区别于静态内容。在java语言中,为了解决这件事定义了一个规范就是servlet。具体的实现由各大厂商自己定义。
大体部分分为两部分一块是建立连接、一块是传输内容。所以servlet规范包括两大部分,,一部分是servlet接口,定义处理请求的规范。一部分是servlet容器的,去管理加载servlet实例。
轻量级的servlet容器有tomcat/jetty/undertow,servlet框架有SpringMVC/Struts/Webx这些,本篇重点讲解SpringMVC
SpringMVC工作流程
Spring MVC 顾名思义就是处理Controll-Model-View的。
- DispatchServlet 是入口,doDispatch方法开始处理请求
- 首先经过controll,controll包含两部分,一部分是url处理映射,将url与具体的处理bean映射起来。也就是HandleMapping,另一部分是具体的Handle,因为需要不同的handle,所以定义了HandleAdapter.
- Model比较简单,主要就是ModelView对象,
- View 包含两部分,一部分是ViewName的解析,另一部分是ViewName的对应的模板引擎,来渲染出最终的模板引擎。
常见扩展点
基于以上,Spring MVC 提供了不同层面的扩展,方便开发者实现定制化的功能,而不需要底层代码的修改
一. Filter
Filter其实不算是SpringMVC,是servlet的,这时候请求还没有到DispatchServlet
。Filter允许对请求和响应做一些统一的定制化处理,比如你限流、日志、trace。
实现javax.servlet.Filter
接口即可
二. Controll - HandleMapping,HandlerAdapter
HandleMapping属于Controll层面,我们可以编写任意的HandlerMapping实现类,然后定义策略来决定一个web请求到HandlerExecutionChain对象的生成。
继承RequestMappingHandlerMapping
类即可。
这个具体案例可以看下fredal的博客-使用基于 SpringMVC 的透明 RPC 开发微服务
简要来说,他的rpc通信协议是基于http的。所以rpc调用就是基于服务端还是原来的restful api。写法给普通的前端去掉无异,然后包一层rpc client。方便客户端调用。但是这样太麻烦了,对于不需要暴露给前端的API,单纯是服务间的rpc调用。再走一遍servlet-SpringMVC没必要。
所以他基于RequestMappingHandlerMapping
做了一个改造。不再基于SpringMVC,而是自己定义了一套rpc的范式,然后转换为springmvc。
三. Controll - Interceptor
Interceptor属于Controll层面,我们可以自定义各种拦截器,在一个请求被真正处理之前、请求被处理但还没输出到响应中、请求已经被输出到响应中之后这三个时间点去做任何我们想要做的事情。广泛应用于Log,Session,鉴权等场景。
实现HandlerInterceptor
接口即可
四. View - HandlerMethodArgumentResolver
解析方法参数的,可以很方便的扩展http请求参数。
实现HandlerMethodArgumentResolver
接口即可
比如需要从http header中处理设备信息
@Component
public class DeviceResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(final MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(DeviceInfo.class);
}
@Override
public Object resolveArgument(final MethodParameter methodParameter,
final ModelAndViewContainer modelAndViewContainer,
final NativeWebRequest nativeWebRequest,
final WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request =
(HttpServletRequest) nativeWebRequest.getNativeRequest(HttpServletRequest.class);
// 从head头中获取设备信息
String id = request.getHeader("x-device-id");
if (id != null) {
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.setId("id");
return deviceInfo;
}
return null;
}
}
五. View - Converter
类型转换器,主要和序列化相关,参数绑定时springmvc会对将前端传来的参数通过某种规则转化成方法定义的参数的类型,默认实现的有StringHttpMessageConverter
、ByteArrayHttpMessageConverter
等等,默认的不能满足需求时我们可自己定义此接口来实现自己的类型的转换。
继承AbstractHttpMessageConverter
即可。
六. View- HandlerExceptionResolver
异常处理,通常需要定义的全局异常。
@ControllerAdvice
注解即可
在一次和前端的相互甩锅的问题记录中有总结过这种
七. 修改requestbody 内容
当我们需要对RequestBody
的内容进行统一处理时,因为HandlerMethodArgumentResolver
只能处理特定类型的,做不到这点要求。
实现RequestBodyAdvice
接口即可。比如我需要处理requestbody中的内容,将emoji输入转换掉
@RestControllerAdvice
public class EmojiReplaceAdvice implements RequestBodyAdvice {
@Override
public boolean supports(final MethodParameter methodParameter, final Type targetType,
final Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.hasParameterAnnotation(EmojiReplace.class);
}
@Override
public Object handleEmptyBody(final Object body, final HttpInputMessage inputMessage,
final MethodParameter parameter, final Type targetType,
final Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage,
final MethodParameter parameter,
final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType)
throws IOException {
return new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
final String content = IOUtils.toString(inputMessage.getBody());
final String emojiUnicodeToAlias = StringUtil.parseEmojiUnicodeToAlias(content);
return new ByteArrayInputStream(
emojiUnicodeToAlias.getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
}
@Override
public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage,
final MethodParameter parameter, final Type targetType,
final Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}
总结
这篇文章主要是系统的概括了SpringMVC的工作原理和各种扩展机制,属于高度概括,细节不足。具体的每个扩展点的实现、坑、应用场景需要在之后的文章继续阐述。
参考
https://fredal.xin/develop-with-transparent-rpc
深入浅出Spring MVC的更多相关文章
- [MVC] 深入浅出Spring MVC
[MVC] 深入浅出Spring MVC 转:http://4925054.blog.51cto.com/4915054/1176855 Spring MVC主要包括以下要点: 1:由Dispatch ...
- Spring MVC中Session的正确用法<转>
Spring MVC是个非常优秀的框架,其优秀之处继承自Spring本身依赖注入(Dependency Injection)的强大的模块化和可配置性,其设计处处透露着易用性.可复用性与易集成性.优良的 ...
- 【转】Spring MVC中Session的正确用法之我见
Spring MVC是个非常优秀的框架,其优秀之处继承自Spring本身依赖注入(Dependency Injection)的强大的模块化和可配置性,其设计处处透露着易用性.可复用性与易集成性.优良的 ...
- 深入浅出Spring(五) SpringMVC
上一篇深入浅出Spring(四) Spring实例分析的博文中,咱们已经可以了解Spring框架的运行原理和实现过程,接下来咱们继续讲解Spring的一个延伸产品——Spring MVC 1.Spri ...
- Spring MVC中Session的正确用法之我见
Spring MVC是个非常优秀的框架,其优秀之处继承自Spring本身依赖注入(Dependency Injection)的强大的模块化和可配置性,其设计处处透露着易用性.可复用性与易集成性.优良的 ...
- Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)
在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...
- 如何用Java类配置Spring MVC(不通过web.xml和XML方式)
DispatcherServlet是Spring MVC的核心,按照传统方式, 需要把它配置到web.xml中. 我个人比较不喜欢XML配置方式, XML看起来太累, 冗长繁琐. 还好借助于Servl ...
- Spring MVC重定向和转发以及异常处理
SpringMVC核心技术---转发和重定向 当处理器对请求处理完毕后,向其他资源进行跳转时,有两种跳转方式:请求转发与重定向.而根据要跳转的资源类型,又可分为两类:跳转到页面与跳转到其他处理器.对于 ...
- Spring MVC入门
1.什么是SpringMvc Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面.Spring 框架提供了构建 Web 应用程序的全功能 M ...
随机推荐
- B站弹幕系统架构——GOIM解读
架构图 说明: 1.logic启动http服务器, 接受http请求,用于将数据推送到kafka以及获取在线用户信息,websocket身份校验 2.comet组件起动webdocket/tcp服务, ...
- nginx常见应用实例
1.nginx 中 location 应用实例location 是 nginx 的精华,nginx 就是通过拦截到的请求去对配置好的 location 块(location block)进行请求代理的 ...
- xenomai内核解析之xenomai的组成结构
@ 目录 一.xenomai 3 二.xenomai3 结构 这是第二篇笔记. 一.xenomai 3 从xenomai3开始支持两种方式构建linux实时系统,分别是cobalt 和 mercury ...
- P1725 琪露诺(单调队列优化)
描述:https://www.luogu.com.cn/problem/P1725 小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子.而且琪露诺按照一种特殊的方式进行移 ...
- 基于3D NAND层差异的固态盘请求调度算法研究立项 报告
Abstract SSD(Solid State Drive),因其超高的读写性能,以及价格的走低趋势逐渐占据市场,为人们带来更好的用户体验,也为企业级的高并行业务需要提供了一定支持,近几年来SSD的 ...
- 聚合类型与POD类型
Lippman在<深度探索C++对象模型>的前言中写道: I have heard a number of people over the years voice opinions sim ...
- Python 文件的读取与写入
1. 读取文件,文件中没有中文 备注 : 文件名 : EnglishFile.txt 文件位置 : 保存在所写的.py文件的同级目录,附上截图,便于参考 备注 : 文件位置可以改变,只需要把文件路径传 ...
- Python-SHA256加密算法接口测试
前言 小伙伴们在做接口测试的时候,是否遇到一些需要加密的接口,但是不知如何进行测试呢?今天我们来学习一下SHA256加密算法接口如何进行测试的. SHA256加密算法介绍: 比特币挖矿的御用算法 SH ...
- [hdu1242]优先队列
题意:给一个地图,'x'走一步代价为2,'.'走一步代价为1,求从s到t的最小代价.裸优先队列. #pragma comment(linker, "/STACK:10240000,10240 ...
- sql查重去除id最小值
select order_id FROM yzj_store_order t WHERE (t.user_id,t.order_status) IN ( SELECT user_id,order_st ...