54

您能否解释一下为什么 Spring 为如下所示的 bean 配置创建两个对象,因为默认情况下 spring 默认范围是单例的?

Spring配置在这里:

<bean id="customer" class="jp.ne.goo.beans.Customer"> 
    <property name="custno" value="100"></property>
    <property name="custName" value="rajasekhar"> </property>
</bean>
<bean id="customer2" class="jp.ne.goo.beans.Customer"> 
    <property name="custno" value="200"></property> 
    <property name="custName" value="siva"></property> 
</bean>
4

9 回答 9

81

Spring 的默认作用域单例。只是您对成为单例意味着什么的想法与 Spring 定义单例的方式不匹配。

如果您告诉 Spring 制作两个具有不同 id 和相同类的单独 bean,那么您将得到两个单独的 bean,每个具有单例范围。所有单例范围意味着当你引用具有相同 id 的东西时,你会得到相同的 bean 实例。

以下是Spring 文档如何定义单例范围

只有一个单例 bean 的共享实例被管理,并且所有对具有一个或多个与该 bean 定义匹配的 id 的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。

单例范围意味着使用相同的 id 检索相同的 bean,仅此而已。测试没有两个 id 引用同一个类会妨碍将映射用作 bean,并且会因为使用 BeanFactories 代理事物而变得复杂。对于 Spring 进行监管,这将涉及大量工作而收效甚微。相反,它相信用户知道他们在做什么。

如果您希望跨多个名称保留 bean 的单一性,这是可行的。您可以有多个名称引用同一个 bean,这是通过使用别名来完成的:

在 bean 定义本身中,您可以为 bean 提供多个名称,方法是使用由 id 属性指定的最多一个名称和 name 属性中任意数量的其他名称的组合。这些名称可以是同一个 bean 的等效别名,并且在某些情况下很有用,例如允许应用程序中的每个组件通过使用特定于该组件本身的 bean 名称来引用公共依赖项。

但是,指定实际定义 bean 的所有别名并不总是足够的。有时需要为在别处定义的 bean 引入别名。这在大型系统中很常见,其中配置在每个子系统之间进行拆分,每个子系统都有自己的一组对象定义。在基于 XML 的配置元数据中,您可以使用该元素来完成此操作。

因此,如果您在 bean 配置中添加名称:

<bean id="customer" name="customer2" 
    class="jp.ne.goo.beans.Customer">
</bean>

或为在别处定义的 bean 创建别名:

<alias name="customer" alias="customer2"/>

那么 "customer" 和 "customer2" 将引用同一个 bean 实例。

于 2015-07-25T19:04:37.910 回答
8

Spring 默认范围是单例的,除非您明确指定范围为原型,否则它将为所有实例创建一个对象。您还没有发布弹簧配置。请发布它,它会提供一个更好的主意。

于 2015-07-25T18:55:18.077 回答
4

在 Spring Singleton 中是指每个 Spring 容器一个 bean,而在 Java Singleton 中是指每个类加载器一个对象。

所以 Spring 单例与 java 单例不同。不要混淆这两者。

于 2019-04-14T19:49:38.013 回答
3

你混淆了两个不同的概念。

spring 中的单词 singleton 用于 bean 范围,这意味着 bean 将只为整个应用程序创建一次。

Singleton 通常的含义是指 GOF 模式。它是一种面向对象的模式,保证只存在一个类的一个实例(至少在类加载器的范围内)。

于 2015-07-25T19:14:47.803 回答
2

您正在声明同一类的两个 bean。那不一样。

@Component("springTestClass")
public class SpringTestClass{
     private int randomNumber = 0;
     public SpringTestClass(){
       randomNumber = new Random().nextInt(2000);
     }

     public int getRandomNumber(){
       return this.randomNumber;
     }

}

并尝试在两个地方访问这个 bean 的数量将是相同的。但是您所做的是创建了两个单独的 bean。

如果您想检查这是否有效,请尝试:

public class Main{
   public static void main(String[] args){
     ApplicationContext ctx = ....;
     SpringTestClass testObject1 = (SpringTestClass)ctx.getBean("springTestClass");
     SpringTestClass testObject2 = (SpringTestClass)ctx.getBean("springTestClass");

    System.out.println(testObject1.getRandomNumber() == testObject2.getRandomNumber());
   }
}

如果它是同一个实例,则此代码应返回 true;但在 SpringTestClass 中,您可以添加 @Scope("prototype") 注释。输出将是错误的

于 2015-07-25T19:16:18.757 回答
2

就像其他人提到的那样,应该从您发布的代码中创建两个 bean。单例定义如下(来自 Spring 文档:Singleton Scope

只有一个单例 bean 的共享实例被管理,并且所有对具有与该 bean 定义匹配的一个或多个 id 的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。

为了更清楚地说明这一点,“共享实例”背后的含义在上一段后面的段落中进行了解释:

该命名 bean 的所有后续请求和引用都返回缓存的对象

创建单例 bean 时,仅实例化和缓存一个 bean 对象。这仅指 bean,而不是 bean 可能是实例的任何类。例如,

<bean id="myBean" class="myPackage.myClass" />

<bean id="myOtherBean1 class="myPackage.myOtherClass1">
    <property name="beanReference1" ref="myBean" />
</bean>
<bean id="myOtherBean2 class="myPackage.myOtherClass2">
    <property name="beanReference2" ref="myBean" />
</bean>

在这个组成的配置中,“myOtherBean1”和“myOtherBean2”引用了相同的“myBean”bean,因此具有相同的“myPackage.myClass”实例。如果您更改代码以添加第二个“myPackage.myClass”bean,它将与“myBean”不同。

要完全理解这一点,还请参考另一个 Spring 范围:原型。来自Prototype Scope的 Spring 文档:

bean 部署的非单例原型范围导致每次对特定 bean 发出请求时都会创建一个新的 bean 实例。

这意味着如果我们要使用与上面相同的 Spring XML,“myOtherBean1”和“myOtherBean2”将各自收到它们自己不同的“myBean”副本,它仍然只是“myPackage.myClass”的一个实例。

于 2016-05-17T13:06:18.527 回答
2

Spring Singleton Bean 不像 Java Singleton 那样工作。

如果我们写

ApplicationContext ctx = new ClassPathXmlApplicationContext("MyConfig.xml");
        Customer obj1= (Customer) ctx.getBean("customer");
        Customer obj2 = (Customer) ctx.getBean("customer2");
        System.out.println(obj1 == obj2);
        System.out.println(obj1+ "::" + obj2);

如果我们看到输出,它将返回 2 个不同的实例。根据 Spring Docs Bean 是单例的,只有一个共享实例将被管理,并且所有具有与该 bean 定义匹配的 ID 或 ID 的请求 bean。这里有 2 个不同的 ID 可用。

Spring 容器作为管理键值对,键作为 ID/名称,值是 bean。

于 2019-07-30T05:24:20.427 回答
0

下面的例子展示了一个被@Bean注解的方法被调用了两次:

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }

}

clientDao() 已在 clientService1() 和 clientService2() 中调用过一次。由于此方法创建了一个新的 ClientDaoImpl 实例并返回它,因此您通常期望有 2 个实例(每个服务一个)。这肯定会有问题:在 Spring 中,实例化的 bean 默认具有单例范围。这就是神奇之处:所有@Configuration 类在启动时都使用CGLIB 子类化。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器中是否有任何缓存(作用域)bean。请注意,从 Spring 3.2 开始,不再需要将 CGLIB 添加到类路径中,因为 CGLIB 类已重新打包在 org.springframework.cglib 下并直接包含在 spring-core JAR 中。

于 2018-09-13T06:52:12.933 回答
0

spring 默认范围是单例。一旦 bean 将被创建并在其整个生命周期中使用相同的 bean。

于 2021-05-11T16:00:40.643 回答