現在開發都在用微服務框架,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 又有了更深的理解,這個也是作者比較得意的一個功能,因爲在開始時,如果我們總是需要調用對返回結果進行封裝的時候,這樣的代碼既累贅,有冗餘,向作者的項目中是沒有這種代碼的
但是我們在前臺最終看到的結果確是封裝了一層