Spring中的Spring Prototype scoped bean

时间:2014-08-06 16:29:54

标签: spring

我正在尝试在单例bean中注入一个原型bean,这样每次对单例bean方法的新调用都会有一个原型bean的新实例。

考虑如下的单例bean:

    @Component
    public class SingletonBean{

       @Autowired 
       private PrototypeBean prototypeBean;

       public void doSomething(){
         prototypeBean.setX(1);
         prototypeBean.display();
       }

    }

我希望每次调用doSomething()方法时,都会使用一个新的PrototypeBean实例。

下面是原型bean:

     @Component 
     @Scope(value="prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
     public class PrototypeBean{

        Integer x;

        void setX(Integer x){
         this.x = x;
        }

        void display(){
          System.out.println(x);
        }
    }

似乎正在发生的事情是,在doSomething()方法中,Spring正在过度切换PrototypeBean的新实例。也就是说,doSomething()方法中的2行代码在每行中创建了一个新的prototypeBean实例。所以在第二行 - prototypeBean.display()打印出NULL。

这是预期的行为还是我错过了正确注入原型bean的一些配置?

5 个答案:

答案 0 :(得分:36)

从春天documentation

  

您无需结合使用<aop:scoped-proxy/>   作为单身人士或原型的豆子。如果你试着   为单例bean创建一个范围代理,即BeanCreationException   被提出来了。

对于版本3.2 documentation,文档似乎发生了一些变化,你可以在这里找到这句话:

  

您无需结合使用<aop:scoped-proxy/>   作为单身人士或原型的豆子。

似乎不期望你使用代理原型bean,因为每次向BeanFactory请求它时,它都会创建一个新的实例。


为了让你的原型bean有一种工厂,你可以使用ObjectFactory,如下所示:

@Component
public class SingletonBean {

    @Autowired
    private ObjectFactory<PrototypeBean> prototypeFactory;

    public void doSomething() {
        PrototypeBean prototypeBean = prototypeFactory.getObject();
        prototypeBean.setX(1);
        prototypeBean.display();
    }
}

并且您的原型bean将声明如下:

@Component 
@Scope(value="prototype")
public class PrototypeBean {
    // ...
}

答案 1 :(得分:16)

Singleton bean只创建一次,因此注入的原型bean也将在singleton bean的实例化时创建一次。原型bean的相同实例将用于每个请求。

如果将在运行时为每个请求创建原型bean的新实例,则可以使用以下方法注入

示例

public class Singleton 
{
    private Prototype prototype;
    public Singleton(Prototype prototype) 
    {
        this.prototype = prototype;
    }

    public void doSomething() 
    {
         prototype.foo();
    }

    public void doSomethingElse()
    {
        prototype.bar();
    }
}

public abstract class Singleton 
{

    protected abstract Prototype createPrototype();

    public void doSomething() 
    {
        createPrototype().foo();
    }

    public void doSomethingElse() 
    {
        createPrototype().bar();
    }
}


<bean id="prototype" class="ch.frankel.blog.Prototype" scope="prototype" />
<bean id="singleton" class="sample.MySingleton">
<lookup-method name="createPrototype" bean="prototype" />
</bean>

答案 2 :(得分:0)

实现它的正确方法 - 使用查找方法注入和使用bean的任何地方使用查找方法调用(detailed answer

答案 3 :(得分:0)

Spring以非常直接的方式将豆子连接起来。我在大型商业应用程序中工作,并插入以下代码片段以验证加载顺序。

1)所有单例bean类结构最初都是由Spring加载的(只要Spring通过注释和/或xml知道它们)。这只发生过一次。您可以通过在静态块中记录或打印来测试它:

    static {
        log.info("#### classNameHere loaded"); //or println if no log setup
    }

2)Spring创建它知道的所有单例实例(但不是原型!如果在单例bean中引用它们,将创建原型实例 - 当然首先加载类结构)。您可以通过将此方法添加到每个类来测试:

    @PostConstruct
    public void methodHitAfterClassInstantiation() {
        LOGGER.info("#### instance of classNameHere");
    }

因此在您的示例中,Spring启动时会加载SingletonBean的类结构。创建了SingletonBean的新实例。 并且因为PrototypeBean在SingletonBean中是Autowired,所以它的类结构被加载并创建它的一个实例。现在,如果有另一个bean,比如说AnotherSingletonBean,里面有一个Autowired PrototypeBean 然后,将创建一个不同的PrototypeBean实例(无需再次加载类结构)。所以只有1个SingletonBean,里面是PrototypeBean,它 将始终指向同一个bean。因此,单身人士应该始终是无国籍的,因为所有使用单身人士的其他豆子都会指向  同一个对象。但是你可以在原型bean中维护状态,因为无论你在哪里创建新的引用,你都会指向另一个bean对象。  http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes-prototype

答案 4 :(得分:0)

从Spring 4.1开始,您可以使用注释@Lookup

@Lookup
    public PrototypeBean getPrototypeBean() {
        return null;
    }

每次您都会调用方法getPrototypeBean()-您将收到新的原型bean实例。 不用担心:实现空方法:Spring会为您覆盖它。

official documentation中了解更多信息。