Skip to content

调用链抽象及节点支持自动注入

472字约2分钟

2025-02-08

大部分系统中,可能都会用到调用链模式,并且同时存在多个调用链。我们希望在使用时,可以自动完成调用链路的构建以及支持自动注入,从而减少代码的冗余,提高开发效率。本文将说明如何利用Spring扩展点,实现调用链的抽象及节点自动注入。

定义接口

要实现调用链模式,调用链需要与节点实现同一个接口,并且节点需要支持自动设置下一个节点,而调用链无需设置。基于以上特点,抽象出以下接口。

/**
 * 描述是否具有可执行的能力
 * @param S 节点输入参数类型
 * @param U 节点输出参数类型
 */
public interface Invoker<S, U> {

  /**
   * 执行当前节点
   */
  U invoke(S source);
}
/**
 * 描述节点是否支持链式调用
 * @param T 节点类型
 * @param S 节点输入参数类型
 * @param U 节点输出参数类型
 */
public interface ChainableInvoker<T extends ChainableInvoker, S, U>
                    extends Invoker<S, U> {

  /**
   * 设置下一个节点
   */
  void setNext(T node);
}
public abstract class AbstractChain<T extends ChainableInvoker<T, S, U>, S, U>
  extends Invoker<S, U>, BeanDefinitionRegistryPostProcessor, SmartInitializer {

  /**
   * 节点列表
   */
  private List<T> nodes = new ArrayList<>();

  /**
   * 节点类型列表
   */
  private List<Class<T>> ndoesClass = new ArrayList<>();

  private BeanFactory beanFactory;

  /**
   * 链路头节点
   */
  private T head;

  @Override
  public void afterSingletonsInstantiated() {
    buildChain();
  }

  @Override
  public U inovke(S source) {
    return head.invoke(source);
  }

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if(nodeClasses.isEmpty()) {
      throw new FatalBeanException("节点类型列表为空");
    }
    for (Class<T> nodeClass : ndoesClass) {
      final int index = i;
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setInstanceSupplier(() -> nodes.get(index));
      beanDefinition.setBeanClass(nodeClass);
      registry.registerBeanDefinition(DefaultBeanDefinitionNameGenerator.INSTANCE.generateBeanName(beanDefinition, registry), beanDefinition);
    }
  }

  @Override
  public void postProcessBeanFactory(BeanFactory beanFactory) {
    this.beanFactory = beanFactory;
  }

  public void addNode(T node) {
    this.nodes.add(node);
    this.nodeClasses.add((Class<T>) node.getClass());
  }

  private void buildChain() {
    for (int i = 0; i < nodes.size(); i++) {
      T currentNode = beanFactory.getBean(nodeClasses.get(i));
      T nextNode = i < nodes.size() - 1 ? beanFactory.getBean(nodeClasses.get(i + 1)) : null;
      if(head == null) {
        head = currentNode;
      }
      currentNode.setNext(nextNode);
    }
  }
}