我问了一个常见的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的需要,我一直认为这是丑陋的。

其他回答

其思想是依赖于依赖注入(控制反转,IoC)。也就是说,您的组件已经配置了所需的组件。这些依赖关系是注入的(通过构造函数或设置函数)——你不能自己得到。

getbean()要求您在组件中显式地命名bean。相反,通过使用IoC,您的配置可以确定将使用什么组件。

这让你可以轻松地用不同的组件实现重新连接应用程序,或者通过提供模拟变量(例如,模拟DAO,这样你就不会在测试期间碰到数据库)以一种直接的方式配置测试对象。

的确,在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的需要,我一直认为这是丑陋的。

There is another time when using getBean makes sense. If you're reconfiguring a system that already exists, where the dependencies are not explicitly called out in spring context files. You can start the process by putting in calls to getBean, so that you don't have to wire it all up at once. This way you can slowly build up your spring configuration putting each piece in place over time and getting the bits lined up properly. The calls to getBean will eventually be replaced, but as you understand the structure of the code, or lack there of, you can start the process of wiring more and more beans and using fewer and fewer calls to getBean.

我只发现了两种需要getBean()的情况:

其他人已经提到在main()中使用getBean()为独立程序获取“主”bean。

我使用getBean()的另一种情况是交互用户配置为特定情况确定bean组成。因此,例如,引导系统的一部分使用带scope='prototype' bean定义的getBean()循环遍历数据库表,然后设置其他属性。据推测,有一种调整数据库表的UI比试图(重新)编写应用程序上下文XML更友好。

其他人指出了普遍的问题(并且是有效的答案),但我只想提供一个额外的评论:并不是说你永远不应该这样做,而是尽可能少地做。

通常这意味着它只执行一次:在引导期间。然后,它只是访问“根”bean,通过它可以解决其他依赖关系。这可以是可重用的代码,如基本servlet(如果开发web应用程序)。