← Назад к вопросам
Где можно использовать аннотацию Lazy в Spring?
2.0 Middle🔥 231 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Аннотация @Lazy в Spring Framework
@Lazy — это аннотация для отложенной инициализации бинов. Вместо создания бина при старте приложения, бин будет создан только в момент, когда он впервые запрошен.
Где использовать @Lazy
1. На классе (Component)
Делает весь бин ленивым:
@Component
@Lazy
public class ExpensiveService {
public ExpensiveService() {
System.out.println("ExpensiveService инициализирован");
}
public String getData() {
return "Data from ExpensiveService";
}
}
@Component
public class Controller {
private final ExpensiveService service; // Бин будет создан при первом обращении
public Controller(ExpensiveService service) {
this.service = service;
}
public void doSomething() {
service.getData(); // Здесь ExpensiveService будет создан
}
}
2. На полях (Field Injection)
@Component
public class MyService {
@Autowired
@Lazy // Бин будет создан при первом обращении
private DatabaseService dbService;
public void query() {
String result = dbService.execute("SELECT *"); // dbService инициализируется здесь
}
}
3. На параметрах конструктора
@Component
public class UserManager {
private final UserRepository userRepo;
private final MailService mailService; // Ленивый
public UserManager(
UserRepository userRepo,
@Lazy MailService mailService // Инициализируется при первом использовании
) {
this.userRepo = userRepo;
this.mailService = mailService;
}
public void createUser(String email) {
userRepo.save(new User(email));
mailService.send(email, "Welcome"); // Здесь MailService создастся
}
}
4. На методах конфигурации
@Configuration
public class AppConfig {
@Bean
@Lazy // DatabasePool создастся при первом использовании
public DatabasePool databasePool() {
System.out.println("Инициализирую дорогой пул БД");
return new DatabasePool("jdbc:mysql://localhost:3306/mydb");
}
@Bean
public UserService userService(DatabasePool pool) {
return new UserService(pool); // pool создастся здесь
}
}
Практические примеры
Пример 1: Дорогой сервис (Heavy Service)
@Component
@Lazy // Инициализируем только если нужен
public class AIMLService {
private final Model model;
public AIMLService() {
System.out.println("Загружаю ML модель в память (2GB)...");
this.model = ModelLoader.loadLargeModel(); // Долгая операция
System.out.println("ML модель загружена");
}
public String predict(String input) {
return model.predict(input);
}
}
@Component
public class RecommendationService {
private final UserRepository userRepo;
private final AIMLService aiService; // Ленивый
public RecommendationService(
UserRepository userRepo,
@Lazy AIMLService aiService
) {
this.userRepo = userRepo;
this.aiService = aiService;
}
public List<String> getRecommendations(Long userId) {
// Если нужны рекомендации, используем ML
User user = userRepo.findById(userId).orElseThrow();
return aiService.predict(user.getBehavior()); // ML инициализируется здесь
}
}
Пример 2: Опциональный сервис (Optional Service)
@Component
public class OrderService {
private final OrderRepository orderRepo;
private final AnalyticsService analytics; // Может не использоваться
public OrderService(
OrderRepository orderRepo,
@Lazy AnalyticsService analytics
) {
this.orderRepo = orderRepo;
this.analytics = analytics;
}
public Order createOrder(OrderRequest request) {
Order order = orderRepo.save(new Order(request));
// analytics может и не быть нужен в некоторых сценариях
return order;
}
public void analyzeOrders() {
// Здесь используем analytics
analytics.generateReport(); // Инициализируется при необходимости
}
}
Пример 3: Кэширование с @Lazy
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("default");
}
@Bean
@Lazy // Redis подключение создаётся только если используется
public RedisTemplate<String, Object> redisTemplate() {
System.out.println("Подключаюсь к Redis...");
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
return template;
}
@Bean
public UserService userService() {
return new UserService(); // Может использовать Redis или нет
}
}
Когда использовать @Lazy
| Сценарий | Использовать? | Пример |
|---|---|---|
| Дорогая инициализация | Да | AI/ML модели, большие БД пулы |
| Редко используемый сервис | Да | Analytics, Reporting |
| Может быть null | Нет (используй Optional) | - |
| Критичное нужно при старте | Нет | Основные сервисы |
| Опциональное подключение | Да | Redis, S3, Email |
@Lazy с ObjectFactory и ObjectProvider
ObjectFactory (старый подход)
@Component
public class UserService {
private final ObjectFactory<EmailService> emailServiceFactory;
public UserService(ObjectFactory<EmailService> emailServiceFactory) {
this.emailServiceFactory = emailServiceFactory;
}
public void registerUser(String email) {
// EmailService создастся только здесь
EmailService emailService = emailServiceFactory.getObject();
emailService.sendWelcomeEmail(email);
}
}
ObjectProvider (новый подход, Spring 5.0+)
@Component
public class UserService {
private final ObjectProvider<EmailService> emailServiceProvider;
public UserService(ObjectProvider<EmailService> emailServiceProvider) {
this.emailServiceProvider = emailServiceProvider;
}
public void registerUser(String email) {
// EmailService создастся только если getIfAvailable() вернёт значение
emailServiceProvider.ifAvailable(
service -> service.sendWelcomeEmail(email)
);
}
}
Важные особенности
1. Потокобезопасность
При первом обращении из разных потоков есть небольшое окно race condition:
@Component
@Lazy
public class MyService {
public MyService() {
System.out.println("Инициализация");
}
}
// Если две нити одновременно обратятся, может быть двойная инициализация
Thread t1 = new Thread(() -> {
MyService service = ctx.getBean(MyService.class); // Может создать
});
Thread t2 = new Thread(() -> {
MyService service = ctx.getBean(MyService.class); // Может создать ещё раз
});
Spring обрабатывает это синхронизацией, но учитывай это.
2. Циклические зависимости
@Lazy может помочь разрешить циклические зависимости:
@Component
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) { // Разрывает цикл
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private final ServiceA serviceA; // ServiceA создана, ServiceB ещё нет
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
3. Proxy объекты
При использовании @Lazy, Spring создаёт proxy вместо реального объекта:
@Component
@Lazy
public class RealService {
public void doSomething() {
System.out.println("Real work");
}
}
@Component
public class Consumer {
private final RealService service; // Это не RealService, а его proxy
public Consumer(RealService service) {
System.out.println(service.getClass()); // Выведет что-то типа RealService$Proxy
this.service = service;
}
}
Антипаттерны
Неправильно: @Lazy везде
// Плохо — усложняет отладку
@Component
@Lazy
public class SimpleService { // Не нужна ленивая инициализация
public String getData() {
return "data";
}
}
Неправильно: @Lazy для null-able зависимостей
// Плохо
@Component
public class Service {
@Autowired
@Lazy
private Optional<EmailService> emailService; // Противоречие
}
// Хорошо
@Component
public class Service {
@Autowired
private Optional<EmailService> emailService; // Optional уже ленивый
}
Выводы
- @Lazy для дорогих операций инициализации
- Используй на классах, параметрах, полях методов конфигурации
- Правило 80/20: большинство бинов без @Lazy
- Для опциональных зависимостей — используй Optional вместо @Lazy
- @Lazy создаёт proxy, будь готов к этому при отладке
- Помогает разрешить циклические зависимости
- На продакшене профилируй — убедись, что @Lazy действительно нужна