源碼學習之設計模式(委托模式)


今天要說的是委派模式。

使用起來 和代理模式有點像,在《設計模式之禪》中講到代理模式就是委派模式,對於這個觀點我不敢苟同。看了《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;
        }

同樣在Methodl類里我們常用代理執行方法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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM