帶你深入學習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 又有了更深的理解,這個也是作者比較得意的一個功能,因爲在開始時,如果我們總是需要調用對返回結果進行封裝的時候,這樣的代碼既累贅,有冗餘,向作者的項目中是沒有這種代碼的

但是我們在前臺最終看到的結果確是封裝了一層

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章