问题描述
您能否解释一下为什么 Spring 为如下所示的 beans 配置创建两个对象,因为默认情况下 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>
1楼
Spring 的默认范围是单例。 只是您对成为单身人士意味着什么的想法与 Spring 定义单身人士的方式不符。
如果您告诉 Spring 使用不同的 id 和相同的类创建两个单独的 bean,那么您将得到两个单独的 bean,每个 bean 都有单例作用域。 所有的单例作用域都意味着当你引用具有相同 id 的东西时,你会得到相同的 bean 实例。
以下是如何:
仅管理单例 bean 的一个共享实例,并且对具有与该 bean 定义匹配的一个或多个 id 的 bean 的所有请求都会导致 Spring 容器返回一个特定的 bean 实例。
单例范围意味着使用相同的 id 检索相同的 bean,仅此而已。 测试没有两个 id 引用同一个类会妨碍将映射用作 bean,并且会因使用 BeanFactories 代理而变得复杂。 对于 Spring 进行监管,这将涉及大量工作,但收效甚微。 相反,它相信用户知道他们在做什么。
如果您希望跨多个名称保留 bean 的单例性,这是可行的。 您可以有多个名称引用同一个 bean,这是通过使用来完成的:
在 bean 定义本身中,您可以通过使用 id 属性指定的最多一个名称和 name 属性中任意数量的其他名称的组合,为 bean 提供多个名称。 这些名称可以是同一个 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实例。
2楼
Spring 默认范围是单例,除非您明确指定范围为原型,否则它将为所有实例创建一个对象。 您还没有发布弹簧配置。 请发布它,它会提供更好的主意。
3楼
你混淆了两个不同的概念。
spring 中的单例一词用于 bean 范围,这意味着该 bean 将为整个应用程序只创建一次。
Singleton 通常的意思是指 GOF 模式。 它是一种面向对象的模式,保证一个类只存在一个实例(至少在 classLoader 的范围内)。
4楼
在 Spring 中单例是指每个 Spring 容器一个 bean,而在 Java 中单例是指每个类加载器一个对象。
所以Spring单例与java单例不同。 不要混淆这两者。
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 文档: )
仅管理单例 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 范围:原型。 来自的 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 容器作为管理 Key 值对,Key 作为 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() 和 clientService2() 中调用一次。 由于此方法会创建一个 ClientDaoImpl 的新实例并返回它,因此您通常会期望有 2 个实例(每个服务一个)。 这肯定会有问题:在 Spring 中,默认情况下实例化的 bean 具有单例作用域。 这就是神奇之处:所有@Configuration 类在启动时使用 CGLIB 进行子类化。 在子类中,子方法在调用父方法并创建新实例之前,首先检查容器中是否有任何缓存的(作用域)bean。 请注意,从 Spring 3.2 开始,不再需要将 CGLIB 添加到类路径中,因为 CGLIB 类已重新打包在 org.springframework.cglib 下并直接包含在 spring-core JAR 中。
9楼
spring 默认范围是单例。 一旦 bean 将被创建并在其整个生命周期中使用相同的 bean。