详解Spring中的单例模式

目录

什么是单例模式?

单例模式是设计模式中最简单也是最常用的设计模式之一,单例顾名思义就是系统中只有唯一实例,这个唯一实例的获取方式就是通过一个方法的调用获得,而不是通过正常流程中的new实例化。在Spring中,单例模式的应用非常广泛,并且是Bean默认的作用域。

单例模式的优缺点

优点

  • Spring中,单例模式可以有效减少对象的创建和销毁次数,从而提高程序的性能和效率。当IOC容器维护Bean实例时,如果一个对象已经被创建了,那么以后每次请求该对象时,都会直接返回之前创建好的对象实例,避免了重复创建和销毁对象的开销,提高系统性能。

缺点

  • 单例模式一般没有接口,所以很难进行扩展。如果要扩展,除了修改代码基本没有第二种途径可以实现。

如何设置单例模式?

  • Spring默认使用单例模式来管理Bean的实例化。如果想要更改可以通过改变注解或配置文件中的scope属性进行修改。
    1. 配置文件:<bean id="us" class="com.example.ExampleBean" scope="singleton"/>

    2. 注解:

      java 复制代码
      @Component
      @Scope("singleton")
      public class MyBean {
          // 类定义
      }
scope的属性值 表示bean对象的作用范围
singleton 单例(默认)最常用的方式,生命周期和配置文件一样(配置文件加载出来后就会创建实例)
prototype 多例,不是加载配置文件的时候创建实例,获取实例时才创建
request 多例,不常用,应用于web项目中,每次http请求时创建一个新的实例
session 多例,不常用,应用于web项目中,同一个http session共享一个实例

单例模式的实现方式

  • 单例模式的实现方式主要有饿汉式单例和懒汉式单例两种。

饿汉式单例

  • 在类加载后就提前创建好了唯一实例, 而不是到用的时候才创建。
  • 唯一实例是在静态代码块里面,静态代码的执行处于类生命周期中的初始化阶段,由虚拟机保证其原子且安全执行。所以不用考虑这里的线程不安全问题,所以,饿汉式线程安全。
  • 因为需要提前加载,所以会比较占用内存。

懒汉式单例

  • 类加载时没有生成单例,只有当第一次调用getlnstance方法时才去创建这个单例。
  • 当创建多个线程调用getlnstance方法时,可能会出现多个线程同时判断没有new而执行多次new的情况,从而调用多次构造方法。(也就是会进行多次初始化)因此线程不安全。
  • 好处是节省内存,坏处就是用的时候才创建性能比较慢。

总结

线程安全 访问速度 性能
饿汉式 安全
懒汉式 不安全

单例模式的实际应用

UserDao类

java 复制代码
package com.qcby.dao;

public interface UserDao {
    public void hello();
}
java 复制代码
package com.qcby.dao.impl;

import com.qcby.dao.UserDao;

@Repository
public class UserDaoImpl implements UserDao {
    public void hello() {
        System.out.println("持久层:你好!");
    }
}

UserDaoImpl是一个DAO层的Bean组件,使用@Repository注解告诉Spring这是一个组件,Spring会默认创建一个UserDAO实例,并保存在IOC容器中,其他组件可以通过注入的方式来获取实例。

UserService类(成员变量包含一个UserDao)

java 复制代码
package com.qcby.service;

public interface UserService {
    public void hello();
}
java 复制代码
package com.qcby.service.impl;

import com.qcby.dao.UserDao;
import com.qcby.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
    private UserDao userDao;
    private String name;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setName(String name) {
        this.name = name;
    }

    public UserServiceImpl() {
    }

    public UserServiceImpl(UserDao userDao, String name) {
        this.userDao = userDao;
        this.name = name;
    }

    public void hello() {
        System.out.println("业务层:你好!");
        userDao.hello(); //通过业务层去调用持久层的方法
    }

    @Override
    public String toString() {
        return "UserServiceImpl{" +
                "userDao=" + userDao +
                ", name='" + name + '\'' +
                '}';
    }
}

这里的UserServiceImpl是一个Service层的组件,使用@Service注解告诉Spring这是一个组件。在UserServiceImpl中使用@Autowired注解将UserDAO注入进来,Spring会自动将之前保存在IOC容器中的单例UserDAO实例注入到UserServiceImpl中使用。

相关推荐
TT哇16 分钟前
【Java EE初阶】计算机是如何⼯作的
java·redis·java-ee
paopaokaka_luck21 分钟前
基于SpringBoot+Vue的电影售票系统(协同过滤算法)
vue.js·spring boot·后端
IT_10246 小时前
Spring Boot项目开发实战销售管理系统——系统设计!
大数据·spring boot·后端
Fireworkitte7 小时前
Apache POI 详解 - Java 操作 Excel/Word/PPT
java·apache·excel
weixin-a153003083167 小时前
【playwright篇】教程(十七)[html元素知识]
java·前端·html
DCTANT7 小时前
【原创】国产化适配-全量迁移MySQL数据到OpenGauss数据库
java·数据库·spring boot·mysql·opengauss
ai小鬼头7 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
Touper.7 小时前
SpringBoot -- 自动配置原理
java·spring boot·后端
黄雪超8 小时前
JVM——函数式语法糖:如何使用Function、Stream来编写函数式程序?
java·开发语言·jvm
ThetaarSofVenice8 小时前
对象的finalization机制Test
java·开发语言·jvm