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

Где можно использовать аннотацию 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 уже ленивый
}

Выводы

  1. @Lazy для дорогих операций инициализации
  2. Используй на классах, параметрах, полях методов конфигурации
  3. Правило 80/20: большинство бинов без @Lazy
  4. Для опциональных зависимостей — используй Optional вместо @Lazy
  5. @Lazy создаёт proxy, будь готов к этому при отладке
  6. Помогает разрешить циклические зависимости
  7. На продакшене профилируй — убедись, что @Lazy действительно нужна
Где можно использовать аннотацию Lazy в Spring? | PrepBro