Java 为什么春天';s ApplicationContext.getBean被认为是坏的?

Java 为什么春天';s ApplicationContext.getBean被认为是坏的?,java,spring,Java,Spring,我问了一个普通的Spring问题:让多人回答,应该尽量避免调用Spring的ApplicationContext.getBean()。为什么呢 我还应该如何访问配置为Spring创建的bean 我在非web应用程序中使用Spring,并计划访问共享的ApplicationContext对象 修正案 我接受下面的答案,但下面是Martin Fowler的另一个观点(本质上与调用包装的ApplicationContext.getBean())相同) Fowler在某种程度上说,“对于服务定位器,应用

我问了一个普通的Spring问题:让多人回答,应该尽量避免调用Spring的
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"));
    }
}