Java 为什么春天';s ApplicationContext.getBean被认为是坏的?
我问了一个普通的Spring问题:让多人回答,应该尽量避免调用Spring的Java 为什么春天';s ApplicationContext.getBean被认为是坏的?,java,spring,Java,Spring,我问了一个普通的Spring问题:让多人回答,应该尽量避免调用Spring的ApplicationContext.getBean()。为什么呢 我还应该如何访问配置为Spring创建的bean 我在非web应用程序中使用Spring,并计划访问共享的ApplicationContext对象 修正案 我接受下面的答案,但下面是Martin Fowler的另一个观点(本质上与调用包装的ApplicationContext.getBean())相同) Fowler在某种程度上说,“对于服务定位器,应用
ApplicationContext.getBean()
。为什么呢
我还应该如何访问配置为Spring创建的bean
我在非web应用程序中使用Spring,并计划访问共享的ApplicationContext
对象
修正案
我接受下面的答案,但下面是Martin Fowler的另一个观点(本质上与调用包装的ApplicationContext.getBean()
)相同)
Fowler在某种程度上说,“对于服务定位器,应用程序类通过给定位器的消息显式地请求它[服务]。对于注入,没有显式的请求,服务出现在应用程序类中-因此控制反转。
控制反转是框架的一个常见特性,但它是需要付出代价的。当您尝试调试时,它往往很难理解,并且会导致问题。因此总体而言,我倾向于避免它[控制反转]除非我需要它。这并不是说它是一件坏事,只是我认为它需要证明自己,而不是更简单的选择。”使用Spring之类的东西最酷的好处之一是,您不必将对象连接在一起。宙斯的脑袋裂开了,你的类出现了,完全形成了它们所有的依赖关系,并根据需要连接起来。它神奇而神奇 你越是说
classired classired=(classired)ApplicationContext.getBean(“classired”)代码>,你得到的魔法就越少。代码越少越好。如果你的类真的需要一个classireedbean,为什么你不把它连接起来呢
也就是说,显然需要一些东西来创建第一个对象。通过getBean()获取一两个bean的主方法没有什么问题,但您应该避免使用它,因为无论何时使用它,您都没有真正使用Spring的所有魔力。使用Spring之类的东西最酷的好处之一是您不必将对象连接在一起。宙斯的脑袋裂开了,你的类出现了,完全形成了它们所有的依赖关系,并根据需要连接起来。它神奇而神奇
你越是说classired classired=(classired)ApplicationContext.getBean(“classired”)代码>,你得到的魔法就越少。代码越少越好。如果你的类真的需要一个classireedbean,为什么你不把它连接起来呢
也就是说,显然需要一些东西来创建第一个对象。通过getBean()获取一两个bean的主方法没有什么问题,但是您应该避免使用它,因为无论何时使用它,您都没有真正使用Spring的所有魔力。我们的想法是依赖依赖于依赖注入(、或IoC)。也就是说,您的组件配置了所需的组件。这些依赖项是注入的(通过构造函数或setter)——您自己无法获得
ApplicationContext.getBean()
要求您在组件中显式命名bean。相反,通过使用IoC,您的配置可以确定将使用哪个组件
这使您可以使用不同的组件实现轻松地重新连接应用程序,或者通过提供模拟变量(例如模拟DAO,以便在测试过程中不会碰到数据库)以简单的方式配置测试对象。这个想法是依赖于依赖项注入(,或IoC)。也就是说,您的组件配置了所需的组件。这些依赖项是注入的(通过构造函数或setter)——您自己无法获得
ApplicationContext.getBean()
要求您在组件中显式命名bean。相反,通过使用IoC,您的配置可以确定将使用哪个组件
这允许您使用不同的组件实现轻松地重新连接应用程序,或者通过提供模拟变量(例如,模拟DAO,以便在测试过程中不会碰到数据库)以简单的方式配置测试对象。动机是编写不显式依赖于Spring的代码。这样,如果您选择切换容器,则不必重写任何代码
将容器视为代码看不见的东西,神奇地满足它的需求,而不被询问
依赖注入是与“服务定位器”模式相对应的。如果您要按名称查找依赖项,那么最好去掉DI容器,使用类似JNDI的东西。动机是编写不明确依赖于Spring的代码。这样,如果您选择切换容器,则不必重写任何代码
将容器视为代码看不见的东西,神奇地满足它的需求,而不被询问
依赖注入是与“服务定位器”模式相对应的。如果您打算按名称查找依赖项,那么您最好放弃DI容器,使用类似JNDI的东西。其他人已经指出了一般问题(这些都是有效的答案),但我只想提供一条补充意见:这不是说您不应该这样做,而是说您应该尽可能少地这样做
通常这意味着它只执行一次:在引导过程中。然后只需访问“根”bean,通过它可以解决其他依赖项。这可以是可重用的代码,比如基本servlet(如果开发web应用的话)。其他人已经指出了一般性的问题(这些都是有效的答案),但我只想补充一点:这不是说你永远不应该这样做,而是说你应该尽可能少地这样做
通常这意味着只需执行一次:在
MyClass myClass = applicationContext.getBean("myClass");
public void setMyClass(MyClass myClass) {
this.myClass = myClass;
}
public class AutowireThisDriver {
private MySpringBean mySpringBean;
public static void main(String[] args) {
AutowireThisDriver atd = new AutowireThisDriver(); //get instance
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"/WEB-INF/applicationContext.xml"); //get Spring context
//the magic: auto-wire the instance with all its dependencies:
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(atd,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
// code that uses mySpringBean ...
mySpringBean.doStuff() // no need to instantiate - thanks to Spring
}
public void setMySpringBean(MySpringBean bean) {
this.mySpringBean = bean;
}
}
ApplicationContext context = new ClassPathXmlApplicationContext("AppContext.xml");
interface HttpLoader {
String load(String url);
}
interface StringOutput {
void print(String txt);
}
@Component
class MyBean {
@Autowired
MyBean(HttpLoader loader, StringOutput out) {
out.print(loader.load("http://stackoverflow.com"));
}
}
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
// execution
new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
@Component
class MyBean {
@Autowired
MyBean(ApplicationContext context) {
HttpLoader loader = context.getBean(HttpLoader.class);
StringOutput out = context.getBean(StringOutput.class);
out.print(loader.load("http://stackoverflow.com"));
}
}
class MyBeanTest {
public void creatingMyBean_writesStackoverflowPageToOutput() {
// setup
String stackOverflowHtml = "dummy";
StringBuilder result = new StringBuilder();
ApplicationContext context = Mockito.mock(ApplicationContext.class);
Mockito.when(context.getBean(HttpLoader.class))
.thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);
// execution
new MyBean(context);
// evaluation
assertEquals(result.toString(), stackOverflowHtml);
}
}
@Component
class MyBean {
@Autowired
MyBean(StringOutput out) {
out.print(new HttpLoader().load("http://stackoverflow.com"));
}
}