结论
先说结论:其实Spring的单例模式并不是传统设计模式上的的单例模式
设计模式中的单例模式是指在整个JVM中,这个类只有一个对象被创建
Spring中的单例模式中只有一个对象被创建其实是基于bean的id的,在Spring容器中,相同的id的bean只会被创建一次,但是不同Id相同类型的bean可以被创建多次
验证结论
下面是关于上述结论的验证
我们在 bean.xml中注入两个类型一样,但是id不一样的bean
然后我们创建Spring 容器,并在代码中获取了 orderService 和 orderService1,其中orderService 获取了两次
看我们的打印结果
由此可见 前两次获取获取到的是同一个对象,但是后面一次获取获取到的是另一个对象,所以对于同一个id来说,获取到的是同一个对象,但是不同Id获取到的是不同对象,这就是基于id的单例模式
其实还有一种方式可以看到其实是创建了两个对象,我们直接打开Spring容器,在Spring容器中其实存在 BeanFactory 的引用的,在 BeanFactory 中有 Map<String,Object> singletonObjects ,他就是我们说的单例池
查看单例次其实是可以看到 orderService 和 orderService1 其实是两个对象
设计模式-单例模式
上面说了这么多,下面来说说设计模式中的单例模式
其实单例设计模式可以分为两种,一种是饿汉式(类加载就会导致该单实例对象被创建),一种是懒汉式(类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建)
饿汉式
其实饿汉式就是在类加载的时候创建对象,最常用的就是在静态代码块中或者是类对象进行实现
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance;
static {
// 可以在静态代码块中获取一些配置
instance = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
其实还有一种方法,就是将这个变量设为枚举的方式,他是一种极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式
/**
* 枚举方式
*/
public enum Singleton {
INSTANCE;
}
懒汉式
其实有很多实现方式,大部分就是提供一个静态方法,调用的时候判断有没有创建,如果创建了就直接返回,如果没有创建就创建之后再返回,但是很多时候可能会出现线程安全问题,大部分时候我们可以使用 双重检查锁+volatile关键字的方式实现懒汉式单例模式
/**
* 双重检查方式
*/
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
破坏单例模式
其实上述的代码除了饿汉式中的枚举方式除外,都可以破坏单例模式
有两种破坏单例模式的方法,一种是 序列化与反序列化方式 还有一种是 反射
序列化和反序列化
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
public class Test {
public static void main(String[] args) throws Exception {
//往文件中写对象
//writeObject2File();
//从文件中读取对象
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
//判断两个反序列化后的对象是否是同一个对象
System.out.println(s1 == s2);
}
private static Singleton readObjectFromFile() throws Exception {
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));
//第一个读取Singleton对象
Singleton instance = (Singleton) ois.readObject();
return instance;
}
// 向文件中写数据
public static void writeObject2File() throws Exception {
//获取Singleton类的对象
Singleton instance = Singleton.getInstance();
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
//将instance对象写出到文件中
oos.writeObject(instance);
}
}
上面代码运行结果是false
,表明序列化和反序列化已经破坏了单例设计模式
反射方式
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
public class Test {
public static void main(String[] args) throws Exception {
//获取Singleton类的字节码对象
Class clazz = Singleton.class;
//获取Singleton类的私有无参构造方法对象
Constructor constructor = clazz.getDeclaredConstructor();
//取消访问检查,暴力反射
constructor.setAccessible(true);
//创建Singleton类的对象s1
Singleton s1 = (Singleton) constructor.newInstance();
//创建Singleton类的对象s2
Singleton s2 = (Singleton) constructor.newInstance();
//判断通过反射创建的两个Singleton对象是否是同一个对象,结果为 false
System.out.println(s1 == s2);
}
}
序列化、反序列方式破坏单例模式的解决方法
在Singleton类中添加readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
反射方式破解单例的解决方法
其实是通过反射获取私有构造方法,然后通过构造方法进行创建对象
但是可以通过反射修改 flag 的值进行强制修改
public class Singleton implements Serializable {
private static boolean flag = false;
//私有构造方法
private Singleton() {
synchronized (Singleton.class) {
if(!flag){
throw new RunTimeException("不能创建多个对象");
}
if(SingletonHolder.INSTANCE != null){
throw new RunTimeException("不能创建多个对象");
}
flag = true;
}
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}