个人见解:spring中原型和单例模式的差别

简介:
首先spring里有个比较关键的概念叫做上下文 也就是ApplicationContext。这是个什么玩意儿呢?

ApplicationContext(经常被称为上下文或Spring上下文)是Spring框架的关键部分。它是Spring的容器,用于存储和管理Bean的实例。ApplicationContext负责实例化、配置和组装beans,即所谓的依赖注入。

当你在应用程序中调用context.getBean()方法时,你是在给定的Spring上下文(context)中获取一个命名的Bean,Spring会查找并返回与所提供的名称匹配的Bean。

例如:context.getBean("studentService"),这个方法调用会返回一个名为studentService的Bean。


注意不一样的点来了!!

  1. 如果这个Bean在Spring配置文件中定义为单例模式getBean()方法会返回容器中的唯一实例。
  2. 如果这个Bean定义为的是原型类型,getBean()方法会为每个调用创建一个新的实例。

那么我们更具体的说:单例模式 (Singleton) 是Spring的默认的bean作用域(在springboot框架中可以通过注释@Scope去切换模式,默认状态下的都是单例模式)。在spring的ioc容器中,每个ID只对应一个bean实例,该实例在第一次创建后会被缓存,以后每次请求该bean,都会直接返回第一次创建的实例。这样可以确保在一个Spring IoC容器的上下文中,每个bean都是唯一的。

原型模式 (Prototype) 在每次请求它的时候都会创建一个新的bean实例,即使同一个ID,由于作用域是原型,所以返回的也是不同的实例。下面通过示例来演示一下

 

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public StudentService studentService() {
        return new StudentService();
    }
}
在这个例子中,StudentService的作用域通过@Scope("prototype")设置为原型,这意味每次调用studentService()方法,都将获得一个新的StudentService实例。
 


分析一下底层的原理我们可以知道
 

@Configuration
public class AppConfig {
    @Bean
    //@Scope("prototype") // 按需取消注释这行来观察效果变化
    public MyBean myBean() {
        return new MyBean();
    }
}

public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyBean bean1 = context.getBean(MyBean.class);
        MyBean bean2 = context.getBean(MyBean.class);
        System.out.println(bean1.hashCode());
        System.out.println(bean2.hashCode());
    }
}

在单例模式下(即没有取消注释 @Scope("prototype") 行),bean1 和 bean2 实际上指向同一个对象,它们的哈希码也因此相同。

在原型模式下(即取消注释 @Scope("prototype") 行),每次通过 context.getBean(MyBean.class); 获取的都将是不同的对象,因此,bean1 和 bean2 的哈希码将会不同,表明它们是不同的对象。

通过打印对象的哈希码来验证单例和原型模式下对象是否相同。哈希码通常是对象在内存中的地址转换成的一个数值,如果两个对象的哈希码相同,那么这两个对象通常就是同一个对象。那么就不难发现,实际上每次调用原型模式生成的实例之间其内存地址不同。熟悉hashcode方法的情况下就很容易去理解

光知道这些有什么用呢 ?
 

对于设计模式来说,我们只需要对其了解就已经足够了,原型模式实际上正常的开发流程中也比较少见,由于他的特点,在实际的开发流程中对原型模式的使用对开发者的技术要求比较高,如果对实例的管理机制不够了解,容易导致一些难以被发现的报错。


原型模式的应用场景可以大致的参考一下以下案例
 

状态不可共享bean
  • 假设我们有一个ShoppingCart类,这个类包含了当前用户的购物车信息:用户ID,商品列表等。由于每个用户的购物车都是不同的,这就需要为每个用户创建一个新的购物车实例。这样每一个实例就可以拥有自己的状态(用户ID,商品列表),并且这些状态之间不会互相干扰。在这种情况下,为ShoppingCart设置成原型模式就很合适

重要资源对象
  • 对于一些创建和销毁成本较高的对象,如果每次需要时都去创建和销毁,那么系统的开销会非常大。例如,在一些大型软件应用中,可能会为了提供服务而创建许多对数据库的连接(数据库连接的建立和销毁成本高),这个时候可以将数据库连接设置为原型模式,这样每次需要数据库连接时,都从Spring容器获取一个新的实例,替代新建一个数据库连接。

在运行时动态确定,创建哪一类的实例

  • 在有些情况下,你可能需要在运行时动态决定创建哪类对象。例如,你有多种排序算法类,这些算法类都实现自同一个接口。你可以在运行时,基于不同的输入或者条件,选择使用哪种排序算法,这时,可以将每种排序算法类设为原型Bean,这样就可以动态地从Spring中获取特定的排序算法实例。 

以上场景不一定需要原型模式来进行设计,总的来说还是需要更具需求再来讨论具体的技术方案的,抛开需求不谈一切都是在耍流氓。具体使用哪个模式,取决于你的具体需求。如果你的 Bean 是有状态的,或者你需要每次都得到一个全新的对象实例,那么原型模式可能是一个好选择。但如果你的 Bean 是无状态的,或者多个组件共享一个 Bean 实例没有问题,那么单例模式可能更有优势,因为它可以减少对象创建的开销。 

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值