摘要
本文旨在详细分析 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 开发微服务 https://fredal.xin/develop-with-transparent-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
来源: https://www.cnblogs.com/stoneFang/p/12996839.html