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