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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Spring框架中的设计模式(五)

通过以前的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的实例)参数的访问者模式

目录
相关文章
|
12天前
|
前端开发 Java 数据库连接
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
58 21
Spring框架初识
|
3月前
|
XML 安全 Java
|
4月前
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
162 0
|
3天前
|
存储 人工智能 开发框架
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
第二届开放原子大赛 Spring AI Alibaba 应用框架挑战赛决赛于 2 月 23 日在北京圆满落幕。
|
2月前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
127 29
|
19天前
|
XML Java 开发者
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
|
2天前
|
存储 人工智能 开发框架
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
|
2月前
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
4月前
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
235 53