Как создать Singleton в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как создать Singleton в Spring
Spring автоматически управляет жизненным циклом beans и по умолчанию создаёт их как singleton (одна единственная инстанция на весь контекст приложения). Однако это требует правильного понимания scope beans и способов создания. Рассмотрим все подходы.
1. Автоматический Singleton через @Bean и @Component
По умолчанию все beans в Spring имеют scope = singleton. Это значит, что Spring создаст одну инстанцию на весь контекст приложения.
@Component
public class DatabaseConnection {
public DatabaseConnection() {
System.out.println("DatabaseConnection создан");
}
public void connect() {
System.out.println("Подключение к БД");
}
}
@Configuration
public class AppConfig {
@Bean
public DatabaseConnection databaseConnection() {
return new DatabaseConnection();
}
}
Можно проверить, что это действительно одна и та же инстанция:
@RestController
public class TestController {
@Autowired
private DatabaseConnection db1;
@Autowired
private DatabaseConnection db2;
@GetMapping("/test")
public String test() {
return db1 == db2 ? "Singleton: true" : "Singleton: false";
}
}
2. Явное указание scope = singleton
Хотя это и дефолт, можно явно указать scope:
@Component
@Scope("singleton")
public class UserService {
}
@Configuration
public class AppConfig {
@Bean
@Scope("singleton")
public UserService userService() {
return new UserService();
}
}
3. Singleton с инициализацией и очисткой
Часто singleton нужно инициализировать при создании и очищать при завершении:
@Component
public class DatabasePool {
private List<Connection> connections = new ArrayList<>();
@PostConstruct
public void init() {
System.out.println("Инициализация пула");
for (int i = 0; i < 5; i++) {
connections.add(createConnection());
}
}
@PreDestroy
public void cleanup() {
System.out.println("Очистка пула");
connections.forEach(Connection::close);
connections.clear();
}
public Connection getConnection() {
return connections.get(0);
}
private Connection createConnection() {
return null;
}
}
4. Lazy инициализация Singleton
По умолчанию singleton инициализируется при запуске приложения. Если нужна ленивая инициализация:
@Component
@Lazy
public class ExpensiveService {
public ExpensiveService() {
System.out.println("ExpensiveService создан");
}
}
5. Thread-safe Singleton через synchronized
Если ты создаёшь singleton вручную (не через Spring), нужна синхронизация:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Но в Spring это не нужно — контейнер сам гарантирует thread-safety!
6. Double-checked locking
Для вручную созданного singleton:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
7. Enum Singleton
Этот подход гарантирует singleton и thread-safety:
public enum Singleton {
INSTANCE;
private String name = "Singleton";
public void doSomething() {
System.out.println(name);
}
}
Singleton.INSTANCE.doSomething();
8. Singleton как Static Factory
public class DatabaseConnection {
private static final DatabaseConnection INSTANCE = new DatabaseConnection();
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
return INSTANCE;
}
}
9. Spring Profile-specific Singleton
Разные singleton реализации для разных окружений:
@Configuration
public class AppConfig {
@Bean
@Profile("dev")
public DatabaseService devDatabase() {
return new H2DatabaseService();
}
@Bean
@Profile("prod")
public DatabaseService prodDatabase() {
return new PostgresDatabaseService();
}
}
10. Nested Singleton Pattern
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Типичные ошибки
Ошибка 1: Использование new вместо Autowired
Неправильно:
public class Controller {
private UserService userService = new UserService();
}
Правильно:
public class Controller {
@Autowired
private UserService userService;
}
Ошибка 2: Неправильное управление жизненным циклом
Не забывай про @PostConstruct и @PreDestroy для инициализации и очистки ресурсов.
Вывод
В Spring Singleton по умолчанию — это встроенная функция контейнера. Для простых случаев используй @Component или @Bean. Для сложной логики — добавь @PostConstruct/@PreDestroy. Spring гарантирует thread-safety автоматически, поэтому синхронизация не требуется.