根据相互依赖关系对Spring bean进行排序

时间:2019-07-12 22:01:24

标签: java spring sorting

我有一个Java8 / Spring应用程序。定义了一个接口DataCalculator

public interface Calculator
{
    public void calculate(Data data);
}

我有几个实现此接口的具体类,它们都用@Component注释,因此它们是Spring bean(单个)。我想运行所有这些,所以我使用了自动装配的列表:

public class MyApplication
{
    private List<Calculator> calculators;

    @Autowired //Technically not needed, added for clarity
    public MyApplication(List<Calculator> calculators)
    {
        this.calculators = calculators;
    }

    public void calculateAll(Collection<Data> dataCollection)
    {
        for(Data data : dataCollection)
        {
            for(Calculator calculator : this.calculators)
            {
                calculator.calculate(data);
            }
        }
    }
}

这可行,但是我偶然发现某些计算器必须先依赖其他计算器才能完成的要求。我看到了三种实现方式,每种都有优点/缺点:

  1. 在需要首先运行的那些计算器上添加@Order(x)批注。优点:程序简洁。缺点:如果依赖关系数量很大或很深,开发人员添加或编辑计算器会非常麻烦。信息分散在所有计算器上,而不是集中可用。
  2. 不要使用spring bean,而是实例化中央类中的所有计算器,并按首选顺序将它们添加到列表中。优点:很清楚存在哪个顺序。缺点:我们失去了自动布线的自动功能。尽管声明的计算器的现有顺序很明确,但是插入新计算器并不总是很明显(可能只是在末尾附加“只是为了确定”,而不是按类别分组)。
  3. 创建自定义注释@CalcDependency(value = OtherCalculator.class),并在计算器列表上运行拓扑排序算法以对它们进行排序,然后再使用它们。优点:排序逻辑放在一个地方,每个计算器都表达它们所依赖的内容,而不是它们所具有的任意“顺序”。易于插入新计算器:您只需声明依赖项,而不是顺序。 (如果检测到周期性依赖关系,则将引发错误)缺点:要编写大量的工作,似乎有点过头了。

有什么想法吗? Spring是否有某种方式声明依赖项,因此在自动装配列表时会予以考虑?

我知道spring有一个@dependsOn注释,但这在这种情况下似乎没有用。

1 个答案:

答案 0 :(得分:1)

您可以编写包装器类,该包装器类具有一些可用于对计算器进行排序的顺序字段,或者可以将该字段简单地添加到bean或添加更多接口...选项供您选择。

在您的“ order”字段中进行简单排序的计算器之后,使用foreach。

示例在下面的评论中问。 (这完全不是生产就绪的代码,只是为了向您展示示例)

package org.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Comparator;
import java.util.List;

@Configuration
@ComponentScan
public class Main {

    @Autowired
    List<Calculator> calculators;

    public void start() {
        calculators.stream().sorted(Comparator.comparing(Calculator::getOrder)).forEach(Calculator::calculate);

    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(Main.class);
        ctx.refresh();

        Main main = ctx.getBean(Main.class);
        main.start();
    }
}

interface Calculator {
    void calculate();

    int getOrder();
}

@Component
class FirstCalc implements Calculator {
    @Override
    public void calculate() {
        System.out.println(String.format("firstCalc with order %s", getOrder()));
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

@Component
class SecondCalc implements Calculator {
    @Override
    public void calculate() {
        System.out.println(String.format("secondCalc with order %s", getOrder()));
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

@Component
class ThirdCalc implements Calculator {
    @Override
    public void calculate() {
        System.out.println(String.format("thirdCalc with order %s should be 3rd", getOrder()));
    }

    @Override
    public int getOrder() {
        return 2;
    }
}

@Component
class FourthCalc implements Calculator {
    @Override
    public void calculate() {
        System.out.println(String.format("lastCalc with order %s should be last", getOrder()));
    }

    @Override
    public int getOrder() {
        return 3;
    }
}

结果将是:

firstCalc with order 1
secondCalc with order 1
thirdCalc with order 2 should be 3rd
lastCalc with order 3 should be last