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

标签 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;
  }

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

下面介绍基于spring 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 是单调的,因此如果我创建多个具有不同参数的 bean,则此要求将被打破,即

@Configuration
public class ApplicationConfiguration {

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

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

  //etc
}

绝对不是解决方案。

提前致谢

更新。 看起来 +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,所以没有像 Avi 答案中那样的 Foo-like bean。

我为“不同的场景修改了上下文,在每个场景中你需要用不同的参数构造 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[]{});它告诉我它只允许用于原型(prototype)作用域的 bean。但是我想要一个单例 bean。

看来我需要另一个建议:如何在配置中删除多个具有相同名称的 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;
}

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

最佳答案

我认为您在这里遗漏了一些东西。当您从在使用它们的类内部构建依赖关系转向注入(inject)它们时,您必须完全停止构建它们。我想这有点含糊,让我用一个例子来解释:

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

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

现在您要注入(inject) 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 的构造函数,您需要在创建 bean 时将它们传递到 @Configuration 类中。
  • 如果您有不同的场景,并且在每个场景中您需要使用不同的参数构造 MyClass - 您需要创建多个 bean,每个 bean 实例化自己的 MyClass .

关于java - 在基于 Java 的 Spring 配置中具有多个构造函数的 Bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21216277/

相关文章:

java - 使用 Guice 设置属性的正确方法是什么?

java - 使用 Docker 云进行 Spring Boot 自动化构建

java - 使用嵌套在另一个类中的类中定义的比较器来实现 Collections.sort

java - Swagger2 : Failed to introspect Class [springfox. 文档.swagger2.configuration.Swagger2DocumentationConfiguration]

Spring Kotlin : Bean fields are null at runtime despite being injected during instantiation

java - 在实例化之前,在 spring bean Autowiring 构造函数中自定义参数

design-patterns - 你能在任何需要单例的地方使用依赖注入(inject)吗?

spring - 如何将 spring 管理的服务 bean 注入(inject) Apache CXF Servlet

java - 使用 java Lambda 在公共(public)属性上加入两个列表并收集另一个属性

java - Apache Flink 中从 Kafka header 读取数据的方法