← Назад к вопросам

Придерживается ли бин Prototype жизненного цикла

1.3 Junior🔥 171 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Придерживается ли бин 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

Сравнение жизненных циклов

ЭтапSingletonPrototype
Создание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 только если:

  1. Stateful объект, зависящий от контекста
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RequestContext {
    private String userId;
    private String requestId;
    
    public void setUserId(String userId) {
        this.userId = userId;
    }
    
    // Каждый request получает свой контекст
}
  1. Тяжёлый объект, создающийся редко
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ReportGenerator {
    public void generate() {
        // Тяжёлая обработка
    }
}
  1. Объект с управляемым жизненным циклом
@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.

Придерживается ли бин Prototype жизненного цикла | PrepBro