分類
發燒車訊

深入淺出Spring MVC_包裝設計

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

摘要

本文旨在詳細分析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的。

  1. DispatchServlet 是入口,doDispatch方法開始處理請求
  2. 首先經過controll,controll包含兩部分,一部分是url處理映射,將url與具體的處理bean映射起來。也就是HandleMapping,另一部分是具體的Handle,因為需要不同的handle,所以定義了HandleAdapter.
  3. Model比較簡單,主要就是ModelView對象,
  4. 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 開發微服務

簡要來說,他的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會對將前端傳來的參數通過某種規則轉化成方法定義的參數的類型,默認實現的有StringHttpMessageConverterByteArrayHttpMessageConverter等等,默認的不能滿足需求時我們可自己定義此接口來實現自己的類型的轉換。

繼承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

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務