在平時的業務開發中,相信你們都有不少這樣的代碼:java
public void login(Parameter parameter) { if (!validateXXX(parameter)) { throw new BizException(ErrCode.PAMRM_ERROR); } // 真正的邏輯代碼 }
那麼,若是代碼還有其餘通用的校驗,並且每加一個接口都要加這些校驗邏輯,長此以往,代碼會顯得較臃腫,看起來會有不少重複的代碼,那麼有沒有辦法精簡這部分代碼呢?有!編程
先上代碼數組
public class CheckXXXHandlerInterceptor extends HandlerInterceptorAdapter { final Map<Method, Boolean> methodCache = new IdentityHashMap<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; /** 這個是雙重判斷鎖單例 外層的判斷,爲了不在實例已經建立好的狀況下再次加鎖獲取,影響性能; 裏層的判斷,考慮在多線程環境下,多個線程同時過掉外層判斷,也就是都已經判斷變量爲空,若是不加一重判斷,仍是有可能重複建立。 */ Method method = handlerMethod.getMethod(); if (!methodCache.containsKey(method)) { synchronized (methodCache) { if (!methodCache.containsKey(method)) { boolean check = false; if (method.isAnnotationPresent(CheckXXX.class)) { check = method.getAnnotation(CheckXXX.class).value(); } else if (method.getDeclaringClass().isAnnotationPresent(CheckXXX.class)) { check = method.getDeclaringClass().getAnnotation(CheckXXX.class).value(); } methodCache.put(method, check); } } } if (methodCache.get(method)) { // do check } return true; } }
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface CheckXXX { boolean value() default true; }
@CheckXXX public class XXXController { public void login(Parameter parameter) { // 真正的邏輯代碼 } }
這樣,就能抽離出通用的邏輯,精簡通用的代碼。那麼,這個攔截器是何時執行的呢?它的實現原理是什麼?安全
經過查看自定義攔截器的UML類圖關係,能夠看出來,實際上是實現了HandlerInterceptor的preHandle方法,經過追蹤HandlerInterceptor的調用鏈路,最終是在請求進入分發器,執行doDispatch
方法用的,而處理器是在初始化的時候就加載好。多線程
總體的流程以下:app
核心代碼:ide
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
攔截器數組interceptors是在Spring容器啓動的時候初始化好的,實現原理比較簡單,就是取出請求處理器的map,遍歷調用註冊好的攔截器。性能
經過攔截器處理通用檢查,背後的編程思想實際上是AOP,面向切面編程。this
使用切面的優勢:首先,如今每一個關注點都集中於一個地 方,而不是分散到多處代碼中;其次,服務模塊更簡潔,由於它們只包含主要關注點(或核 心功能)的代碼,而次要關注點的代碼被轉移到切面中了。----摘自《Spring實戰》
關於AOP,網上有不少資料解釋,看維基百科的描述也很清晰,,筆者就很少贅述了。spa
在這個例子裏面,每一個接口的核心功能是響應爲業務功能提供服務,可是每一個接口須要的參數檢查、安全檢查,都統一交給切面完成。以下圖所示:
代碼和原理比較簡單,可是裏面包含的知識點卻很多,經過追朔源碼,能瞭解細節之餘,還能掌握某一類問題的實現方案。
原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。
若是本文對你有幫助,請點個贊吧,謝謝^_^
更多精彩內容,請關注我的公衆號。