Java 单例中的 Spring Prototype 作用域 bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25165507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:46:23  来源:igfitidea点击:

Spring Prototype scoped bean in a singleton

javaspring

提问by Angad

I am trying to inject a prototypebean in a singletonbean such that every new call to a singleton bean method has a new instance of the prototype bean.

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

Consider a singleton bean as below:

考虑一个单例 bean,如下所示:

    @Component
    public class SingletonBean {
       @Autowired 
       private PrototypeBean prototypeBean;

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

I expect that every time the doSomething()method is called, a new PrototypeBeaninstance is utilized.

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

Below is the prototype bean:

下面是原型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);
        }
    }

What seems to be happening is that spring is being overeager in handing over a new instance of PrototypeBean in the doSomething()method. That is, the 2 lines of code in the doSomething()method are creating a new instance of prototypeBean in each line.

似乎正在发生的事情是 spring 过于急切地在方法中移交 PrototypeBean 的新实例doSomething()。也就是说,方法中的 2 行代码doSomething()在每一行中都创建了一个新的prototypeBean 实例。

And so in the 2nd line - prototypeBean.display()prints NULL.

所以在第二行 -prototypeBean.display()打印NULL

What is missing in configuration for this injection?

此注入的配置中缺少什么?

采纳答案by Francisco Spaeth

From Spring documentation:

从 Spring文档

You do not need to use the <aop:scoped-proxy/>in conjunction with beans that are scoped as singletons or prototypes. If you try to create a scoped proxy for a singleton bean, the BeanCreationException is raised.

您不需要将<aop:scoped-proxy/>与作用域为单例或原型的 bean 结合使用。如果您尝试为单例 bean 创建范围代理,则会引发 BeanCreationException。

It seems the documentation has changed a bit for version 3.2 documentationwhere you can find this sentence:

3.2 版文档的文档似乎有所更改,您可以在其中找到这句话:

You do not need to use the <aop:scoped-proxy/>in conjunction with beans that are scoped as singletons or prototypes.

您不需要将<aop:scoped-proxy/>与作用域为单例或原型的 bean 结合使用。

It seems that its not expected you use a proxied prototype bean, as each time it is requested to the BeanFactoryit will create a new instance of it.

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



In order to have a kind of factory for your prototype bean you could use an ObjectFactoryas follows:

为了为您的原型 bean 建立一种工厂,您可以使用ObjectFactory如下:

@Component
public class SingletonBean {

    @Autowired
    private ObjectFactory<PrototypeBean> prototypeFactory;

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

and your prototype bean would be declared as follows:

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

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

回答by Ramachandran Krishnan

Singleton bean is created only once so the prototype bean which is injected also will be created once at the instantiation of singleton bean.The same instance of prototype bean will be used for every request.

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

If new instance of prototype bean will be created for each request at runtime ,the below method Injection can be used

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

Example

例子

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>

回答by gstackoverflow

Right way to achieve it - use lookup method injection and everywhere where you used beans use lookup method invocation (detailed answer)

实现它的正确方法 - 使用查找方法注入和使用 bean 的任何地方都使用查找方法调用(详细答案

回答by MattC

Spring wires up your beans in a pretty straight forward way. I'm working in a large commercial application, and I inserted the following code snippets to verify the load order.

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

1) All of your singleton bean class structures are initially loaded by Spring (as long as Spring is aware of them via annotations and/or xml). This only ever happens once. You can test this by logging or printing in a static block:

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

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

2) Spring creates all singleton instances that it is aware of (but not prototypes! Prototype instances WILL be created IF they are referenced inside a singleton bean - there class structures are of course loaded first). You can test this by adding this method to each class:

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

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

So in your example, the class structures of SingletonBean is loaded when Spring starts up. A new instance of SingletonBean is created. And because PrototypeBean is Autowired inside of SingletonBean, its class structure is loaded and an instance of it is created. Now, if there was another bean, say AnotherSingletonBean, with an Autowired PrototypeBean inside of it, then a DIFFERENT instance of PrototypeBean would be created (no need to load the class structure again). So there is only ever 1 SingletonBean, and inside of it is a PrototypeBean, which will always point to the same bean. Because of this, singletons should always be stateless, as all of your other beans that use a singleton will be pointing at the same object. But you CAN maintain state in a prototype bean, because wherever you create a new reference, you will be pointing at another bean object. http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-scopes-prototype

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

回答by Optio

Since Spring 4.1 you can use annotation @Lookup

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

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

Every time you will call method getPrototypeBean() - you will receive new prototype bean instance. Don't worryabout empty method realization: Spring will override it for you.

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

Read more in official documentation.

官方文档中阅读更多内容