美文网首页
源码学习之设计模式(委托模式)

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

作者: 奔跑的小虾米 | 来源:发表于2019-12-19 13:22 被阅读0次

    今天要说的是委派模式。

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

    相关文章

      网友评论

          本文标题:源码学习之设计模式(委托模式)

          本文链接:https://www.haomeiwen.com/subject/aaymnctx.html