设计模式之【委派模式】+ 框架源码分析

点赞的靓仔,你最帅哦!

源码已收录github 查看源码,别忘了star哦!

开题

初入博客圈,第一个编写的专题定位在设计模式,前面已经完成了部分设计模式的内容,设计模式是框架架构设计的基础,不能说懂设计模式才会懂框架,但懂设计模式一定可以更好的懂框架,而对设计模式深入了解后,当工作中遇到需求或者问题的时候,甚至能够自然而然的想到用设计模式来解决。

更重要的是,我们学习技术是为了提升自我,找到一份好的工作。那么作为面试中的高频题目,非常有必要掌握。本文通过理论 + 实践 + 源码解读的方式来详细的结束委派模式。

委派模式

委派模式并不复杂,和其字面含义类似,指委派者接收任务,然后将任务分配给具体做这个任务的对象。就像做项目一样,老板安排任务给项目经理,项目经理再将任务委托给具体做任务的人。

这样咋一看和代理模式非常类似,其实可以理解委托模式就是一种特殊的代理模式,他们的区别在于各自关注的点不同,代理模式通常注重代理的过程,如前置、后置、环绕等功能的增强;而委派模式不注重过程,注重任务的执行结果,就像老板,他不关心项目经理把这件事情安排给谁完成,他关注的是最终的结果。


image

委派模式主要涉及到三种角色:委派者、工作者、任务
如上个图例,委派者即项目经理,工作者即产品、架构师、后端,任务即要做的事情。下面通过代码实现。

工作者接口

package demo.pattren.delegate;

public interface Worker {
    void doJob(Job job);
}

Job定义任务类

package demo.pattren.delegate;

import lombok.Data;
@Data
public class Job {
    //实际开发中的任务类肯定比较复杂,属性非常多
    private String jobName;
}

Worker的其中一个实现,大致类似。其他就省略了,可以在文章开头去我的Github拉取源码。

package demo.pattren.delegate;

/**
 * 开发
 */
public class Development implements Worker {
    @Override
    public void doJob(Job job) {
        System.out.println("开发人员向你抛出异常,项目延期");
        System.out.println("加班加点,开发人员完成工作");
        System.out.println("开发人员黑着眼圈,并完成" + job.getJobName());
    }
}

测试

package demo.pattren.delegate;

import java.util.ArrayList;
import java.util.List;
/**
 * 委派模式模拟测试测试
 */
public class DelegateTest {
    public static void main(String[] args) {
        //任务
        List project = new ArrayList<>();
        Job job1 = new Job();
        Job job2 = new Job();
        Job job3 = new Job();
        job1.setJobName("原型");
        job2.setJobName("架构");
        job3.setJobName("开发");
        project.add(job1);
        project.add(job2);
        project.add(job3);

        ProjectManager manager = new ProjectManager();
        //产品经理委派任务,对老板来说,任务都交给项目经理,并不关心具体谁完成
        project.forEach(item -> manager.dispatch(item));
    }
}

深入源码解读委派模式

通常写代码的时候,通常会在类名后面加上设计模式的名称,比如JdbcTemplate就用到了模板模式, 委派模式在JDK以及框架中应用非常多,我们用Delegate在Idea中查询,可以查到一大堆,几十上百个不止。


image

我们找一个比较熟悉的BeanDefinitionParserDelegate,查看该类在那些地方有使用。
关于该类的作用翻译如下:

Stateful delegate class used to parse XML bean definitions.

Intended for use by both the main parser and any extension

解析XML配置的委托类,即真正解析XML内容的类。

可以同时用于主解析器以及主解析器的扩展。

如果了解过Spring的容器初始化Bean的过程,那么一定对BeanDefinitionReader不陌生,BeanDefinitionReader是用于读取Bean定义的接口,最终解析配置返回BeanFefinition对象,Debug跟踪源码,在XML定义的Bean的解析最终是交由BeanDefinitionParserDelegate完成的,类图结构如下。


image

XmlBeanDefinitionReader.registerBeanDefinitions

//解析XML并注册为BeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //创建reader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    //注册解析Bean
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

该方法核心代码是注册解析Bean,继续追踪

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  this.readerContext = readerContext;
  //调用do方法, 
  doRegisterBeanDefinitions(doc.getDocumentElement());
}

以do开头的都是真正干活的方法,继续追踪

protected void doRegisterBeanDefinitions(Element root) {
    //保存当前结点的父委派对象,因为xml是有层级和包含关系,所以处理这里的时候是递归进行的,
    //保证可以正确解析xml到beanDefinition
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}

此时请求执行到了BeanDefinitionParserDelegate,而BeanDefinitionParserDelegate还不是真正解析的类,而是将解析的工作交由解析器处理。我们再将类图关系完善,整个解析处理流程如下。


image

至此,整个处理流程已经清晰。简单总结。

Spring从XML解析到BeanDefinition流程如下。
XmlBeanDefinitionReader为起点,任务交由DefaultBeanDefinitionDocumentReader处理,DefaultBeanDefinitionDocumentReader通过委托类BeanDefinitionParserDelegate将解析任务委托给真正的解析器BeanDefinitionParser的实现类处理。

你可能感兴趣的:(设计模式之【委派模式】+ 框架源码分析)