Придерживается ли бин Prototype жизненного цикла
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Придерживается ли бин Prototype жизненного цикла
Ответ: Да, но ТОЛЬКО для инициализации (init callbacks), НО НЕ для деструкции (destroy callbacks). Это критическое отличие от Singleton scope.
Жизненный цикл Prototype бина
Prototype scope создаёт новый экземпляр каждый раз, когда он запрашивается. Spring не управляет полным жизненным циклом такого бина.
ZingarettionOne (Singleton) Prototype
───────────────────────────────── ──────────────────────────────
1. Создание экземпляра 1. Создание экземпляра
2. Внедрение зависимостей 2. Внедрение зависимостей
3. Вызов @PostConstruct 3. Вызов @PostConstruct ✓
4. Использование в приложении 4. Использование в приложении
5. Вызов @PreDestroy ✓ 5. Использование ЗАВЕРШАЕТСЯ
6. Деструкция 6. Деструкция (Spring НЕ отвечает)
↓
Зависит от сборщика мусора!
Пример
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeService {
@PostConstruct
public void init() {
System.out.println("PrototypeService инициализирован");
// Вызовётся ДЛЯ КАЖДОГО экземпляра ✓
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeService уничтожен");
// НЕ гарантируется вызвать! ✗
// Spring не управляет жизненным циклом
}
public void doWork() {
System.out.println("Работаю");
}
}
@Component
public class Application {
@Autowired
private PrototypeService service;
public void run() {
// Каждый раз новый экземпляр
service.doWork(); // PrototypeService инициализирован
// Работаю
service.doWork(); // НОВЫЙ экземпляр инициализирован
// Работаю
// destroy НЕ ВЫЗОВЕТСЯ для предыдущих экземпляров!
}
}
Вывод в консоль:
PrototypeService инициализирован
Работаю
PrototypeService инициализирован
Работаю
// destroy НЕ вызывается!
Почему Spring не управляет деструкцией Prototype?
Основная причина: Spring не знает, когда закончится использование экземпляра.
@Component
public class MyService {
@Autowired
private PrototypeService proto;
public void process() {
proto.doWork();
// Когда proto больше не нужен?
// Сейчас? Или в конце метода? Или когда MyService удалится?
// Spring не может это определить!
}
}
С Singleton всё просто:
// Singleton
@Component
public class SingletonService { }
// Spring знает: когда приложение завершается,
// SingletonService больше не нужен
// Вызовется @PreDestroy
Сравнение жизненных циклов
| Этап | Singleton | Prototype |
|---|---|---|
| Создание | 1 раз при старте | Каждый раз при запросе |
| Внедрение зависимостей | ✓ | ✓ |
| @PostConstruct | ✓ Вызывается | ✓ Вызывается |
| Использование | Весь lifetime приложения | До конца ссылки |
| @PreDestroy | ✓ При shutdown | ✗ НЕ гарантировано |
| Сборка мусора | При shutdown | Когда нет ссылок |
Практический пример проблемы
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DatabaseConnection {
private Connection conn;
@PostConstruct
public void init() throws SQLException {
conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
System.out.println("Соединение открыто");
}
@PreDestroy
public void close() throws SQLException {
conn.close();
System.out.println("Соединение закрыто");
}
public void query(String sql) {
// использование соединения
}
}
@Component
public class UserRepository {
@Autowired
private DatabaseConnection dbConn;
public User findById(Long id) {
dbConn.query("SELECT * FROM users WHERE id = " + id);
// После метода dbConn НЕ закроется!
// Соединение останется открытым до сборки мусора
return new User();
}
}
Результат:
Соединение открыто
Соединение открыто
Соединение открыто
... (множество открытых соединений)
Соединение закрыто (когда?!)
Это приведёт к утечке ресурсов!
Решение 1: ObjectProvider с методом cleanup
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DatabaseConnection implements Closeable {
private Connection conn;
@PostConstruct
public void init() throws SQLException {
conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
}
@Override
public void close() throws IOException {
try {
if (conn != null) conn.close();
} catch (SQLException e) {
throw new IOException(e);
}
}
}
@Component
public class UserRepository {
@Autowired
private ObjectProvider<DatabaseConnection> dbConnProvider;
public User findById(Long id) {
// Используем try-with-resources для гарантированного закрытия
try (DatabaseConnection dbConn = dbConnProvider.getObject()) {
dbConn.query("SELECT * FROM users WHERE id = " + id);
return new User();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Вывод:
Соединение открыто
Соединение закрыто ✓
Соединение открыто
Соединение закрыто ✓
Решение 2: Остаться Singleton
Для большинства случаев Prototype не нужен. Используй Singleton и управляй состоянием явно:
@Component // Singleton по умолчанию
public class DatabaseConnection {
private final ConnectionPool pool;
@PostConstruct
public void init() {
pool = new ConnectionPool();
}
@PreDestroy
public void close() {
pool.closeAll();
}
public Connection getConnection() {
return pool.getConnection();
}
}
Кога нужен Prototype?
Используй Prototype только если:
- Stateful объект, зависящий от контекста
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RequestContext {
private String userId;
private String requestId;
public void setUserId(String userId) {
this.userId = userId;
}
// Каждый request получает свой контекст
}
- Тяжёлый объект, создающийся редко
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ReportGenerator {
public void generate() {
// Тяжёлая обработка
}
}
- Объект с управляемым жизненным циклом
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserSession implements Closeable {
@PostConstruct
public void init() {
// инициализация
}
@Override
public void close() {
// очистка ЯВНО управляется
}
}
Ответ на вопрос
Придерживается ли бин Prototype жизненного цикла?
✓ ДА для инициализации:
- @PostConstruct вызовется для каждого экземпляра
- Зависимости будут внедрены
- Конфигурация применится
✗ НЕТ для деструкции:
- @PreDestroy НЕ гарантировано вызовется
- Spring не отвечает за очистку ресурсов
- Зависит от сборщика мусора
Практический совет: Если твой Prototype использует ресурсы (БД, файлы, сокеты), управляй очисткой явно через try-with-resources или ObjectProvider.