從SpringMVC來看介面卡模式
最近在看SpringMVC原始碼,從中看到了比較優秀的設計模式所以來分享下。
1.介面卡模式(Adapter):將一個類的介面轉換成客戶希望的另外一個介面,Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作
具體的詳細知識可以參考這篇文章
適用場景:
1、已經存在的類的介面不符合我們的需求;
2、建立一個可以複用的類,使得該類可以與其他不相關的類或不可預見的類(即那些介面可能不一定相容的類)協同工作;
3、在不對每一個都進行子類化以匹配它們的介面的情況下,使用一些已經存在的子類。
2.SpringMvc中的介面卡(HandlerAdapter)
SpringMVC中的介面卡到底是解決以上哪個問題的呢?我們來一步一步看看原始碼,看看Spring是怎麼做的
首先我們找到前端控制器DispatcherServlet可以把它理解為介面卡模式中的Client,它的主要作用在於通過處理對映器(HandlerMapper)來找到相應的Handler(即Controlle(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)r),並執行Controller中相應的方法並返回ModelAndView,
mappedHandler.getHandler()其實就是通過Spring容器中獲取到的(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)Controller
先短暫回顧下流程
1.DispatcherServlet中的doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 此處通過HandlerMapping來對映Controller mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 獲取介面卡 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 通過介面卡呼叫controller的方法並返回ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
2.為什麼要使用介面卡模式呢?
Controller可以理解為Adaptee(被適配者)其中之一
可以看到處理器(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)的型別不同,有多重實現方式,那麼呼叫方式就不是確定的,如果需要直接呼叫Controller方法,需要呼叫的時候就得不斷是使用if else來進行判斷是哪一種子類然後執行。那麼如果後面要擴充套件(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)Controller,就得修改原來的程式碼,這樣違背了開閉原則(對修改關閉,對擴充套件開放)。
3.SpringMvc 是如何使用介面卡模式來解決以上問題的呢?
Spring建立了一個介面卡介面(HandlerAdapter)使得每一種處理器(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)有一種對應的介面卡實現類,讓介面卡代替(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)執行相應的方法。這樣在擴充套件Controller 時,只需要增加一個介面卡類就完成了SpringMVC的擴充套件了。
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
supports()方法傳入處理器(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)判斷是否與當前介面卡支援如果支援則從DispatcherServlet中的HandlerAdapter實現類中返回支援的介面卡實現類。handler方法就是代理Controller來執行請求的方法並返回結果。
在DispatchServlert中的doDispatch方法中
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
此程式碼通過呼叫DispatchServlert 中getHandlerAdapter傳入Controller(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等),來獲取對應的HandlerAdapter 的實現子類,從而做到使得每一種Controller有一種對應的介面卡實現類
返回後就能通過對應的適配實現類代理Controller(寬泛的概念Controller,以及HttpRequestHandler,Servlet,等等)來執行請求的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
其中一個處理器介面卡就是我們常說的最熟悉的Controller類介面卡