查看原文
其他

Spring框架中的设计模式(五)

2017-09-04 知秋 程序猿DD

通过以前的4篇文章,我们看到Spring采用了大量的关于创建和结构方面的设计模式。本文将描述属于行为方面的两种设计模式:命令和访问者。

前传:

命令模式

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

  1. public class CommandTest {

  2.  // This test method is a client

  3.  @Test

  4.  public void test() {

  5.    Administrator admin = new Administrator();

  6.    Server server = new Server();

  7.    // start Apache

  8.    admin.setCommand(new StartApache(server));

  9.    admin.typeEnter();

  10.    // start Tomcat

  11.    admin.setCommand(new StartTomcat(server));

  12.    admin.typeEnter();

  13.    // check executed commands

  14.    int executed = server.getExecutedCommands().size();

  15.    assertTrue("Two commands should be executed but only "+

  16.      executed+ " were", executed == 2);

  17.  }

  18. }

  19. // commands

  20. abstract class ServerCommand {

  21.  protected Server server;

  22.  public ServerCommand(Server server) {

  23.    this.server = server;

  24.  }

  25.  public abstract void execute();

  26. }

  27. class StartTomcat extends ServerCommand {

  28.  public StartTomcat(Server server) {

  29.    super(server);

  30.  }

  31.  @Override

  32.  public void execute() {

  33.    server.launchCommand("sudo service tomcat7 start");

  34.  }

  35. }

  36. class StartApache extends ServerCommand {

  37.  public StartApache(Server server) {

  38.    super(server);

  39.  }

  40.  @Override

  41.  public void execute() {

  42.    server.launchCommand("sudo service apache2 start");

  43.  }

  44. }

  45. // invoker

  46. class Administrator {

  47.  private ServerCommand command;

  48.  public void setCommand(ServerCommand command) {

  49.    this.command = command;

  50.  }

  51.  public void typeEnter() {

  52.    this.command.execute();

  53.  }

  54. }

  55. // receiver

  56. class Server {

  57.  // as in common terminals, we store executed commands in history

  58.  private List<String> executedCommands = new ArrayList<String>();

  59.  public void launchCommand(String command) {

  60.    System.out.println("Executing: "+command+" on server");

  61.    this.executedCommands.add(command);

  62.  }

  63.  public List<String> getExecutedCommands() {

  64.    return this.executedCommands;

  65.  }

  66. }

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

  1. Executing: sudo service apache2 start on server

  2. Executing: sudo service tomcat7 start on server

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

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

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

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

  1. /**

  2.     * BeanPostProcessor that logs an info message when a bean is created during

  3.     * BeanPostProcessor instantiation, i.e. when a bean is not eligible for

  4.     * getting processed by all BeanPostProcessors.

  5.     */

  6.    private static class BeanPostProcessorChecker implements BeanPostProcessor {

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

  8.        private final ConfigurableListableBeanFactory beanFactory;

  9.        private final int beanPostProcessorTargetCount;

  10.        public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) {

  11.            this.beanFactory = beanFactory;

  12.            this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;

  13.        }

  14.        @Override

  15.        public Object postProcessBeforeInitialization(Object bean, String beanName) {

  16.            return bean;

  17.        }

  18.        @Override

  19.        public Object postProcessAfterInitialization(Object bean, String beanName) {

  20.            if (bean != null && !(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&

  21.                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {

  22.                if (logger.isInfoEnabled()) {

  23.                    logger.info("Bean '" + beanName + "' of type [" + bean.getClass() +

  24.                            "] is not eligible for getting processed by all BeanPostProcessors " +

  25.                            "(for example: not eligible for auto-proxying)");

  26.                }

  27.            }

  28.            return bean;

  29.        }

  30.        private boolean isInfrastructureBean(String beanName) {

  31.            if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {

  32.                BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);

  33.                return RootBeanDefinition.ROLE_INFRASTRUCTURE == bd.getRole();

  34.            }

  35.            return false;

  36.        }

  37.    }

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

  1. public static void registerBeanPostProcessors(

  2.            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

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

  4.        // Register BeanPostProcessorChecker that logs an info message when

  5.        // a bean is created during BeanPostProcessor instantiation, i.e. when

  6.        // a bean is not eligible for getting processed by all BeanPostProcessors.

  7.        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;

  8.  //BeanPostProcessorChecker

  9.        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

  10.        // Separate between BeanPostProcessors that implement PriorityOrdered,

  11.        // Ordered, and the rest.

  12.        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();

  13.        List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();

  14.        List<String> orderedPostProcessorNames = new ArrayList<>();

  15.        List<String> nonOrderedPostProcessorNames = new ArrayList<>();

  16.        for (String ppName : postProcessorNames) {

  17.            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {

  18.                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);

  19.                priorityOrderedPostProcessors.add(pp);

  20.                if (pp instanceof MergedBeanDefinitionPostProcessor) {

  21.                    internalPostProcessors.add(pp);

  22.                }

  23.            }

  24.            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {

  25.                orderedPostProcessorNames.add(ppName);

  26.            }

  27.            else {

  28.                nonOrderedPostProcessorNames.add(ppName);

  29.            }

  30.        }

  31.        // First, register the BeanPostProcessors that implement PriorityOrdered.

  32.        sortPostProcessors(beanFactory, priorityOrderedPostProcessors);

  33.        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

  34.        // Next, register the BeanPostProcessors that implement Ordered.

  35.        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();

  36.        for (String ppName : orderedPostProcessorNames) {

  37.            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);

  38.            orderedPostProcessors.add(pp);

  39.            if (pp instanceof MergedBeanDefinitionPostProcessor) {

  40.                internalPostProcessors.add(pp);

  41.            }

  42.        }

  43.        sortPostProcessors(beanFactory, orderedPostProcessors);

  44.        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

  45.        // Now, register all regular BeanPostProcessors.

  46.        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();

  47.        for (String ppName : nonOrderedPostProcessorNames) {

  48.            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);

  49.            nonOrderedPostProcessors.add(pp);

  50.            if (pp instanceof MergedBeanDefinitionPostProcessor) {

  51.                internalPostProcessors.add(pp);

  52.            }

  53.        }

  54.        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

  55.        // Finally, re-register all internal BeanPostProcessors.

  56.        sortPostProcessors(beanFactory, internalPostProcessors);

  57.        registerBeanPostProcessors(beanFactory, internalPostProcessors);

  58.        // Re-register post-processor for detecting inner beans as ApplicationListeners,

  59.        // moving it to the end of the processor chain (for picking up proxies etc).

  60.        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));

  61.    }

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

访问者模式

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

  1. public class VisitorTest {

  2.  @Test

  3.  public void test() {

  4.    CarComponent car = new Car();

  5.    Mechanic mechanic = new QualifiedMechanic();

  6.    car.accept(mechanic);

  7.    assertTrue("After qualified mechanics visit, the car should be broken",

  8.      car.isBroken());

  9.    Mechanic nonqualifiedMechanic = new NonQualifiedMechanic();

  10.    car.accept(nonqualifiedMechanic);

  11.    assertFalse("Car shouldn't be broken becase non qualified mechanic " +

  12.      " can't see breakdowns", car.isBroken());

  13.  }

  14. }

  15. // visitor

  16. interface Mechanic {

  17.  public void visit(CarComponent element);

  18.  public String getName();

  19. }

  20. class QualifiedMechanic implements Mechanic {

  21.  @Override

  22.  public void visit(CarComponent element) {

  23.    element.setBroken(true);

  24.  }

  25.  @Override

  26.  public String getName() {

  27.    return "qualified";

  28.  }

  29. }

  30. class NonQualifiedMechanic implements Mechanic {

  31.  @Override

  32.  public void visit(CarComponent element) {

  33.    element.setBroken(true);

  34.  }

  35.  @Override

  36.  public String getName() {

  37.    return "unqualified";

  38.  }

  39. }

  40. // visitable

  41. abstract class CarComponent {

  42.  protected boolean broken;

  43.  public abstract void accept(Mechanic mechanic);

  44.  public void setBroken(boolean broken) {

  45.    this.broken = broken;

  46.  }

  47.  public boolean isBroken() {

  48.    return this.broken;

  49.  }

  50. }

  51. class Car extends CarComponent {

  52.  private boolean broken = false;

  53.  private CarComponent[] components;

  54.  public Car() {

  55.    components = new CarComponent[] {

  56.      new Wheels(), new Engine(), new Brake()

  57.    };

  58.  }

  59.  @Override

  60.  public void accept(Mechanic mechanic) {

  61.    this.broken = false;

  62.    if (mechanic.getName().equals("qualified")) {

  63.      int i = 0;

  64.      while (i < components.length && this.broken == false) {

  65.        CarComponent component = components[i];

  66.        mechanic.visit(component);

  67.        this.broken = component.isBroken();

  68.        i++;

  69.      }

  70.    }

  71.    // if mechanic isn't qualified, we suppose that

  72.    // he isn't able to see breakdowns and so

  73.    // he considers the car as no broken

  74.    // (even if the car is broken)

  75.  }

  76.  @Override

  77.  public boolean isBroken() {

  78.          return this.broken;

  79.  }

  80. }

  81. class Wheels extends CarComponent {

  82.  @Override

  83.  public void accept(Mechanic mechanic) {

  84.    mechanic.visit(this);

  85.  }

  86. }

  87. class Engine extends CarComponent {

  88.  @Override

  89.  public void accept(Mechanic mechanic) {

  90.    mechanic.visit(this);

  91.  }

  92. }

  93. class Brake extends CarComponent {

  94.  @Override

  95.  public void accept(Mechanic mechanic) {

  96.    mechanic.visit(this);

  97.  }

  98. }

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

  1. /**

  2. * Traverse the given BeanDefinition object and the MutablePropertyValues

  3. * and ConstructorArgumentValues contained in them.

  4. * @param beanDefinition the BeanDefinition object to traverse

  5. * @see #resolveStringValue(String)

  6. */

  7. public void visitBeanDefinition(BeanDefinition beanDefinition) {

  8.  visitParentName(beanDefinition);

  9.  visitBeanClassName(beanDefinition);

  10.  visitFactoryBeanName(beanDefinition);

  11.  visitFactoryMethodName(beanDefinition);

  12.  visitScope(beanDefinition);

  13.  visitPropertyValues(beanDefinition.getPropertyValues());

  14.  ConstructorArgumentValues cas = beanDefinition.

  15.    getConstructorArgumentValues();

  16.  visitIndexedArgumentValues(cas.

  17.    getIndexedArgumentValues());

  18.  visitGenericArgumentValues(cas.

  19.    getGenericArgumentValues());

  20. }

  21. protected void visitParentName(BeanDefinition beanDefinition) {

  22.  String parentName = beanDefinition.getParentName();

  23.  if (parentName != null) {

  24.    String resolvedName = resolveStringValue(parentName);

  25.    if (!parentName.equals(resolvedName)) {

  26.      beanDefinition.setParentName(resolvedName);

  27.    }

  28.  }

  29. }

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

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

推荐阅读

长按指纹

一键关注


70 44786 70 31370 0 0 4642 0 0:00:09 0:00:06 0:00:03 7467

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存