在基于Java的Spring配置中具有多个构造函数的Bean

时间:2014-01-19 11:43:10

标签: java spring dependency-injection refactoring inversion-of-control

我正在尝试重构某些应用程序以使用Spring DI而不是普通的java并坚持使用该问题。

基本上我有一个包含几个构造函数的类:

  public MyClass() {
    this(new A());
  }

  public MyClass(A a) {
    this(a, new B()));
  }

  public MyClass(String string) {
    this(new A(string));
  }

  public MyClass(A a, B b) {
    this.a = a;
    this.c = a.getC();
    this.b = b;
    this.d = b.getD();
  }

  public MyClass(A a, B b, D d) {
    this.a = a;
    this.c = a.getC();
    this.b = b;
    this.d = d;
  }

这些构造函数在很多地方使用,其中一些在代码中,一些在测试中,等等。

现在,我将介绍基于java的应用程序配置:

@Configuration
public class ApplicationConfiguration {

  @Bean
  MyClass myClass() {
    return null;
  }

}

尝试通过从应用程序上下文中获取bean来重写所有位置:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
MyClass myClass = (MyClass) context.getBean("myClass", arg1, arg2);

问题是在某些地方我只有arg1,在某些arg1和arg2中,在某些地方我没有args。那么,我怎么能在应用程序配置中表达它呢?

此外,豆是单音,所以如果我,例如创建几个具有不同参数的bean,则该要求将被破坏,即

@Configuration
public class ApplicationConfiguration {

  @Bean
  MyClass myClass1() {
    return new MyClass();
  }

  @Bean
  MyClass myClass2(A a) {
    return new MyClass(a);
  }

  //etc
}

绝对不是解决方案。

提前致谢

UPD。 看起来+ Avi答案是正确的,但我仍然不明白如何做正确的事。

我创建了一个junit4测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfiguration.class)
public class MyTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void setupMyClass() {
    //    myClass = new MyClass();
        myClass = (MyClass) applicationContext.getBean("myClass");
    }
}

所以,在这里我想在测试中使用MyClass,所以没有类似Foo的bean在Avi的答案中。

我修改了“不同场景的上下文,并且在每个场景中你需要用不同的参数构造MyClass - 你需要创建多个bean,每个bean都实例化它自己的MyClass”(我以错误的方式得到了这个短语,但是这就是我的意思):

@Configuration
public class ApplicationConfiguration {

  //Note: beans have the same name

  @Bean
  MyClass myClass() {
    return new MyClass();
  }

  @Bean
  MyClass myClass(A a) {
    return new MyClass(a);
  }

  //etc
}

但是现在还有另一个问题:applicationContext.getBean(“myClass”)随机返回(取决于具有相同名称和参数的bean的数量)bean而不是没有参数的bean。当我指定args - applicationContext.getBean(“myClass”,new Object [] {});它告诉我,只允许原型范围豆。但我想要一个单身豆。

看起来我需要另一个建议:如何摆脱配置中具有相同名称的几个bean?也许我需要一个聪明的工厂,或者@Autowired(required = false)可以在这里提供帮助吗?

即使我的测试中有类似Foo的对象,我应该如何在测试中使用它?

@Configuration
@Import(ApplicationConfiguration.class)
public class FooConfiguration {
     @Autowire
     MyClass myClass; //but which one constructor?

     @Bean
     Foo foo() {
         return new Foo(myClass);
     }

}

我不想在每个配置中创建MyClass,我想只有一个,我可以导入...

UPD2。

好的,我删除了所有构造函数,只留下了一个具有所有参数

的构造函数
@Configuration
public class ApplicationConfiguration {       

    @Bean
    MyClass myClass(A a, B b, C c, D d) {
      return new MyClass(a, b, c, d);
    }
}

现在进行测试我做了:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyTest.TestConfiguration.class)
public class MyTest {

  @Configuration
  @Import(ApplicationConfiguration.class)
  static class TestConfiguration {

    @Bean
    A a() {
        return new A();
    }

    @Bean
    B b() {
        return new B();
    }

    @Bean
    C c() {
        return b().getC();
    }

    @Bean
    D d() {
        return c().getD();
    }
  }

  @Autowired
  private MyClass myClass;
}

但现在,我不明白如何避免为每次测试都写这个..

2 个答案:

答案 0 :(得分:2)

我觉得你在这里错过了一些东西。当你从使用它们的类中构建依赖项转移到注入它们时,你必须完全停止构造它们。我想这有点模糊,让我用一个例子来解释:

假设您有一个类Foo,它使用您在上下文中创建的bean:

class Foo {
   public void someMethod() {
      MyClass myClass1 = new MyClass();
      // do something with myClass1
   }
}

现在你要注入bean。您不像在示例中那样直接致电AnnotationConfigApplicationContext。你正在做这样的事情:

class Foo {
   private MyClass myClass1;

   public Foo(MyClass myClass1) {
      this.myClass1 = myClass1;
   }

   public void someMethod() {
      // do something with myClass1
   }
}

在您的应用程序上下文中,您也可以创建Foo作为bean。类似的东西:

@Configuration
public class ApplicationConfiguration {

    @Bean
    Foo createFooBean() {
      return new Foo(createMyClassBean());
    }

    @Bean
    MyClass createMyClassBean() {
      return new MyClass();
    }

}
  • 如果你有MyClass的构造函数的参数,你需要在@Configuration类中传递它们,当你创建bean时。
  • 如果你有不同的场景,并且在每个场景中你需要用不同的参数构造MyClass - 你需要创建多个bean,每个bean都实例化它自己的MyClass

答案 1 :(得分:1)

要为同一类提供几个bean,可以使用bean命名并调用适当的bean:

@Bean("foo1")
Foo createFooBean() {
   return new Foo(createMyClassBean());
}

@Autowired
@Qualifier("foo1")
Bar createBarBean(Foo foo){..}

在创建bean时,应该满足所有依赖的条件。 因此,出于测试目的,您可以创建1个测试bean,并满足其创建过程中所有的依赖。

@Bean("testbean")
MyClass myClass(A a, B b, C c, D d) {
  A a = new A();
  B b = new B();
  C c = new C();
  D d = new D();

  return new MyClass(a, b, c, d);
}