今天要說的是委派模式。
使用起來 和代理模式有點像,在《設計模式之禪》中講到代理模式就是委派模式,對於這個觀點我不敢苟同。看了《Spring5核心原理與30個類手寫實戰》以及網上查閱資料,我總結了以下幾點:
-
代理模式注重過程,而委派模式更加看重結果。生活中常有委派發生,班級大掃除的時候,班主任安排任務給班長,班長把分配給同學們,班主任呢,他只關注打掃的干不干凈,至於怎么打掃的,他不太關心。
-
代理模式的對象自始至終都沒有發生改變,且在代理模式中代理類不能改變業務邏輯。委托恰恰相反,不僅可以實現都有的邏輯,也可以自由的更換委托對象,就像上面的例子,老師也可以把任務委派給衛生委員。
-
在編程方面,代理模式更像是上下級的關系,委托則像平級關系。
下面分析源碼加深一下理解。
源碼分析
jdk中有一個典型的委托,眾所周知jvm在加載類是用的雙親委托模型,這又是什么呢?一個類加載器在加載類時,先把這個請求委托給自己的父類加載器去執行,如果父類加載器還存在父類加載器,就繼續向上委托,直到頂層的啟動類加載器。如果父類加載器能夠完成類加載,就成功返回,如果父類加載器無法完成加載,那么子加載器才會嘗試自己去加載。從定義中可以看到雙親加載模型一個類加載器加載類時,首先不是自己加載,而是委托給父加載器。這就和上面打掃衛生的例子很像。父加載器肯定有自己的業務邏輯 ,對比下代理模式,正好印證上面的第二條。下面我們來看看loadClass
方法的源碼,此方法位於ClassLoader
類里。
在此類里定義了一個雙親,用於下面的加載。
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
同樣在Method
l類里我們常用代理執行方法invoke()
也存在類似的機制。
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
看完代碼,相信童鞋們對委托和代理區別搞清楚了吧。在spring的DispacherServlet
也實現了委托,讓我們一起來看下吧。
委托在Spring中應用
學過spring都知道DispacherServlet
是提供web的集中訪問點,分配任務去給處理器執行
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);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
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);
}
}
}
}
DispacherServlet
將任務分配給處理器,主要由
HandlerAdapter
:確定當前請求的處理程序適配器HandlerExecutionChain
:確定當前請求的處理程序。cleanupMultipart()
:清理multipart多余的資源。HandlerMapping
:將請求映射到處理器ViewReslover
: 解析視圖名到具體試圖實現。
從以上我們可以看出DispatcherServlet主要負責流程的控制。當然我分析的可能有出入,小伙伴們可以指出來,一起討論。
好了,委托模式就到這了,童鞋們只有深入理解委托和代理的異同點,才能在以后的學習生活中更好的使用,至少還可以在小伙伴面前裝個逼。哈哈哈哈哈哈哈哈哈
看過我的博文都知道,我的文章會有大幅度代碼,有些人可能會質疑我。我個人認為,學習東西需要思考,思考有個方向,文字解釋只是引導方向,閱讀了相關的代碼,並思考代碼中應用,才能有自己的體會 ;光靠別人解釋,吃別人咀嚼過東西沒有味道,體會不到真正的意義。僅代表個人觀點,有不同意見可以提出,一起討論。
說明
本文章為作者讀書筆記及感悟,其中參考了《spring5核心原理與30個類手寫實戰》以及互聯網上的內容。如有錯誤,請評論或者私聊我,歡迎探討技術問題 。即將畢業,在准備找工作,有朋友想給我介紹的,歡迎添加微信:sllbiao。