Может ли Singleton бин иметь два экземпляра?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Может ли Singleton бин иметь два экземпляра?
Да, Singleton бин в Spring может иметь два экземпляра при определённых условиях.
Сценарий 1: Несколько ApplicationContext
@Configuration
public class Config1 {
@Bean
public SingletonService singletonService() {
return new SingletonService();
}
}
@Configuration
public class Config2 {
@Bean
public SingletonService singletonService() {
return new SingletonService();
}
}
public class Main {
public static void main(String[] args) {
ApplicationContext context1 = new AnnotationConfigApplicationContext(Config1.class);
ApplicationContext context2 = new AnnotationConfigApplicationContext(Config2.class);
SingletonService bean1 = context1.getBean(SingletonService.class);
SingletonService bean2 = context2.getBean(SingletonService.class);
System.out.println(bean1 == bean2); // false - ДВА разных экземпляра
}
}
Когда две разные ApplicationContext создают бины — это ДВА разных экземпляра.
Сценарий 2: ClassLoader
Если один и тот же класс загружен двумя разными ClassLoader'ами — это будут два разных класса и два разных экземпляра.
Сценарий 3: Сериализация
@Component
public class SingletonService implements Serializable {
private Object readResolve() {
return this;
}
}
Без метода readResolve() десериализация создаёт новый объект.
Сценарий 4: Reflection
public class EagerSingleton {
public static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() { }
}
Constructor<?> constructor = EagerSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
EagerSingleton obj2 = (EagerSingleton) constructor.newInstance();
Reflection может создать новый экземпляр, обойдя приватный конструктор.
Почему это происходит?
- Spring Singleton — это Singleton на уровне контейнера, не на уровне JVM
- Разные контексты — разные контейнеры, разные бины
- ClassLoader — разные загрузчики = разные классы
- Reflection — может обойти приватные конструкторы
- Сериализация — десериализация создаёт новый объект
Гарантированный Singleton — Enum
public enum TrueSingleton {
INSTANCE;
public void doSomething() {
System.out.println("Истинный Singleton");
}
}
Enum — это наиболее безопасный способ. Он:
- Потокобезопасен по умолчанию
- Защищен от сериализации
- Защищен от Reflection
- Не может быть клонирован
Bill Pugh Singleton
public class BillPughSingleton {
private BillPughSingleton() { }
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
Паттерн использует классовую инициализацию для потокобезопасного Singleton.
Итог
Spring Singleton НЕ гарантирует ровно один экземпляр на всё приложение. Он гарантирует один экземпляр в рамках одного ApplicationContext.
Для действительно глобального Singleton используй Enum или Bill Pugh паттерн.