← Назад к вопросам
Сколько раз вызывается @PostConstruct?
2.2 Middle🔥 171 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# @PostConstruct в Java: когда вызывается и как часто
@PostConstruct — это аннотация для инициализации bean'а после создания. Это важно понимать для корректной работы Spring приложений.
Короткий ответ
@PostConstruct вызывается ровно ОДИН раз — сразу после создания объекта, когда все зависимости инжектированы.
@Component
public class MyService {
@PostConstruct
public void init() {
System.out.println("Инициализация"); // Выводится 1 раз
}
}
// При старте приложения:
// - Spring создаёт MyService
// - Инжектирует зависимости
// - Вызывает init() ← @PostConstruct
// - MyService готов к использованию
Жизненный цикл Spring Bean
1. Instantiation (создание объекта через конструктор)
↓
2. Setting properties (установка @Autowired, @Value)
↓
3. BeanNameAware.setBeanName() (если реализует)
↓
4. BeanClassLoaderAware, ApplicationContextAware (interfaces)
↓
5. @PostConstruct ← Вызывается ОДИН раз
↓
6. InitializingBean.afterPropertiesSet() (если реализует)
↓
7. <bean init-method="..."> (XML конфиг, если есть)
↓
BEAN READY (готов к использованию)
Примеры использования
Пример 1: Инициализация ресурсов
@Service
public class DatabaseService {
private DataSource dataSource;
private Connection connection;
@Autowired
private ConfigProperties config;
@PostConstruct
public void init() {
System.out.println("Инициализирую БД соединение...");
try {
// Используем уже инжектированный config
this.dataSource = createDataSource(config.getDbUrl());
this.connection = dataSource.getConnection();
System.out.println("БД готова к работе");
} catch (SQLException e) {
throw new RuntimeException("Failed to initialize DB", e);
}
}
@PreDestroy // Вызывается ОДИН раз при shutdown
public void cleanup() {
System.out.println("Закрываю БД соединение...");
try {
if (connection != null) connection.close();
if (dataSource != null) dataSource.close();
} catch (SQLException e) {
log.error("Error closing DB", e);
}
}
}
Пример 2: Кэширование данных
@Component
public class ReferenceDataCache {
private Map<String, Category> categoryCache;
private Map<String, Country> countryCache;
@Autowired
private CategoryRepository categoryRepository;
@Autowired
private CountryRepository countryRepository;
@PostConstruct
public void loadCaches() {
System.out.println("Загружаю reference data кэши...");
// Загружаем при старте один раз
this.categoryCache = categoryRepository.findAll()
.stream()
.collect(Collectors.toMap(Category::getCode, Function.identity()));
this.countryCache = countryRepository.findAll()
.stream()
.collect(Collectors.toMap(Country::getCode, Function.identity()));
System.out.println("Загружены " + categoryCache.size() + " категорий");
}
public Category getCategory(String code) {
return categoryCache.get(code); // Очень быстро из памяти
}
}
Пример 3: Валидация конфигурации
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String apiKey;
private String dbUrl;
private int maxConnections;
@PostConstruct
public void validate() {
if (apiKey == null || apiKey.isEmpty()) {
throw new IllegalArgumentException("app.api-key must be set");
}
if (maxConnections <= 0) {
throw new IllegalArgumentException("app.max-connections must be > 0");
}
System.out.println("Конфигурация валидна");
}
}
Когда НЕ вызывается @PostConstruct
Случай 1: Без @Component/@Service/@Repository
// НЕПРАВИЛЬНО: @PostConstruct НЕ вызывается
public class MyService {
@PostConstruct
public void init() {
System.out.println("Может быть не вызвано!");
}
}
// ПРАВИЛЬНО: добавляем @Service
@Service
public class MyService {
@PostConstruct
public void init() {
System.out.println("Вызовется гарантировано");
}
}
Случай 2: Если bean лень инициализируется
@Service
public class LazyService {
@PostConstruct
public void init() {
System.out.println("Когда вызовется?");
}
}
// Если внедрить как lazy, @PostConstruct вызовется когда bean впервые запросят
@Bean
@Lazy // Отложенная инициализация
public LazyService lazyService() {
return new LazyService();
}
Случай 3: Если @PostConstruct выбросит исключение
@Service
public class BadService {
@PostConstruct
public void init() {
System.out.println("Начало инициализации");
throw new RuntimeException("Initialization failed!");
// Приложение не запустится!
}
}
// Spring выбросит исключение при старте
// Exception in thread "main" java.lang.RuntimeException: Initialization failed!
@PostConstruct vs конструктор
@Service
public class OrderService {
@Autowired
private OrderRepository repository; // null в конструкторе!
// НЕПРАВИЛЬНО: repository ещё не инжектирован
public OrderService() {
System.out.println("Конструктор, repository = " + repository); // null!
// this.repository.findAll(); // NullPointerException!
}
// ПРАВИЛЬНО: repository уже инжектирован
@PostConstruct
public void init() {
System.out.println("PostConstruct, repository = " + repository); // не null!
this.repository.findAll(); // Работает!
}
}
@PostConstruct vs InitializingBean
// Оба способа
// Способ 1: Аннотация @PostConstruct
@Component
public class Service1 {
@PostConstruct
public void init() {
System.out.println("Init 1");
}
}
// Способ 2: Интерфейс InitializingBean
@Component
public class Service2 implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Init 2");
}
}
// Способ 3: XML <bean init-method="init">
@Component
public class Service3 {
public void init() {
System.out.println("Init 3");
}
}
// Способ 4: @Bean(initMethod = "init")
@Configuration
public class Config {
@Bean(initMethod = "init")
public Service4 service4() {
return new Service4();
}
}
// Порядок вызова:
// 1. @PostConstruct
// 2. InitializingBean.afterPropertiesSet()
// 3. init-method (XML или @Bean)
// Рекомендуется: просто использовать @PostConstruct
Сколько раз вызывается в разных сценариях
Сценарий 1: Singleton (по умолчанию)
@Service // scope = SINGLETON по умолчанию
public class MyService {
@PostConstruct
public void init() {
System.out.println("Вызывается 1 раз при старте приложения");
}
}
// При старте Spring:
// - Spring создаёт MyService один раз
// - Вызывает @PostConstruct один раз
// Сколько раз использовать MyService — неважно, @PostConstruct вызовется только один раз
Сценарий 2: Prototype scope
@Service
@Scope("prototype") // Новый bean на каждый request
public class RequestScopedService {
@PostConstruct
public void init() {
System.out.println("Вызывается каждый раз когда запрашивают новый bean");
}
}
// При инжекции:
MyService service1 = applicationContext.getBean(RequestScopedService.class);
// → Создаёт новый bean → Вызывает @PostConstruct
MyService service2 = applicationContext.getBean(RequestScopedService.class);
// → Создаёт ДРУГОЙ новый bean → Вызывает @PostConstruct СНОВА
// Вывод:
// Вызывается каждый раз когда запрашивают новый bean
// Вызывается каждый раз когда запрашивают новый bean
Сценарий 3: Request scope (в web приложении)
@Service
@Scope("request") // Новый bean на каждый HTTP request
public class UserContextService {
@PostConstruct
public void init() {
System.out.println("Инициализирую контекст пользователя");
}
@PreDestroy
public void cleanup() {
System.out.println("Очищаю контекст пользователя");
}
}
// Для каждого HTTP request:
// 1. Spring создаёт новый UserContextService
// 2. Вызывает @PostConstruct
// 3. Обрабатывает request
// 4. После request вызывает @PreDestroy
// 5. Удаляет bean
// За день с 1000 запросов:
// @PostConstruct вызовется 1000 раз!
Практический пример: что вызовется сколько раз
// app.properties
app.max-retries=3
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private int maxRetries;
@PostConstruct
public void init() {
System.out.println("AppConfig init, maxRetries = " + maxRetries);
// Вызовется 1 раз при старте приложения
}
}
@Service
public class UserService {
@Autowired
private AppConfig config;
@PostConstruct
public void init() {
System.out.println("UserService init, maxRetries = " + config.getMaxRetries());
// Вызовется 1 раз при старте приложения
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
// Нет @PostConstruct здесь
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAll(); // Вызывается много раз
}
}
// Вывод при старте:
// AppConfig init, maxRetries = 3
// UserService init, maxRetries = 3
// После каждого GET /users:
// (@PostConstruct не вызывается, инициализация была один раз)
Выводы
- @PostConstruct вызывается ровно один раз для Singleton beans (по умолчанию)
- Вызывается после инжекции всех зависимостей (@Autowired)
- Используй для инициализации ресурсов (БД, кэши, конфигурация)
- Для Prototype/Request scope — вызывается каждый раз при создании нового bean'а
- Если выбросишь исключение — приложение не запустится
- Всегда paired с @PreDestroy для cleanup'а ресурсов