我问了一个常见的Spring问题:自动转换Spring bean,很多人回答说应该尽可能避免调用Spring的ApplicationContext.getBean()。为什么呢?
我还应该如何访问我配置Spring创建的bean呢?
我在一个非web应用程序中使用Spring,并计划访问LiorH所描述的共享ApplicationContext对象。
修正案
我接受下面的答案,但这里有Martin Fowler的另一种观点,他讨论了依赖注入与使用服务定位器(本质上与调用包装的ApplicationContext.getBean()相同)的优点。
In part, Fowler states, "With service locator the application class asks for it [the service] explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class - hence the inversion of control.
Inversion of control is a common feature of frameworks, but it's something that comes at a price. It tends to be hard to understand and leads to problems when you are trying to debug. So on the whole I prefer to avoid it [Inversion of Control] unless I need it. This isn't to say it's a bad thing, just that I think it needs to justify itself over the more straightforward alternative."
的确,在application-context.xml中包含该类可以避免使用getBean。然而,即使这样,实际上也是不必要的。如果你正在编写一个独立的应用程序,并且你不想在application-context.xml中包含你的驱动程序类,你可以使用下面的代码让Spring自动装配驱动程序的依赖项:
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;
}
}
当我有一些独立的类需要使用我的应用程序的某些方面(例如测试)时,我需要这样做几次,但我不想将它包含在应用程序上下文中,因为它实际上不是应用程序的一部分。还请注意,这避免了使用字符串名称查找bean的需要,我一直认为这是丑陋的。
我在另一个问题的评论中提到了这一点,但控制反转的整个思想是让你的任何类都不知道或关心它们如何获得它们所依赖的对象。这使得您可以随时更改所使用的给定依赖项的实现类型。它还使类易于测试,因为您可以提供依赖关系的模拟实现。最后,它使类更简单,更专注于它们的核心职责。
调用ApplicationContext.getBean()不是反转控制!虽然更改为给定bean名配置的实现仍然很容易,但类现在直接依赖Spring提供该依赖项,不能通过其他方式获得它。您不能只是在测试类中创建自己的模拟实现并将其传递给它自己。这基本上违背了Spring作为依赖注入容器的目的。
当你想说:
MyClass myClass = applicationContext.getBean("myClass");
相反,你应该声明一个方法:
public void setMyClass(MyClass myClass) {
this.myClass = myClass;
}
然后在构型中
<bean id="myClass" class="MyClass">...</bean>
<bean id="myOtherClass" class="MyOtherClass">
<property name="myClass" ref="myClass"/>
</bean>
Spring会自动将myClass注入到myOtherClass中。
以这种方式声明所有内容,并在其根源上有如下内容:
<bean id="myApplication" class="MyApplication">
<property name="myCentralClass" ref="myCentralClass"/>
<property name="myOtherCentralClass" ref="myOtherCentralClass"/>
</bean>
MyApplication是最核心的类,它至少间接地依赖于程序中的所有其他服务。当引导时,在你的主方法中,你可以调用applicationContext.getBean("myApplication"),但你不需要在其他任何地方调用getBean() !