Spring Bean的单例和多例配置

在Spring以及Spring Boot开发中,我们常常使用自动装配就可以完成一个类中字段的组装,这是非常方便的,这是因为Spring框架会自动地将特定的类(标注了@Component等注解的)自动实例化为Bean并自动处理其依赖关系,最后存入IoC容器,我们直接取出就可以使用了。

也相信大家都听说过:在Spring中Bean默认是单例的,这究竟是什么意思呢?以及能否配置Bean为多例的呢?

1,Spring Bean的默认单例性

Spring可以帮我们自动创建对象,这些对象都称作Spring Bean,而Spring默认使用单例模式管理Bean,简单来说就是在IoC容器中,默认情况下一个类只会存在一个它的实例,即对应的每个(标注了@Component等注解的)类只会被实例化一次

在IoC容器中,Spring负责管理应用中组件的创建、配置和组装。当我们在Spring应用中声明一个Bean时,容器负责实例化该Bean,并且默认情况下,它会保持这个实例,并在需要的时候将该实例返回给调用者。

我们来看下列一个简单的例子,帮助我们理解Spring Bean的默认单例性。

首先新建一个Spring Boot工程,然后创建一个类MessageService用于后续被装配

 

java

复制代码

package com.gitee.swsk33.springbootdemo.service; import org.springframework.stereotype.Component; @Component public class MessageService { public void print() { System.out.println("测试消息服务"); } }

然后创建两个测试类,在这两个类中我们使用@Autowired自动装配上述MessageService类,并在这两个测试类中的@PostConstruct标注的方法中打印装配的MessageService类实例的HashCode,查看它们是否是同一个实例。

首先是第一个测试类:

 

java

复制代码

package com.gitee.swsk33.springbootdemo.launcher; import com.gitee.swsk33.springbootdemo.service.MessageService; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 测试类1 */ @Component public class TestOne { @Autowired private MessageService messageService; @PostConstruct private void init() { System.out.println("哈希码:" + messageService.hashCode()); } }

然后是第二个测试类:

 

java

复制代码

package com.gitee.swsk33.springbootdemo.launcher; import com.gitee.swsk33.springbootdemo.service.MessageService; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 测试类2 */ @Component public class TestTwo { @Autowired private MessageService messageService; @PostConstruct private void init() { System.out.println("哈希码:" + messageService.hashCode()); } }

运行,得到下列输出:

image.png

hashCode方法是Object根类的方法,它默认会输出这个对象的哈希码,而哈希码是使用对象的内部地址信息(对象在内存中的位置)来生成的,这样同一个对象的哈希码始终是相同的,我们也可以通过判断两个变量的哈希码是否相同,来判断它们是否指向同一个对象。

可见上述我们在两个不同的类中自动装配了MessageService对象,但是在两个类中得到的MessageService实例都是同一个实例,这就说明在IoC容器中有且只有一个MessageService实例,并且无论我们在多少个类中自动装配了这个MessageService对象,得到的都是同一个实例。

现在在另一个测试类中,使用BeanFactory来获取多次MessageService的Bean试试:

 

java

复制代码

package com.gitee.swsk33.springbootdemo.launcher; import com.gitee.swsk33.springbootdemo.service.MessageService; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TestBeanFactory { @Autowired private BeanFactory beanFactory; @PostConstruct private void init() { // 获取两次MessageService的Bean MessageService s1 = beanFactory.getBean(MessageService.class); MessageService s2 = beanFactory.getBean(MessageService.class); System.out.println("是否为同一对象:" + (s1 == s2)); System.out.println("s1哈希码:" + s1.hashCode()); System.out.println("s2哈希码:" + s2.hashCode()); } }

结果:

image.png

这也可见无论从IoC容器中取出多少次Bean,取出的始终是同一个。

可见Spring在创建和管理Bean的时候,默认遵循了单例模式,这样做有许多的好处,例如:

  • 性能更好: 单例模式可以减少对象的创建和销毁次数,提高系统性能,在应用启动时,Spring容器会创建并初始化所有单例的Bean,然后在整个应用的生命周期中,只需要使用同一个实例,这避免了频繁创建和销毁对象的开销
  • 节省资源: 单例模式确保所有对该Bean的请求都共享同一个实例,这对于一些资源密集型的操作,比如数据库连接池、线程池等是非常有利的,可以避免资源的浪费
  • 保证一致性: 在某些情况下,多个对象需要共享同一状态或配置信息,通过使用单例模式,可以确保这些对象都引用相同的实例,保持一致性

2,配置多例Bean

虽然Spring Bean默认遵循单例模式,但是这并非总是一成不变的。

我们可以通过@Scope注解配置一个类为多例的Bean,该注解中可以传入以下值:

  • ConfigurableBeanFactory.SCOPE_SINGLETON 配置当前类为单例模式,即单例Bean,这是所有Bean的默认配置模式
  • ConfigurableBeanFactory.SCOPE_PROTOTYPE 配置当前类为原型模式,即多例Bean

我们现在来配置MessageService为原型模式,使其能够生成多个Bean:

 

java

复制代码

package com.gitee.swsk33.springbootdemo.service; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; // 配置该类为原型模式(多例Bean模式) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Component public class MessageService { // 省略其它... }

好的,现在我们在之前的两个测试类(TestOneTestTwo)中自动装配该类并打印哈希码试试:

image.png

可见由于配置了该类为原型模式,这样只要每进行一次自动装配,就会实例化一个新的MessageService对象,这样不同类中自动装配的MessageService对象也就不再是同一个了。

我们再使用BeanFactory取出两次MessageService的Bean试试:

image.png

可见,配置一个类为原型模式,使其能够生成多个Bean是非常简单的。

对于原型模式的类,通常不建议使用@Autowired装配它,否则可能会导致一些资源的浪费,而是建议在需要的时候使用BeanFactory去实例化它并使用。

除非是一些有状态的类要生成为Bean,不然对于绝大多数无状态的类的Bean,仍然建议沿用默认的单例模式。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值