Spring Beans中

时间:2018-03-10 09:20:47

标签: java spring dependency-injection

在我的代码中有一个Bean Admin Admin 的其中一项操作是创建任务启动

任务执行的操作相当复杂。因此它被分成几个不同的 Step 类。

这就是我的代码看起来的样子

public interface Admin{
    void start();

    //other methods
}

public class AdminImpl implements Admin{
    private BeanA bean1;
    private BeanB bean2;
    //other fields

    public Admin(BeanA bean1,
        BeanB bean2,
        BeanC bean3
        //Lot more parameters
        BeanO bean15
    ){
        this.bean1 = bean1;
        this.bean2 = bean2;
        //and so on
    }

    public void start(){
        return new Task(bean1, bean2, bean3,...).start()
    }

    //other methods
}

public class Task{
    private BeanA bean1;
    private BeanB bean2;
    //other fields
    public Task(BeanA bean1,
        BeanB bean2,
        BeanC bean3
        //Lot more parameters
    ){
        //bind parameters to fields
    }

    public void start(){
        new Step1(bean1, bean2,.. other parameters).do();
        new Step2(bean3, bean7,.. other parameters).do();
        //more steps
    }
}

@Configuration
public class MyConfiguration{
    @Bean
    public Admin admin(BeanA bean1, BeanB bean2....){
        return new AdminImpl(bean1, bean2...);
    }
}

正如您所看到的,每个 Step 类都有2个或3个Bean依赖项。 Step 类不是Spring Beans,因此它们是从 Task 传递的依赖项。 Task 也不是Spring Bean,因此它从 Admin 获取依赖关系。 这导致 Admin 拥有太多依赖关系(~15)。

我试过了:https://dzone.com/articles/autowiring-spring-beans-into-classes-not-managed-by-spring

基本上,您创建了一个名为BeanUtil的服务Bean,它是ApplicationContextAware。静态方法getBean使用ApplicationContext获取bean。

Step class现在看起来像这样:

class Step{
    public Step(){
        BeanA bean1 = BeanUtil.getBean(BeanA.class);
        BeanB bean2 = BeanUtil.getBean(BeanB.class);
    }

    public void do(){
        //do stuff 
    }
}

这解决了最初的问题,但后来我在测试时遇到了困难。 这就是测试类现在的样子。

 @ContextConfiguration(loader = AnnotationConfigContextLoader.class)
    public class Step1Test extends AbstractTestNGSpringContextTests {

        @Test
        public void test(){
            Step1 step = new Step().do();
        }

        @Configuration
        static class MockConfiguration {

            @Bean
            public BeanA beanA() {
                BeanA mockBeanA=Mockito.mock(BeanA.class);
                // set behavior of mock
                return mockBeanA;
            }

            @Bean
            public BeanUtil beanUtil(){
                return new BeanUtil();
            }
        }
    }

您无法在不创建不同配置类的情况下更改不同测试用例的模拟行为。这就像通过创建另一个问题来解决一个问题。

这是Spring开发人员面临的一个常见问题,其中抽象级别较高的类最终会产生过多的依赖关系吗?或者我的设计有问题吗?处理或避免这种情况的正确方法是什么?

其他似乎相似但不是

的问题

编辑:  我从user7得到的一个建议是我将BeanX分组并将抽象传递给Admin。 bean没有任何我可以利用的逻辑分组。此外,许多 Steps 需要对bean进行完全访问(访问界面中的所有方法)。这会导致抽象变得臃肿。

1 个答案:

答案 0 :(得分:1)

你可以将Spring bean作为原型bean(因为它们是有状态的,你每次都想要一个不同的实例),并在你的Task bean中注入Provider<Step>(如果我理解正确,可以是单身豆)。

例如:

public class Step1 {
    private Bean1 bean1;
    private Bean2 bean2;

    private final String someValue;
    private final String someOtherValue;

    public Step(String someValue, String someOtherValue) {
        this.someValue = someValue;
        this.someOtherValue = someOtherValue;
    }

    @Autowired
    public void setBean1(Bean1 bean1) {
        this.bean1 = bean1;
    }

    @Autowired
    public void setBean2(Bean2 bean2) {
        this.bean2 = bean2;
    }

    do() {
        // ...
    }
}

在配置类中,然后将各种步骤定义为bean,方法需要所需的参数:

@Bean
@Scope("prototype")
public Step1 step1(String someValue, String someOtherValue) {
    return new Step(someValue, someOtherValue);
}

在Task bean中,您注入ObjectProvider<Step1>

private ObjectProvider<Step1> stepProvider;

public Service(ObjectProvider<Step1> step1Provider) {
    this.stepProvider = stepProvider;
}

public void start() {
    Step1 step1 = step1Provider.getObject("a", "b");
    step1.do();
}