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

其他回答

使用@Autowired或ApplicationContext.getBean()实际上是一回事。通过这两种方式,您可以获得在您的上下文中配置的bean,并且通过这两种方式,您的代码都依赖于spring。 你唯一应该避免的是实例化你的ApplicationContext。只做一次!换句话说,一条线就像

ApplicationContext context = new ClassPathXmlApplicationContext("AppContext.xml");

在应用程序中只应使用一次。

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

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

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

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

但是,仍然有一些情况需要使用服务定位器模式。 例如,我有一个控制器bean,这个控制器可能有一些默认的服务bean,这些服务bean可以通过配置注入依赖项。 虽然这个控制器现在或以后还可以调用许多附加的或新的服务,但这些服务需要服务定位器来检索服务bean。

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

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