带你深入学习spring mvc
Posted 左沩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你深入学习spring mvc相关的知识,希望对你有一定的参考价值。
现在开发都在用微服务框架,spring boot ,那么由于spring boot 自动配置的原因,我们在开发时通常只需要了解这么几个@responsebody ,@requestMapping ,@requestParam 等常用的几个注解就可以了,但是当我们做架构设计的时候,如果仅仅是了解这么几个注解的话,是无法做好架构设计的,那么我们需要深入的了解spring mvc的架构,以及如何用spring boot 自定义我们的spring mvc 配置。
首先我们通过一张图来了解spring mvc 的执行流程
通过 上面我们 可以明确的知道,springmvc 由前端控制器调用处理器映射器,找到handler,(也就是我们写的http接口),处理器适配器执行handler,返回结果通过视图解析器解析后,返回给前端控制器,前端控制器渲染到前端。
总结一句话就是 一个中心,三个组件,那么一个中心,和三个组件spring boot 都会自动配置好,但是当我们需要一些自定义的时候,我们如何通过spring boot 实现自定义配置呢?那么我们需要掌握以下技术点
@EnableWebMvc 注解
@EnableWebMvc
1:@EnableWebMvc 默认 类似实现了<mvc:annotation-driven/>这个配置。
2. @EnableWebMvc添加给@Configuration类来导入SpringMvc的配置;3.自定义MVC配置,实现接口WebMvcConfigurer或更可能继承WebMvcConfigurerAdapter,并且使用@EnableWebMvc;
4.如果还想要自定义配置,移除@EnableWebMvc,并且继承WebMvcConfigurationSupport或DelegatingWebMvcConfiguration。
参考文档:https://www.cnblogs.com/lvbinbin2yujie/p/10624584.html
上面讲到了spring boot 的默认配置和如何实现自定义配置
首先我们来学习下 WebMvcConfigurationSupport 这个类,然后了解下WebMvcConfigurationSupport 他能实现哪些自定义配置
我们打开WebMvcConfigurationSupport 源码,很容易就可以发现可以自定义哪些东西
同时还有常用的配置静态资源放行的方法,下面展示作者是如何在项目中自定义配置的
/**
* 自定义媒体转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters)
super.extendMessageConverters(converters);
for (HttpMessageConverter converter :
converters)
if (converter instanceof MappingJackson2HttpMessageConverter)
((MappingJackson2HttpMessageConverter) converter).setObjectMapper(new Jackson2ObjectMapperBuilder().simpleDateFormat("yyyy-MM-dd HH:mm:ss").serializationInclusion(JsonInclude.Include.NON_NULL).build());
/**
* 放行的静态资源
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry)
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
/**
* 定义一个返回结果处理器
* @return
*/
@Bean
public MethodReturnValueHandler mgMethodReturnValueHandler()
MethodReturnValueHandler formatJsonReturnValueHandler = new MethodReturnValueHandler(getMessageConverters());
return formatJsonReturnValueHandler;
从上面的代码中,不知道大家发现没有作者自定义了一个消息返回处理器的bean,但是好像我并没有继承WebMvcConfigurationSupport 中添加返回结果处理器的方法,源码中的方法如下
那么作者在这里要做的其实并不是添加一个返回结果处理器,而是要重构spring mvc 默认的RequestResponseBodyMethodProcessor 这个处理器
那么如何替换掉默认的返回结果处理器呢,那么这个我们就要知道RequestResponseBodyMethodProcessor 属于处理器适配器里面的东西,源码如下
哈哈!我们发现我们自定义的很多配置都是处理器适配器的中心,处理器适配器也是作者一开始提到的三大组件的一个组件。
同时我们也发现 处理器适配器继承了InitializingBean 这个接口,这个接口我们从名字就可以看出来应该使用在初始化bean对象时用的,利用这个功能我们便可以在处理器适配器对象创建的时候,对默认的RequestResponseBodyMethodProcessor 返回结果处理器进行替换,下面贴上作者的源码,看我是如何操作的
/**
* 对结果返回处理器进行加强
*/
@Configuration
public class InitializingAdvice implements InitializingBean
/**
* 由于处理器适配器继承了 InitializingBean 那么在bean 创建对象的时候执行该方法,对responsebody 进行加强
*/
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Autowired
private MethodReturnValueHandler methodReturnValueHandler;
/**
* 使用装饰模式加强@responseBody ,对responseBody返回的结果进行加强。
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception
List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers);
this.decorateHandlers(handlers);
requestMappingHandlerAdapter.setReturnValueHandlers(handlers);
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers)
for (HandlerMethodReturnValueHandler handler : handlers)
if (handler instanceof RequestResponseBodyMethodProcessor)
int index = handlers.indexOf(handler);
handlers.set(index, methodReturnValueHandler);
break;
下面也贴上作者是如何是自定义返回结果处理器的代码
/**
* 对controller返回的数据统一封装为ResponseInfo,注意: 1、controller异常由spring mvc异常机制处理,会跳过该处理器
* 2、该处理器仅处理包含@RestController、@ResponseBody注解的控制器
*/
public class MethodReturnValueHandler extends RequestResponseBodyMethodProcessor
public MethodReturnValueHandler(List<HttpMessageConverter<?>> converters)
super(converters);
@Override
public boolean supportsReturnType(MethodParameter returnType)
Class<?> controllerClass = returnType.getContainingClass();
returnType.getMethodAnnotation(ResponseBody.class);
return controllerClass.isAnnotationPresent(RestController.class)
|| controllerClass.isAnnotationPresent(ResponseBody.class)
|| returnType.getMethodAnnotation(ResponseBody.class) != null;
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException
if (((ServletWebRequest) webRequest).getResponse().isCommitted())
super.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
AjaxJson responseInfo = null;
if (returnValue instanceof AjaxJson)
responseInfo = (AjaxJson) returnValue;
else
if (webRequest instanceof ServletWebRequest)
ServletWebRequest request = (ServletWebRequest) webRequest;
String path = request.getRequest().getServletPath();
if (request.getRequest().getServletPath().startsWith("/actuator"))
super.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
responseInfo = new AjaxJson();
responseInfo.setObj(returnValue);
super.handleReturnValue(responseInfo, returnType, mavContainer, webRequest);
说到这里作者已经介绍完了如何通过继承WebMvcConfigurationSupport 实现自定义的配置,以及如何替换掉处理器适配器默认的配置。相信大家对springmvc 又有了更深的理解,这个也是作者比较得意的一个功能,因为在开始时,如果我们总是需要调用对返回结果进行封装的时候,这样的代码既累赘,有冗余,向作者的项目中是没有这种代码的
但是我们在前台最终看到的结果确是封装了一层
以上是关于带你深入学习spring mvc的主要内容,如果未能解决你的问题,请参考以下文章
深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析MVC容器核心流程以及运作原理