点赞的靓仔,你最帅哦!
源码已收录github 查看源码,别忘了star哦!
开题
初入博客圈,第一个编写的专题定位在设计模式,前面已经完成了部分设计模式的内容,设计模式是框架架构设计的基础,不能说懂设计模式才会懂框架,但懂设计模式一定可以更好的懂框架,而对设计模式深入了解后,当工作中遇到需求或者问题的时候,甚至能够自然而然的想到用设计模式来解决。
更重要的是,我们学习技术是为了提升自我,找到一份好的工作。那么作为面试中的高频题目,非常有必要掌握。本文通过理论 + 实践 + 源码解读的方式来详细的结束委派模式。
委派模式
委派模式并不复杂,和其字面含义类似,指委派者接收任务,然后将任务分配给具体做这个任务的对象。就像做项目一样,老板安排任务给项目经理,项目经理再将任务委托给具体做任务的人。
这样咋一看和代理模式非常类似,其实可以理解委托模式就是一种特殊的代理模式,他们的区别在于各自关注的点不同,代理模式通常注重代理的过程,如前置、后置、环绕等功能的增强;而委派模式不注重过程,注重任务的执行结果,就像老板,他不关心项目经理把这件事情安排给谁完成,他关注的是最终的结果。
委派模式主要涉及到三种角色:委派者、工作者、任务,
如上个图例,委派者即项目经理,工作者即产品、架构师、后端,任务即要做的事情。下面通过代码实现。
工作者接口
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中查询,可以查到一大堆,几十上百个不止。
我们找一个比较熟悉的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完成的,类图结构如下。
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还不是真正解析的类,而是将解析的工作交由解析器处理。我们再将类图关系完善,整个解析处理流程如下。
至此,整个处理流程已经清晰。简单总结。
Spring从XML解析到BeanDefinition流程如下。
XmlBeanDefinitionReader为起点,任务交由DefaultBeanDefinitionDocumentReader处理,DefaultBeanDefinitionDocumentReader通过委托类BeanDefinitionParserDelegate将解析任务委托给真正的解析器BeanDefinitionParser的实现类处理。