目录
什么是单例模式?
单例模式是设计模式中最简单也是最常用的设计模式之一,单例顾名思义就是系统中只有唯一实例,这个唯一实例的获取方式就是通过一个方法的调用获得,而不是通过正常流程中的
new
实例化。在Spring
中,单例模式的应用非常广泛,并且是Bean
默认的作用域。
单例模式的优缺点
优点
- 在
Spring
中,单例模式可以有效减少对象的创建和销毁次数,从而提高程序的性能和效率。当IOC
容器维护Bean
实例时,如果一个对象已经被创建了,那么以后每次请求该对象时,都会直接返回之前创建好的对象实例,避免了重复创建和销毁对象的开销,提高系统性能。
缺点
- 单例模式一般没有接口,所以很难进行扩展。如果要扩展,除了修改代码基本没有第二种途径可以实现。
如何设置单例模式?
- Spring默认使用单例模式来管理Bean的实例化。如果想要更改可以通过改变注解或配置文件中的
scope
属性进行修改。-
配置文件:
<bean id="us" class="com.example.ExampleBean" scope="singleton"/>
-
注解:
@Component @Scope("singleton") public class MyBean { // 类定义 }
-
scope的属性值 | 表示bean对象的作用范围 |
---|---|
singleton | 单例(默认)最常用的方式,生命周期和配置文件一样(配置文件加载出来后就会创建实例) |
prototype | 多例,不是加载配置文件的时候创建实例,获取实例时才创建 |
request | 多例,不常用,应用于web项目中,每次http请求时创建一个新的实例 |
session | 多例,不常用,应用于web项目中,同一个http session共享一个实例 |
单例模式的实现方式
- 单例模式的实现方式主要有饿汉式单例和懒汉式单例两种。
饿汉式单例
- 在类加载后就提前创建好了唯一实例, 而不是到用的时候才创建。
- 唯一实例是在静态代码块里面,静态代码的执行处于类生命周期中的初始化阶段,由虚拟机保证其原子且安全执行。所以不用考虑这里的线程不安全问题,所以,饿汉式线程安全。
- 因为需要提前加载,所以会比较占用内存。
懒汉式单例
- 类加载时没有生成单例,只有当第一次调用
getlnstance
方法时才去创建这个单例。 - 当创建多个线程调用
getlnstance
方法时,可能会出现多个线程同时判断没有new
而执行多次new
的情况,从而调用多次构造方法。(也就是会进行多次初始化)因此线程不安全。 - 好处是节省内存,坏处就是用的时候才创建性能比较慢。
总结
线程安全 | 访问速度 | 性能 | |
---|---|---|---|
饿汉式 | 安全 | 快 | 差 |
懒汉式 | 不安全 | 慢 | 好 |
单例模式的实际应用
UserDao类
package com.qcby.dao;
public interface UserDao {
public void hello();
}
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)
package com.qcby.service;
public interface UserService {
public void hello();
}
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
中使用。