弹簧默认范围单例与否?
Posted
技术标签:
【中文标题】弹簧默认范围单例与否?【英文标题】:Is spring default scope singleton or not? 【发布时间】:2015-10-16 06:36:12 【问题描述】:您能否解释一下为什么 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>
【问题讨论】:
在哪里??你还没有发布配置 好吧,如果你定义同一个类的两个bean实例,就会有两个实例...... 感谢 Gab,因为我不明白单例的用途是什么?单例的含义是只应为整个应用程序创建一个实例(对象)。那为什么spring要为此创建两个对象。 您正在定义两个恰好具有相同类型的单例 bean。单例是每个 bean,而不是每个类。 【参考方案1】:Spring 的默认作用域是单例。只是你对单例的理解与 Spring 对单例的定义不符。
如果您告诉 Spring 创建两个具有不同 id 和相同类的单独 bean,那么您将得到两个单独的 bean,每个具有单例范围。所有单例范围的意思是,当您引用具有相同 id 的内容时,您将获得相同的 bean 实例。
这里是the Spring documentation defines singleton scope:
只有一个单例 bean 的共享实例被管理,并且所有对具有一个或多个与该 bean 定义匹配的 id 的 bean 的请求都会导致 Spring 容器返回一个特定的 bean 实例。
单例范围意味着使用相同的 id 检索相同的 bean,仅此而已。测试没有两个 id 引用同一个类会妨碍将映射用作 bean,并且会因为使用 BeanFactories 代理事物而变得复杂。 对于 Spring 进行监管,这将涉及大量工作而收效甚微。相反,它相信用户知道他们在做什么。
如果您希望 bean 的单例性保留在多个名称中,这是可行的。您可以有多个名称引用同一个 bean,这是通过使用 alias 来完成的:
在 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 实例。
【讨论】:
谢谢 nathan,我不明白单例有什么用?单例的含义是只应为整个应用程序创建一个实例(对象)。那么为什么spring为此创建两个对象。弹簧内部为此使用什么机制。 @Rajasekhar_b_1989:我认为您的定义是错误的,请参阅我正在添加的 spring 文档中的引用。【参考方案2】:Spring 默认作用域是单例的,除非你明确指定作用域为原型,否则它将为所有实例创建一个对象。您还没有发布弹簧配置。请贴出来,它会给你一个更好的主意。
【讨论】:
在您的示例中,当您将值初始化为 customer2 时,它将更改客户对象的值。对于这两种情况。您可以通过不对客户2 初始化任何值来理解这一点。在 Spring Singleton 中是指每个 Spring 容器一个 bean,而在 Java Singleton 中是指每个类加载器一个对象。
所以 Spring 单例与 java 单例不同。不要混淆这两者。
【讨论】:
【参考方案4】:您混淆了两个不同的概念。
spring 中的单词 singleton 用于 bean 范围,这意味着 bean 将只为整个应用程序创建一次。
Singleton 通常的含义是指 GOF 模式。它是一种面向对象的模式,保证只存在一个类的一个实例(至少在类加载器的范围内)。
【讨论】:
感谢 Gab,根据 GOF 模式,单例意味着只应创建一次实例,但在春季为什么要在这种情况下创建两个实例。 因为在春天,单例这个词被应用于 bean 而不是类。它的两个不同的概念 感谢 Gab 提供的信息,最后类对象只加载到内存而不是 bean。在这种情况下,class 和 bean 是什么意思? 一个 bean 是一个类实例,它的生命周期(创建、注入等)由容器管理。类与面向对象的概念相同... "类对象只加载到内存而不是 bean"。这种说法是不正确的。 bean 是一个类实例,实例和类定义都加载在内存中,但在不同的位置(分别是堆和“permgen”)。 bean 这个词只是指一个实例由一个提供的称为容器的上下文管理的事实。【参考方案5】:您正在声明同一类的两个 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") 注释。 输出为假
【讨论】:
【参考方案6】:就像其他人提到的那样,应该从您发布的代码中创建两个 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”的一个实例。
【讨论】:
【参考方案7】: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/Name,值是bean。
【讨论】:
【参考方案8】:下面的例子展示了一个被@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() 中调用过一次,在 客户端服务2()。由于此方法创建了一个新的实例 ClientDaoImpl 并返回它,你通常期望有 2 实例(每个服务一个)。那肯定是 有问题的:在 Spring 中,实例化的 bean 有一个单例范围 默认。这就是神奇之处:所有@Configuration 类 在启动时使用 CGLIB 进行子类化。在子类中,孩子 方法首先检查容器中是否有任何缓存的(作用域)bean 它调用父方法并创建一个新实例。请注意,截至 Spring 3.2,不再需要将 CGLIB 添加到类路径中 因为CGLIB类已经被重新打包在 org.springframework.cglib 并直接包含在 spring-core 中 罐子。
【讨论】:
【参考方案9】:spring 默认作用域是单例。一旦 bean 将被创建并在其整个生命周期中使用相同的 bean。
【讨论】:
以上是关于弹簧默认范围单例与否?的主要内容,如果未能解决你的问题,请参考以下文章
spring默认的bean范围是单例,但它是如何在实际应用程序中处理的?
将 boost::python::numpy::ndarray 作为 boost::python 函数的(默认与否)参数传递?