本文旨在详细分析SpringMVC工做原理以及做为开发者如何基于SpringMVC作扩展。由于SpringMVC分析的文章比较多,因此本文重点讲解如何利用SpringMVC的扩展点实现咱们的需求。前端
SpringMVC的做用是什么呢?须要解决什么问题呢?java
下图是一个客户端与服务端的交互web
大致部分分为两部分一块是创建链接、一块是传输内容。因此servlet规范包括两大部分,,一部分是servlet接口,定义处理请求的规范。一部分是servlet容器的,去管理加载servlet实例。spring
轻量级的servlet容器有tomcat/jetty/undertow,servlet框架有SpringMVC/Struts/Webx这些,本篇重点讲解SpringMVCapi
基于以上,Spring MVC 提供了不一样层面的扩展,方便开发者实现定制化的功能,而不须要底层代码的修改tomcat
Filter其实不算是SpringMVC,是servlet的,这时候请求尚未到DispatchServlet
。Filter容许对请求和响应作一些统一的定制化处理,好比你限流、日志、trace。bash
实现javax.servlet.Filter
接口便可restful
HandleMapping属于Controll层面,咱们能够编写任意的HandlerMapping实现类,而后定义策略来决定一个web请求到HandlerExecutionChain对象的生成。mvc
继承RequestMappingHandlerMapping
类便可。 这个具体案例能够看下fredal的博客-使用基于 SpringMVC 的透明 RPC 开发微服务app
简要来讲,他的rpc通讯协议是基于http的。因此rpc调用就是基于服务端仍是原来的restful api。写法给普通的前端去掉无异,而后包一层rpc client。方便客户端调用。可是这样太麻烦了,对于不须要暴露给前端的API,单纯是服务间的rpc调用。再走一遍servlet-SpringMVC不必。
因此他基于RequestMappingHandlerMapping
作了一个改造。再也不基于SpringMVC,而是本身定义了一套rpc的范式,而后转换为springmvc。
Interceptor属于Controll层面,咱们能够自定义各类拦截器,在一个请求被真正处理以前、请求被处理但还没输出到响应中、请求已经被输出到响应中以后这三个时间点去作任何咱们想要作的事情。普遍应用于Log,Session,鉴权等场景。
实现HandlerInterceptor
接口便可
解析方法参数的,能够很方便的扩展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;
}
}
复制代码
类型转换器,主要和序列化相关,参数绑定时springmvc会对将前端传来的参数经过某种规则转化成方法定义的参数的类型,默认实现的有StringHttpMessageConverter
、ByteArrayHttpMessageConverter
等等,默认的不能知足需求时咱们可本身定义此接口来实现本身的类型的转换。
继承AbstractHttpMessageConverter
便可。
异常处理,一般须要定义的全局异常。
@ControllerAdvice
注解便可 在一次和前端的相互甩锅的问题记录中有总结过这种
当咱们须要对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的工做原理和各类扩展机制,属于高度归纳,细节不足。具体的每一个扩展点的实现、坑、应用场景须要在以后的文章继续阐述。