Vad*_*huk 5 java refactoring spring dependency-injection 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;
}
Run Code Online (Sandbox Code Playgroud)
这些构造函数在很多地方使用,其中一些在代码中,一些在测试中,等等.
现在,我将介绍基于java的Spring应用程序配置:
@Configuration
public class ApplicationConfiguration {
@Bean
MyClass myClass() {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
并尝试从应用程序上下文中获取bean来重写所有位置:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
MyClass myClass = (MyClass) context.getBean("myClass", arg1, arg2);
Run Code Online (Sandbox Code Playgroud)
问题是在某些地方我只有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
}
Run Code Online (Sandbox Code Playgroud)
绝对不是解决方案.
提前致谢
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");
}
}
Run Code Online (Sandbox Code Playgroud)
所以,在这里我想在测试中使用MyClass,所以在Avi的答案中没有类似Foo的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
}
Run Code Online (Sandbox Code Playgroud)
但是现在还有另一个问题: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);
}
}
Run Code Online (Sandbox Code Playgroud)
我不想在每个配置中创建MyClass,我想只有一个,我可以导入...
UPD2.
好的,我删除了所有构造函数并且只留下了一个具有所有参数的构造函数
@Configuration
public class ApplicationConfiguration {
@Bean
MyClass myClass(A a, B b, C c, D d) {
return new MyClass(a, b, c, d);
}
}
Run Code Online (Sandbox Code Playgroud)
现在在测试我做:
@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;
}
Run Code Online (Sandbox Code Playgroud)
但现在,我不明白如何避免为每一个测试写这个..
我想你在这里遗漏了一些东西。当您从在使用它们的类内部构建依赖关系转向注入它们时,您必须完全停止构建它们。我觉得这有点模糊,让我用一个例子来解释一下:
假设您有一个类Foo使用您在上下文中创建的 bean:
class Foo {
public void someMethod() {
MyClass myClass1 = new MyClass();
// do something with myClass1
}
}
Run Code Online (Sandbox Code Playgroud)
现在您想要注入 Bean。您不会AnnotationConfigApplicationContext像示例中那样直接调用。你正在做这样的事情:
class Foo {
private MyClass myClass1;
public Foo(MyClass myClass1) {
this.myClass1 = myClass1;
}
public void someMethod() {
// do something with myClass1
}
}
Run Code Online (Sandbox Code Playgroud)
在您的应用程序上下文中,您Foo也可以创建一个 bean。就像是:
@Configuration
public class ApplicationConfiguration {
@Bean
Foo createFooBean() {
return new Foo(createMyClassBean());
}
@Bean
MyClass createMyClassBean() {
return new MyClass();
}
}
Run Code Online (Sandbox Code Playgroud)
MyClass则需要@Configuration在创建 bean 时将它们传递到类中。MyClass使用不同的参数进行构造 - 您需要创建多个 bean,每个 bean 实例化自己的MyClass.