命令模式

这篇文章描述的第一个行为设计模式是命令。它允许将请求封装在一个对象内并附加一个回调动作(每次遇到所所谓的回调大家就只需要理解为一个函数方法就好,省的去浪费那么多脑子)。请求被封装在命令对象之下,而请求的结果被发送到接收者。命令本身不是由调用者执行。为了直白了解其中的主要思想,想象一下管理服务器的情况(远程通过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的实例)参数的访问者模式

Spring框架中的设计模式(五)相关推荐

  1. Spring框架中的设计模式(一)

    设计模式有助于遵循良好的编程实践.作为最流行的Web框架之一的Spring框架也使用其中的一些. 本文将介绍Spring Framework中使用的设计模式.这是5篇专题文章的第一部分.这次我们将发现 ...

  2. Spring框架中的设计模式(四)

    适配器 当我们需要在给定场景下(也就是给定接口)想要不改变自身行为而又想做到一些事情的情况下(就是我给电也就是接口了,你来做事也就是各种电器),使用适配器设计模式(这里再说一点,就相当于我们再一个规章 ...

  3. Spring框架中的设计模式(二)

    在上一篇中我们在Spring中所谈到的设计模式涉及到了创建模式三剑客和1个行为模式(解释器模式).这次我们会将眼光更多地关注在具有结构性和行为性的设计模式上. 在这篇文章中,我们将看到每个类型的两种模 ...

  4. Spring框架中的设计模式(三)

    原型模式 这篇文章的第一个设计模式是原型.可以通过官方文档查找有关Spring作用域中的bean作用域的文章中介绍了类似的概念(prototype).原型设计模式与有用相同名称的(prototype) ...

  5. Spring框架中常用的设计模式详解

    一.浅谈控制反转(IOC)与依赖注入(DI) IOC(Inversion of Control)是Spring中一个非常重要的概念,它不是什么技术,而是一种解耦的设计思想.它主要的额目的是借助于第三方 ...

  6. Spring框架中的控制反转和依赖注入

    控制反转: 控制反转是用来降低代码之间的耦合度的,基本思想就是借助"第三方"实现具有依赖对象的解耦. 为什么需要控制反转,因为项目中对象或多或少存在耦合.控制反转的关键在于Ioc容 ...

  7. Spring框架中的Bean

    1.什么是Bean? 在Spring框架中,Bean是指一个由Spring容器管理的对象.这个对象可以是任何一个Java类的实例,例如数据库连接.业务逻辑类.控制器等等.Bean实例的创建和管理是由S ...

  8. 浅谈Spring框架应用的设计模式(一)——工厂模式

    文章目录 前言 一.工厂模式介绍 1.简单工厂模式 (1)静态工厂模式 (2)利用反射机制实现的简单工厂 2.工厂方法模式 3.抽象工厂模式 二.Spring框架中工厂模式的重要应用 1.BeanFa ...

  9. 在Spring 框架中如何更有效的使用JDBC?

    使用Spring JDBC 框架,资源管理以及错误处理的代价都会减轻.开发人员只需通过statements 和queries 语句从数据库中存取数据.Spring 框架中通过使用模板类能更有效的使用J ...

最新文章

  1. python基础第三章选择结构答案-python3 学习笔记(二)选择结构、循环结构
  2. 20145209刘一阳《JAVA程序设计》第七周课堂测试
  3. 经常使用计算机的孩子,常玩电脑对孩子负面影响大,家长们不容小觑!
  4. kissy 淘宝网脚本库
  5. selenium 验证元素是否存在_使用selenium判断标签的元素值是否存在
  6. 有Excel、Tableau、PowerBI都能做数据分析,为什么还要用Python
  7. 51单片机c语言编程20个实例,51单片机C语言编程,20个实例..pdf
  8. 基因定相(Phasing) 与 SHAPEIT 原理简介
  9. 洛谷.3733.[HAOI2017]八纵八横(线性基 线段树分治 bitset)
  10. php excel 设置下拉菜单,phpexcel setFormula下拉选择列表 序列
  11. z-blog建立博客网站SEO优化细节教程
  12. 查询条件中含有加号_中国邮储银行信用卡公众号账单查询
  13. linux curl命令详解(附使用示例)
  14. 【C语言】按位运算符
  15. 汽车VIN编号 有效性检查 PHP 验证算法
  16. 美国春季计算机硕士入学的学校,美国硕士春季入学学校推荐哪些?
  17. vue2+element-ui创建顶部导航栏及下拉菜单
  18. java房地产销售系统javaweb-sqlservice
  19. ​pmp是什么证,如何考取?
  20. 澳洲中学组织加密货币信息之夜

热门文章

  1. CentOS 6.7安装Storm 0.9.7
  2. 解决重写父类的方法且不会影响继承的子类的问题
  3. Monkey基本用法与常用参数
  4. 使用maven编译YCSB0.1.4对cassandra进行性能测试
  5. 《价值50亿的10句话》读后感(学生作业分享)
  6. 解读电感和电容在交流电路中的作用
  7. 利用耦合隐马尔可夫模型改进交易策略
  8. VC++下命名管道编程的原理及实现
  9. Android NDK开发——Android studio使用JNI调用OpenCV处理图像
  10. python3 32位_Python3.8.2安装教程