singleton和prototype bean有什么区别?

时间:2013-04-17 11:05:54

标签: spring dependency-injection

我是春天的新手,我读到了这个:

  

基本上bean有一个范围来定义它们在应用程序中的存在

     

Singleton:表示每个Spring IOC容器对单个对象实例的单个bean定义。

     

Prototype:表示对任意数量的对象实例的单个bean定义。

那么什么是“对象实例”。

9 个答案:

答案 0 :(得分:66)

原型范围 =每次注入/查找时都会创建一个新对象。它每次都会使用new SomeClass()

单例范围 = (默认)每次注入/查找时都会返回相同的对象。这里它将实例化SomeClass的一个实例,然后每次都返回它。

另见:

答案 1 :(得分:15)

让我们只是通过代码查看。

以下是TennisCoach Bean,默认为 singleton 范围

@Component
@Scope("singleton")
public class TennisCoach implements Coach {

    public TennisCoach(){

    }

    @Autowired
    public void setFortuneService(FortuneService fortuneService) {
        this.fortuneService = fortuneService;
    }

    @Override
    public String getDailyWorkout() {
        return "Practice your backhand volley";
    }

    @Override
    public String getDailyFortune() {
        return "Tennis Coach says : "+fortuneService.getFortune();
    }

}

以下是具有原型范围

的TennisCoach Bean
@Component
@Scope("prototype")
public class TennisCoach implements Coach {

    public TennisCoach(){
        System.out.println(">> TennisCoach: inside default constructor");
    }

    @Autowired
    public void setFortuneService(FortuneService fortuneService) {
        System.out.println(">> Tennis Coach: inside setFortuneService");
        this.fortuneService = fortuneService;
    }

    @Override
    public String getDailyWorkout() {
        return "Practice your backhand volley";
    }

    @Override
    public String getDailyFortune() {
        return "Tennis Coach says : "+fortuneService.getFortune();
    }

}

以下是Main类:

public class AnnotationDemoApp {

    public static void main(String[] args) {


        // read spring config file
        ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("applicationContext.xml");

       // get the bean from the spring container
       Coach theCoach = context.getBean("tennisCoach",Coach.class);
       Coach alphaCoach = context.getBean("tennisCoach",Coach.class);
       // call a method on the bean
       System.out.println("Are the two beans same :" + (theCoach==alphaCoach));

       System.out.println("theCoach : " + theCoach);
       System.out.println("alphaCoach: "+ alphaCoach);


       context.close()

    }
}

对于 singleton 范围,输出为:

Are the two beans same :true
theCoach : com.springdemo.TennisCoach@2a53142
alphaCoach: com.springdemo.TennisCoach@2a53142

对于原型范围,输出为:

Are the two beans same :false
theCoach : com.springdemo.TennisCoach@1b37288
alphaCoach: com.springdemo.TennisCoach@1a57272

希望这能回答你的问题。 :d

答案 2 :(得分:9)

添加到上面..不要混淆java单例。 根据JAVA规范,singleton意味着每个JVM只会创建该bean的一个实例。 但是在spring中,singleton意味着每个应用程序上下文将创建一个特定bean的实例。 因此,如果您的应用程序有多个上下文,那么该bean仍然可以拥有多个实例。

答案 3 :(得分:3)

它们都是创造性的设计模式。

Singleton将在第一次调用中创建一个新实例,并在后续调用中返回它。

Prototype每次都会返回一个新实例。

答案 4 :(得分:3)

Singleton Scope:使用Singleton作用域,使用提供的bean定义创建一个且只有一个bean实例,对于同一个bean的后续请求,Spring容器将返回相同的实例。

从Spring文档:

  

..当你定义一个bean定义并且它的范围是一个单例时,   Spring IoC容器只创建一个对象实例   由该bean定义定义。这个单个实例存储在一个   这些单例bean的缓存,以及所有后续请求和   该命名bean的引用返回缓存对象...

实施例: 可以说,我们已经定义了一个bean accountDao,如下所示:

<bean id="accountDao" class="" />

另外两个bean,它使用这个accountDao bean

<bean id="someBean" ref="accountDao" /> 
<bean id="anotherBean" ref="accountDao" />

Spring最初将创建accountDao bean并对其进行缓存。然后对于someBean以及anotherBean,它将提供accountDao的相同实例。

注意:如果没有使用bean定义指定范围,则Singleton是默认范围。

原型范围:对于原型范围,对于bean的每个请求,将创建并返回bean的新实例。这类似于在java中为类调用 new 运算符。

实施例: 可以说,我们已经定义了一个bean accountDao,如下所示:

<bean id="accountDao" class="" scope="prototype"/>

另外两个bean,它使用这个accountDao bean

<bean id="someBean" ref="accountDao" /> 
<bean id="anotherBean" ref="accountDao" />

对于someBean和anotherBean,Spring将返回accountDao对象的两个独立实例。

一个重要的区别是,对于原型范围,Spring不管理bean的整个生命周期,需要通过客户端代码完成清理。

从Spring文档:

  

Spring不管理原型bean的完整生命周期:   容器实例化,配置和组装a   原型对象,并将其交给客户端,没有进一步的记录   那个原型实例。因此,虽然初始化生命周期   无论范围如何,都会在所有对象上调用回调方法   原型的情况下,配置的销毁生命周期回调不是   调用。客户端代码必须清理原型范围的对象和   释放原型bean所持有的昂贵资源。

答案 5 :(得分:2)

我想添加一些额外的信息,可以帮助我们找出&#34;对象实例&#34;的含义。在提到的句子中。 Spring Doc的这一段试图定义&#34;对象实例&#34;:

  

创建bean定义时,您可以创建一个配方,用于创建由该bean定义定义的类的实际 实例。 bean定义是一个配方的想法很重要,因为它意味着,   与类一样,您可以从单个配方创建许多对象实例

因此,如上一节所述,每个bean定义都被视为一个类(就面向对象而言)。根据您在其中定义的数据(例如作用域,...),此类(或bean定义)可能只有一个对象实例(单个作用域只有一个共享实例)或任意数量的对象实例(例如每次发出对该特定bean的请求时,通过创建新的bean实例来创建原型范围。)

答案 6 :(得分:0)

Singleton是跨应用程序的相同实例

Prototype是getBean的每个新请求的新实例

答案 7 :(得分:0)

原型范围:每次注入时都会创建一个新对象 单例范围:每次注入时都返回相同的对象。

原型范围用于所有有状态的bean,而单例范围应用于无状态bean。 让我用我的例子来解释。请自行复制并运行以获得清晰的理解。 考虑一个Coach接口。

public interface Coach {

    public String getDailyWorkout();

    public String getDailyFortune();

}

我们有另一个名为TrackCoach的类,它实现了Coach。

public class TrackCoach implements Coach {

    private FortuneService fortuneService;


    public TrackCoach(FortuneService fortuneService) {
        this.fortuneService = fortuneService;
    }

    @Override
    public String getDailyWorkout() {
        return "Run a hard 5k";
    }

    @Override
    public String getDailyFortune() {
        return "Just Do it: " + fortuneService.getFortune();
    }    
}

现在有一个FortuneService接口。

public interface FortuneService {

    public String getFortune();

}

它由我们的类HappyFortuneService实现。

public class HappyFortuneService implements FortuneService {

    @Override
    public String getFortune() {
        return "Today is your lucky day!";
    }

}

让我们连接两个类,并使用Xml将一个类的对象bean注入另一个类。让我们执行依赖注入。请注意,我们也可以使用java注释来完成此操作。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">


    <!-- Define your beans here -->

    <!--  define the dependency  -->
    <bean id = "myFortuneService"
        class = "com.luv2code.springdemo.HappyFortuneService">
    </bean>

    <bean id = "myCoach"
        class = "com.luv2code.springdemo.TrackCoach"
        scope = "singleton">


        <!-- set up construction injection -->
        <constructor-arg ref = "myFortuneService" />
    </bean>

</beans>

请注意scope = singleton

现在让我们定义我们的主要方法BeanScopeDemoApp。

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanScopeDemoApp {

    public static void main(String[] args) {

        // load the spring configuration file 
        ClassPathXmlApplicationContext context = 
                new ClassPathXmlApplicationContext("beanScope-applicationContext.xml");

        // retrieve bean from spring container 
        Coach theCoach = context.getBean("myCoach", Coach.class);

        Coach alphaCoach = context.getBean("myCoach", Coach.class);

        // check if they are the same 
        boolean result = (theCoach == alphaCoach);

        // print out the results 
        System.out.println("\nPointing to the same object: " + result);

        System.out.println("\nMemory location for theCoach: " + theCoach);

        System.out.println("\nMemory location for alphaCoach: " + alphaCoach +"\n");

        // close the context 
        context.close();
    }

}

运行上述代码后,您将看到以下结果:

Pointing to the same object: true

Memory location for theCoach: com.luv2code.springdemo.TrackCoach@16515bb7

Memory location for alphaCoach: com.luv2code.springdemo.TrackCoach@16515bb7

它指向同一个对象并在调用它两次后占用相同的内存位置。 现在让我们更改Xml文件中的scope = prototype,保存并再次运行BeanScopeDemoApp。 您将看到以下结果:

Pointing to the same object: false

Memory location for theCoach: com.luv2code.springdemo.TrackCoach@6d4d203d

Memory location for alphaCoach: com.luv2code.springdemo.TrackCoach@627fbcda

它指向不同的对象并在调用它两次后占用不同的内存位置。 这将是我刚才所说的图解说明。 enter image description here enter image description here

答案 8 :(得分:0)

  1. Singleton范围是默认值。
  2. 在应用程序上下文初始化期间创建单例bean,并且始终返回相同的bean。
  3. 原型bean是在调用时创建的。每次调用它时我们都会得到一个新对象。
  4. 注意:如果Bean被其他bean引用并使用Application上下文调用,则将创建具有任何范围的Bean。

    检查此问题的示例代码。

    public class PrototypeClass {
    
            PrototypeClass()
            {
                System.out.println("prototype class is created"+this.toString());
            }
    
        }
    

    这将在调用构造函数时打印相关文本。

    以下代码

    for(int i=0;i<10;i++) {
       PrototypeClass pct= (PrototypeClass) context.getBean("protoClass");
    }
    
      

    原型类创建了Spring.PrototypeClass@29774679原型   class is createdSpring.PrototypeClass@3ffc5af1原型类是   createdSpring.PrototypeClass@5e5792a0原型类是   createdSpring.PrototypeClass@26653222原型类是   createdSpring.PrototypeClass@3532ec19原型类是   createdSpring.PrototypeClass@68c4039c原型类是   createdSpring.PrototypeClass@ae45eb6原型类是   createdSpring.PrototypeClass@59f99ea原型类是   createdSpring.PrototypeClass@27efef64原型类是   createdSpring.PrototypeClass@6f7fd0e6原型类是   createdSpring.PrototypeClass@47c62251

    Bean定义是

    <bean id="protoClass" class="Spring.PrototypeClass" scope="prototype</bean>
    

    现在我将bean定义中的范围更改为singleton。在上下文初始化期间仅调用构造函数一次。接下来,我删除了scope属性,并观察到与singleton相同的行为。