将原型bean自动装入原型bean?

时间:2014-09-30 23:04:56

标签: java spring

我正在处理一些现有的代码,而且它正在做我以前从未见过的事情。我使用方法注入或使用getBean()从上下文中获取bean,将原型bean自动装配到单例中。我在我正在处理的代码中看到的是一个bean,它是一个原型并使用getBean()检索,并且它具有自动连接的依赖项。其中大多数是单身豆,这是有道理的。但是有一个另一个原型bean的autowire,从我看到的,它看起来似乎正在获得一个新的bean。我的问题是当你将原型自动装配到原型中时,会给你一个新实例吗?由于autowire请求不是在启动时,而是在创建此bean时,是否会创建新实例?这违背了我对autowire和原型bean的看法,我希望能够从野外听到答案。感谢您的任何见解。我试图最小化我对这段代码的重构,因为它有点意大利面。

示例:

@Scope("prototype")
public class MyPrototypeClass  {

    @Autowired
    private ReallyGoodSingletonService svc;

    @Autowired
    private APrototypeBean bean;

    public void doSomething() {
        bean.doAThing();
    }
}

@Scope("prototype)
public class APrototypeBean {
   private int stuffgoeshere;

   public void doAThing() {
   }
}

因此,当调用MyPrototypeClass中的doSomething()时,是" bean" MyPrototypeClass的每个实例的单例或新单元?

3 个答案:

答案 0 :(得分:10)

在您的示例中,APrototypeBean bean将被设置为一个全新的bean,它将一直存在,直到您创建的MyPrototypeClass实例被销毁。

如果您创建了MyPrototypeClass的第二个实例,那么该第二个实例将收到自己的APrototypeBean。使用当前配置,每次调用doSomething()时,都会在APrototypeBean的{​​{1}}实例上调用该方法,该实例对于该MyPrototypeClass对象是唯一的。

答案 1 :(得分:1)

您对@Autowired或一般自动装配的理解存在缺陷。当创建bean的实例而不是在启动时发生自动装配。

如果你有一个懒惰的单例bean并且没有直接使用bean,那么只要在应用程序上下文中使用例如getBean检索bean就不会发生任何事情。已创建,依赖关系已连线,BeanPostProcessors已应用等。

对于每种类型的bean,只要在它之前创建它就会被处理,这是相同的。

现在回答你的问题,原型bean是一个原型bean,所以是的,每次调用getBean时你都会收到新的实例。

答案 2 :(得分:0)

为@Mark Laren的回答添加更多解释。

Spring 4.1.6 docs

中所述
  

在大多数应用程序方案中,容器中的大多数bean都是   单身。当单例bean需要与另一个bean协作时   单例bean或非单例bean需要与之协作   另一个非单例bean,你通常通过处理依赖   将一个bean定义为另一个bean的属性。出现问题时   豆的生命周期是不同的。假设单身豆A需要   使用非单例(原型)bean B,可能在每个方法上   在A.上调用容器只创建一次单例bean A,   因此只有一次机会来设置属性。该   容器不能为bean A提供每个bean的新实例   需要时间。

下面的方法将解决这个问题,但这是可取的,因为此代码将业务代码与Spring框架结合违反 IOC模式。以下是此方法的示例:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

因此,有两种理想的方法可以解决这个问题。

<强> 1。使用Spring的方法注入

  • 顾名思义,Spring将实施&amp;使用 Spring 4 中的@Lookup注释注入我们的抽象方法,如果使用xml版本则使用标记。请参阅this DZone article

使用@Lookup。

  

来自Java Doc ...

     
    

表示“查找”的注释&#39;方法,被覆盖的方法     容器将它们重定向回BeanFactory以进行getBean调用。     这实际上是基于注释的XML版本     lookup-method属性,导致相同的运行时安排。

  
     

自:   4.1

@Component
public class MyClass1 {
  doSomething() {
    myClass2();
  }

  //I want this method to return MyClass2 prototype
  @Lookup
  public MyClass2 myClass2(){
    return null; // No need to declare this method as "abstract" method as
                 //we were doing with earlier versions of Spring & <lookup-method> xml version. 
                 //Spring will treat this method as abstract method and spring itself will provide implementation for this method dynamically.
  }
}

上面的例子每次都会创建新的myClass2实例。

<强> 2。使用Java EE中的提供程序(Java的依赖注入(JSR 330))。

@Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Component
    public static class SomeRequest {}

    @Service
    public static class SomeService {

        @Autowired
        javax.inject.Provider<SomeRequest> someRequestProvider;

        SomeRequest doSomething() {
            return someRequestProvider.get();
        }
    }

以上示例每次都会创建新的SomeRequest实例。