⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://muyinchen.github.io/2017/08/01/Spring%E6%A1%86%E6%9E%B6%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F(%E4%BA%94)/ 「一叶知秋」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

通过以前的4篇文章,我们看到Spring采用了大量的关于创建和结构方面的设计模式。

本文将描述属于行为方面的两种设计模式:命令和访问者。

命令模式

这篇文章描述的第一个行为设计模式是命令。它允许将请求封装在一个对象内并附加一个回调动作(每次遇到所所谓的回调大家就只需要理解为一个函数方法就好,省的去浪费那么多脑子)。请求被封装在命令对象之下,而请求的结果被发送到接收者。命令本身不是由调用者执行。为了直白了解其中的主要思想,想象一下管理服务器的情况(远程通过ssh操作Linux服务器)。管理员(invoker)在命令行(commands)中启动一些操作,将结果发送到服务器(接收器)。在这里,所有这一切都是由客户端的终端(也就是我们用的xshell)来完成的。搞个Demo来说明一下(对于命令,它的动作就是执行,对于管理员来讲,我们的动作其实就是一个回车,执不执行当然是管理员说的算了,执行交给命令对象了,服务器最后就是一个展示结果):

public class CommandTest {

// This test method is a client
@Test
public void test() {
Administrator admin = new Administrator();
Server server = new Server();

// start Apache
admin.setCommand(new StartApache(server));
admin.typeEnter();

// start Tomcat
admin.setCommand(new StartTomcat(server));
admin.typeEnter();

// check executed commands
int executed = server.getExecutedCommands().size();
assertTrue("Two commands should be executed but only "+
executed+ " were", executed == 2);
}

}

// commands
abstract class ServerCommand {

protected Server server;

public ServerCommand(Server server) {
this.server = server;
}

public abstract void execute();
}

class StartTomcat extends ServerCommand {

public StartTomcat(Server server) {
super(server);
}

@Override
public void execute() {
server.launchCommand("sudo service tomcat7 start");
}
}

class StartApache extends ServerCommand {

public StartApache(Server server) {
super(server);
}

@Override
public void execute() {
server.launchCommand("sudo service apache2 start");
}
}

// invoker
class Administrator {

private ServerCommand command;

public void setCommand(ServerCommand command) {
this.command = command;
}

public void typeEnter() {
this.command.execute();
}

}

// receiver
class Server {

// as in common terminals, we store executed commands in history
private List<String> executedCommands = new ArrayList<String>();

public void launchCommand(String command) {
System.out.println("Executing: "+command+" on server");
this.executedCommands.add(command);
}

public List<String> getExecutedCommands() {
return this.executedCommands;
}

}

测试应通过并打印两个命令:

Executing: sudo service apache2 start on server
Executing: sudo service tomcat7 start on server

命令模式不仅允许封装请求(ServerCommand)并将其传输到接收器(Server),而且还可以更好地处理给定的请求。在这里,这种更好的处理是通过存储命令的执行历史。在Spring中,我们在beanFactory后置处理器的特性中来找到指令设计模式的原理。要通过快速对它们进行定义,应用程序上下文会启动后置处理器,并可以用来对创建的bean进行一些操作(这里不打算细说了,具体的我后面会专门写一篇这方面的文章,来分析其中的源码细节)。

当我们将先前Demo里呈现的命令逻辑转换并对比到Spring bean工厂后处理器时,我们可以区分以下actors后置处理器bean(是指实现BeanFactoryPostProcessor接口)是命令,org.springframework.context.support.PostProcessorRegistrationDelegate是调用者(它执行postProcessBeanFactory方法注册所有的后置处理器bean,此处看下面第二段代码)和接收器org.springframework.beans.factory.config.ConfigurableListableBeanFactory可以在元素(bean)构造初始化之前修改它们(例如:在初始化bean之前可以更改属性)。

另外,回顾下上面的那个Demo,和我们的Demo中的命令历史管理一样。PostProcessorRegistrationDelegate包含一个内部类BeanPostProcessorChecker,它可以记录当一个bean不符合处理条件的情况。

可以观察PostProcessorRegistrationDelegate中的两段代码:

/**
* BeanPostProcessor that logs an info message when a bean is created during
* BeanPostProcessor instantiation, i.e. when a bean is not eligible for
* getting processed by all BeanPostProcessors.
*/
private static class BeanPostProcessorChecker implements BeanPostProcessor {

private static final Log logger = LogFactory.getLog(BeanPostProcessorChecker.class);

private final ConfigurableListableBeanFactory beanFactory;

private final int beanPostProcessorTargetCount;

public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) {
this.beanFactory = beanFactory;
this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null && !(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' of type [" + bean.getClass() +
"] is not eligible for getting processed by all BeanPostProcessors " +
"(for example: not eligible for auto-proxying)");
}
}
return bean;
}

private boolean isInfrastructureBean(String beanName) {
if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
return RootBeanDefinition.ROLE_INFRASTRUCTURE == bd.getRole();
}
return false;
}
}

定义后的调用,用的就是ConfigurableListableBeanFactory的实例(看BeanPostProcessorChecker注释):

public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
//BeanPostProcessorChecker
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}

// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(beanFactory, orderedPostProcessors);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);

// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(beanFactory, internalPostProcessors);
registerBeanPostProcessors(beanFactory, internalPostProcessors);

// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

总结一个过程就是,我要BeanFactory里面得到对象(也就是为了得到一个命令的执行结果),那么,想要在得到对象的时候就已经实现了一些对其修改的想法,那么就通过后置处理器,也是就实现了后置处理器接口的beans(命令里可以通过传入不同的参数来得到不同结果,或者对命令的脚本进行修改),然后还需要一个执行者(我们在做自动化运维的时候,不止操作一个脚本,这里的PostProcessorRegistrationDelegate就是集中来管理这些的),最后得到的结果就由BeanFactory来展示咯。

访问者模式

接下来要介绍的一个行为设计模式是Visitor:抽象一点就是通过另一种类型的对象来使一个对象访问。在这个简短定义中,使用这个设计模式中的对象将被视为访问者或对象可被访问。第一个访问者要有可访问支持。这个模式的一个现实的例子可以是一个汽车质检员,他们检查一些汽车零件,比如轮子,制动器和发动机,以判断汽车质量是否合格。我们来做个JUnit测试用例:

public class VisitorTest {

@Test
public void test() {
CarComponent car = new Car();
Mechanic mechanic = new QualifiedMechanic();
car.accept(mechanic);
assertTrue("After qualified mechanics visit, the car should be broken",
car.isBroken());
Mechanic nonqualifiedMechanic = new NonQualifiedMechanic();
car.accept(nonqualifiedMechanic);
assertFalse("Car shouldn't be broken becase non qualified mechanic " +
" can't see breakdowns", car.isBroken());
}

}

// visitor
interface Mechanic {
public void visit(CarComponent element);
public String getName();
}

class QualifiedMechanic implements Mechanic {

@Override
public void visit(CarComponent element) {
element.setBroken(true);
}

@Override
public String getName() {
return "qualified";
}
}

class NonQualifiedMechanic implements Mechanic {

@Override
public void visit(CarComponent element) {
element.setBroken(true);
}

@Override
public String getName() {
return "unqualified";
}
}

// visitable
abstract class CarComponent {
protected boolean broken;

public abstract void accept(Mechanic mechanic);

public void setBroken(boolean broken) {
this.broken = broken;
}

public boolean isBroken() {
return this.broken;
}
}

class Car extends CarComponent {

private boolean broken = false;
private CarComponent[] components;

public Car() {
components = new CarComponent[] {
new Wheels(), new Engine(), new Brake()
};
}

@Override
public void accept(Mechanic mechanic) {
this.broken = false;
if (mechanic.getName().equals("qualified")) {
int i = 0;
while (i < components.length && this.broken == false) {
CarComponent component = components[i];
mechanic.visit(component);
this.broken = component.isBroken();
i++;
}
}
// if mechanic isn't qualified, we suppose that
// he isn't able to see breakdowns and so
// he considers the car as no broken
// (even if the car is broken)
}

@Override
public boolean isBroken() {
return this.broken;
}
}

class Wheels extends CarComponent {

@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}

class Engine extends CarComponent {

@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}

class Brake extends CarComponent {

@Override
public void accept(Mechanic mechanic) {
mechanic.visit(this);
}
}

在这个例子中,我们可以看到他们有两个机制(访问者,其实就是免检和不免检):合格和不合格。暴露于他们的可见对象是汽车。通过其接受方式,决定哪个角色应该适用于被访问者(通过代码mechanic.getName().equals("qualified")来判断)。当访问者合格时,Car让他分析所有组件。如果访问者不合格,Car认为其干预是无用的,并且在方法isBroken()中直接返回false(其实就是为了达到一个免检的效果)。Spring在beans配置中实现了访问者设计模式。为了观察,我们可以看看org.springframework.beans.factory.config.BeanDefinitionVisitor对象,该对象用于解析bean元数据并将其解析为String(例如:具有作用域或工厂方法名称的XML属性)或Object(例如:构造函数定义中的参数)。已解析的值在与分析的bean关联的BeanDefinition实例中进行判断设置。具体的源码请看BeanDefinitionVisitor的代码片段:

/**
* Traverse the given BeanDefinition object and the MutablePropertyValues
* and ConstructorArgumentValues contained in them.
* @param beanDefinition the BeanDefinition object to traverse
* @see #resolveStringValue(String)
*/
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.
getConstructorArgumentValues();
visitIndexedArgumentValues(cas.
getIndexedArgumentValues());
visitGenericArgumentValues(cas.
getGenericArgumentValues());
}

protected void visitParentName(BeanDefinition beanDefinition) {
String parentName = beanDefinition.getParentName();
if (parentName != null) {
String resolvedName = resolveStringValue(parentName);
if (!parentName.equals(resolvedName)) {
beanDefinition.setParentName(resolvedName);
}
}
}

在这种情况下,他们只是访问方式,没有对访问者做任何补充的控制(在Demo里对car的质检员做了控制)。这里访问包括分析给定BeanDefinition的参数,并将其替换为已解析对象。

在最后一篇关于Spring中设计模式的文章中,我们发现了2种行为模式:用于处理bean工厂的后置处理的命令模式用于将定义的bean参数转换为面向对象(String或Object的实例)参数的访问者模式

666. 彩蛋

如果你对 Spring 感兴趣,欢迎加入我的知识星球一起交流。

知识星球

文章目录
  1. 1. 命令模式
  2. 2. 访问者模式
  • 666. 彩蛋