委派模式在JDK以及Spring源码中的应用

行为型模式

目录

1、委派模式

1.1 委派模式UML类图

1.2 日常生活中看委派模式

1.3 Java代码实现

2、委派模式在源码中的应用

2.1 JDK源码中委派模式体现

2.2 Spring源码中委派模式体现

2.2.1 IOC中对象实例化委派模式

2.2.2 SpringMVC中,类DispatcherServlet

3、委派模式优缺点

3.1 优点

3.2 缺点

3.3 应用场景

4、委派模式与代理模式异同


1、委派模式

委派模式不属于GOF23种设计模式,但是在Spring中委派模式确实用的比较多的一种模式。本文将此模式归类为行为型模式。主要角色有三种: 抽象任务角色, 委派者角色, 具体任务角色有前辈将其总结为代理模式和策略模式的组合

意图:定义抽象接口的一个实现类, 他负责判断和调用哪个实现类的实例。

委派模式对外隐藏了具体实现, 仅将委派者角色暴露给外部, 如Spring的DispatcherServlet.

1.1 委派模式UML类图

委派

1.2 日常生活中看委派模式

如公司老板想举办年会,这个时候,需要很多人员参与配合,需要有人写PPT,需要有人布置会场,需要有人采购,还需要有人宣传等。这个时候,老板本人并不知道公司有哪些同事擅长做这些事情,于是,老板就将这个艰巨的任务交给了HR,然后让HR给安排合适的任选进行工作的进行。这就是一个比较简单易懂的委派模式的表现。

1.3 Java代码实现

1.3.1 定义抽象任务角色接口

/**
 * 抽象任务角色
 */
public interface Task {
    void doTask();
}

1.3.2 具体任务角色, 实现上面的接口, 这里定义两个实现类

/**
 * 具体实现类A
 */
public class ConcreteTaskA implements Task {
    public void doTask() {
        System.out.println("执行 , 由A实现");
    }
}

/**
 * 具体实现类B
 */
public class ConcreteTaskB implements Task {
    public void doTask() {
        System.out.println("执行 , 由B实现");
    }
}

1.3.3 委派角色, 是整个模式的核心角色, 下面代码中我们使用随机数来判断应该实例化哪个具体实现类

import java.util.Random;
/**
 * 代理角色
 */
public class TaskDelegate implements Task{
    public void doTask() {
        System.out.println("代理执行开始....");

        Task task = null;
        if (new Random().nextBoolean()){
        task = new ConcreteTaskA();
        task.doTask();
        }else{
            task = new ConcreteTaskB();
            task.doTask();
        }

        System.out.println("代理执行完毕....");
    }
}

1.3.4 调用

public class TaskTest {
    public static void main(String[] args) {
        new TaskDelegate().doTask();
    }
}

2、委派模式在源码中的应用

2.1 JDK源码中委派模式体现

JDK中有一个典型的委派,JVM在加载类时是用的双亲委派模型。

​ 一个类加载器在加载类时,先把这个请求委派给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委派,直到顶层的启动类加载器。

​ 如果父类加载器能过完成类加载,就成功返回。如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。从定义中可看出双亲加载模型一个类加载器加载类时,首先不是自己加载,而是委派给父加载器。

​ 在下方的代码示例中,loadClass()方法,定义在ClassLoader中,在这个类中就定义了一个双亲,用于下面的类加载。

public abstract class 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;
        }
    }
}

2.2 Spring源码中委派模式体现

2.2.1 IOC中对象实例化委派模式

在doRegisterBeanDefinitions()即BeanDefinition进行注册的过程中,会设置BeanDefinitionParserDelegate类型的Delegate对象传给this.delegate,并将这个对象作为一个参数传给:parseBeanDefinitions(root, this.delegate)中,然后主要的解析的工作就是通过delegate作为主要角色来完成的,可以看到下方代码:

/**

 * Parse the elements at the root level in the document:

 * "import", "alias", "bean".

 * @param root the DOM root element of the document

 */

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

   //判断节点是否属于同一命名空间,是则执行后续的解析

   if (delegate.isDefaultNamespace(root)) {

      NodeList nl = root.getChildNodes();

      for (int i = 0; i < nl.getLength(); i++) {

         Node node = nl.item(i);

         if (node instanceof Element) {

            Element ele = (Element) node;

            if (delegate.isDefaultNamespace(ele)) {

               parseDefaultElement(ele, delegate);

            }

            else {

               //注解定义的Context的nameSpace进入到这个分支中

               delegate.parseCustomElement(ele);

            }

         }

      }

   }

   else {

      delegate.parseCustomElement(root);

   }

}

其中最终能够走到bean注册部分的是,会进入到parseDefaultElement(ele, delegate)中,然后针对不同的节点类型,针对bean的节点进行真正的注册操作,而在这个过程中,delegate会对element进行parseBeanDefinitionElement,得到了一个BeanDefinitionHolder类型的对象,之后通过这个对象完成真正的注册到Factory的操作

2.2.2 SpringMVC中,类DispatcherServlet

当然了,我们熟知的DispatcherServlet 虽然没带delegate,但也是委派模式的一种实现。

前端请求都统一走到DispatcherServlet 的doService()方法中,然后在doService()方法中调用doDispatch()方法,在doDispatch()方法中,会获取业务处理的handler,执行handle()方法处理请求。

doDispatch()方法核心源码截图

doDispatch

就是:用于HTTP请求处理程序/控制器的中央调度程序,针对通过WEB UI输入的url请求,委派给DispatcherServlet处理,从委派者的角度来看,关注结果即可

3、委派模式优缺点

3.1 优点

通过任务委派能够将一个大型的任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,能够加快任务执行的效率。

3.2 缺点

​ 任务委派方式需要根据任务的复杂程度进行不同的改变,在任务比较复杂的情况下可能需要进行多重委派,容易造成紊乱。

3.3 应用场景

  1. 当你要实现表现层和业务层之间的松耦合的时候。
  2. 当你想要编排多个服务之间的调用的时候。
  3. 当你想要再封装一层服务查找和调用时候

4、委派模式与代理模式异同

我们知道,代理模式是由代理来帮你完成一些工作,而这里的委派模式,是由委派对象来帮你完成一些工作,字面上来看,好像并没有什么差别。那么,既然分为两种不同 的设计模式,那么它们俩之间肯定是有差别的。

首先,我们知道代理可以增强我们的代理目标类,比如,王婆帮西门庆安排了一场约会,这里的王婆,就是西门庆的代理了。但是,具体的参与约会的对象,依然是西门庆,而并不是王婆。所以,代理模式中,被代理类还是需要进行实际去参与行动

委派模式,像上面的例子,boss想举办一场豪华的年会,他只需要告诉HR小姐姐即可,接下来的所有的事情,都交给小姐姐去处理即可了,自己完全不必实际去参与到行动中

这就是代理模式和委派模式的区别所在。

 

参考文章:

https://www.cnblogs.com/walkinhalo/p/9603932.html

https://www.cnblogs.com/happyone/p/12496880.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值