代码之家  ›  专栏  ›  技术社区  ›  user1884155

基于互依关系的SpringBean排序

  •  0
  • user1884155  · 技术社区  · 6 年前

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

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

    我有几个实现这个接口的具体类,它们都用注释 @Component 所以它们是春豆(单子)。我想运行所有的列表,所以我使用了一个自动连线的列表:

    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) 需要先运行的计算器的注释。专业:简洁到程序。con:如果依赖性的数量很高或很深,开发人员添加或编辑计算器是非常麻烦的。信息分散在所有的计算器上,而不是集中提供。
    2. 不要使用SpringBeans,而是实例化一个中心类中的所有计算器,并按首选顺序将它们添加到列表中。pro:非常清楚哪个订单存在。康:我们失去自动布线的自动电源。虽然声明的计算器的现有顺序是明确的,但是插入新的计算器并不总是显而易见的(并且可能只是在末尾附加“只是为了确保”,而不是按类别分组)。
    3. 创建自定义批注 @CalcDependency(value = OtherCalculator.class) 并在计算器列表上运行拓扑排序算法,在使用前对其进行排序。赞成:排序逻辑是在一个地方,每个计算器表达他们所依赖的,而不是他们所拥有的一些任意的“顺序”。易于插入新的计算器:只需声明依赖项,而不是顺序。(如果检测到循环依赖项,则应该抛出一个错误)con:大多数编程工作量,可能看起来都太大了。

    思想?Spring是否有某种方法来声明在自动连接列表时会考虑到的依赖项?

    我知道春天有一个 @dependsOn 注释,但这似乎对本例不有用。

    0 回复  |  直到 6 年前
        1
  •  1
  •   Pavel Chernyak    6 年前

    您可以编写包装类,它有一些顺序字段,您可以使用这些字段对计算器进行排序,或者简单地将该字段添加到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