源码学习之设计模式(委托模式)

今天要说的是委派模式。

使用起来 和代理模式有点像,在《设计模式之禅》中讲到代理模式就是委派模式,对于这个观点我不敢苟同。看了《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。

你可能感兴趣的:(源码学习之设计模式(委托模式))