1、使用場景
1、擴充卡模式主要是用于把一個類的接口變換成用戶端所期待的另一種接口,進而使原本接口不比對而無法一起工作的兩個類能夠在一起工作。
2、多個元件功能類似,但接口不統一且可能會經常切換時,可使用擴充卡模式,使得用戶端可以以統一的接口使用它們
2、什麼是擴充卡模式
實際生活的例子就是家用電流220V,手機充電器,手機充電需要5V
三者的關系就是,手機充電器對家用電流進行适配封裝,使得輸入充電器的電流是220V,而輸出給手機的電流是5V。
擴充卡模式共用三個角色:
Target(手機)
Adapter(手機充電器)
Adapetee(家用電流)
1、如果是類的擴充卡模式
步驟1: 建立Target接口(使用5V的手機)
public interface Target {
//隻能接受5V的工作電流(原有插頭(Adaptee)沒有的)
public void work_with_5v();
}
步驟2: 建立Adaptee(輸出220V的家用電器) ;
class OutPut220V{
//原有插頭隻能輸出220V
public void output_220v(){
}
}
步驟3:建立Adapter(手機充電器)
class Adapter220V extends OutPut220V implements Target{
@Override
public void work_with_5v(){
//重寫了Target的work_with_5v方法,内部調用output_220v
this.output_220v;
}
}
步驟4:定義具體使用目标類,并通過Adapter類調用所需要的方法進而實作目标(不需要通過原有插頭)
//進口機器類
class Phone{
@Override
public void work() {
System.out.println("手機正常充電");
}
}
//通過Adapter類進而調用所需要的方法
public class AdapterPattern {
public static void main(String[] args){
Target target= new Adapter220V();
Phone phone= new Phone();
target.work_with_5v();
phone.work();
}
}
2、如果是對象的擴充卡模式
與類的擴充卡模式相同的是,對象的擴充卡模式也是把适配的類的API轉換成為目标類的API。
與類的擴充卡模式不同的是,對象的擴充卡模式不是使用繼承關系連接配接到Adaptee類,而是使用委派關系連接配接到Adaptee類。
步驟1: 建立Target接口;
public interface Target {
//這是源類Adapteee沒有的方法
public void Request();
}
步驟2: 建立源類(Adaptee) ;
public class Adaptee {
public void SpecificRequest(){
}
}
步驟3: 建立擴充卡類(Adapter)(不适用繼承而是委派)
class Adapter implements Target{
// 直接關聯被适配類
private Adaptee adaptee;
// 可以通過構造函數傳入具體需要适配的被适配類對象
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void Request() {
// 這裡是使用委托的方式完成特殊功能
this.adaptee.SpecificRequest();
}
}
步驟4:定義具體使用目标類,并通過Adapter類調用所需要的方法進而實作目标
public class AdapterPattern {
public static void main(String[] args){
//需要先建立一個被适配類的對象作為參數
Target mAdapter = new Adapter(new Adaptee());
mAdapter.Request();
}
}
與類的擴充卡模式的代碼不同的就是:隻是在适配類實作時将“繼承”改成“在内部委派Adaptee類”而已
3、Spring MVC源碼中的實際應用
然後我們看看在Spring MVC中源碼的實際應用。
先複習一下SpringMVC的執行流程:
1、一個請求比對前端控制器 DispatcherServlet 的請求映射路徑(在 web.xml中指定), WEB 容器将該請求轉交給 DispatcherServlet 處理
2、DispatcherServlet 接收到請求後, 将根據 請求資訊 交給 處理器映射器 (HandlerMapping)
HandlerMapping 根據使用者的url請求 查找比對該url的 Handler,并傳回一個執行鍊
3、DispatcherServlet 再請求 處理器擴充卡(HandlerAdapter) 調用相應的 Handler 進行處理并傳回 ModelAndView 給 DispatcherServlet
4、DispatcherServlet 将 ModelAndView 請求 ViewReslover(視圖解析器)解析,傳回具體 View
5、DispatcherServlet 對 View 進行渲染視圖(即将模型資料填充至視圖中)
6、DispatcherServlet 将頁面響應給使用者
可以看到在第三步中,使用了HandlerAdapter(處理器擴充卡) ,在學完擴充卡模式後,就應該對Adapter字尾的類名有很強的敏感度,一眼看過去就是使用了擴充卡模式,是為了使得兩個無法一起工作的類,進過擴充卡适配後,能夠一起工作。
擴充卡模式在 SpringMVC 中的經典使用展現在它的核心方法 doDispatch 方法中,再來看一個 Spring MVC 中的 HandlerAdapter 類,它也有多個子類,類圖如下。
其适配調用的關鍵代碼還是在 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);
}
}
}
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while (var2.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]:The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}