С какими типичными проблемами вы сталкивались при разработке на Spring Boot?

Ответ

Spring Boot упрощает конфигурацию, но может приводить к специфичным проблемам:

1. Конфликты автоконфигурации и бинов

  • Проблема: Несколько бинов реализуют один интерфейс, что приводит к NoUniqueBeanDefinitionException при инъекции.
  • Решение: Использовать аннотации @Primary (указать основной бин) или @Qualifier (уточнить имя).

    @Configuration
    public class AppConfig {
        @Bean
        @Primary // Этот DataSource будет выбран по умолчанию
        public DataSource primaryDataSource() { ... }
    
        @Bean
        public DataSource secondaryDataSource() { ... }
    }
    
    @Service
    public class MyService {
        // Уточняем, какой именно бин внедрять
        @Autowired
        @Qualifier("secondaryDataSource")
        private DataSource dataSource;
    }

2. Циркулярные зависимости (Circular Dependencies)

  • Проблема: Бин A зависит от бина B, который в свою очередь зависит от бина A. Spring выбрасывает BeanCurrentlyInCreationException.
  • Решение:
    • Рефакторинг: Вынести общую логику в третий компонент.
    • Использовать @Lazy: Одна из зависимостей инициализируется лениво, разрывая цикл на этапе создания контекста.
    • Setter/Field Injection: Вместо инъекции через конструктор (хотя инъекция через конструктор является best practice).

3. Проблемы с профилями и свойствами (Profiles & Properties)

  • Проблема: Неочевидный порядок загрузки свойств из application.yml, application-{profile}.yml, переменных окружения, что приводит к неожиданным значениям.
  • Решение: Четко понимать официальный порядок переопределения свойств. Использовать @ConfigurationProperties для типобезопасной работы.

4. Долгий запуск приложения

  • Проблема: Большой контекст Spring с множеством бинов и компонентов сканирования увеличивает время старта.
  • Решение:
    • Включить ленивую инициализацию (spring.main.lazy-initialization=true).
    • Сузить область сканирования компонентов (@SpringBootApplication(scanBasePackages = "com.myapp")).
    • Использовать Spring Boot 2.4+ с поддержкой иерархических контекстов для модульных приложений.

5. Ошибки управления транзакциями

  • Проблема: Метод, помеченный @Transactional, вызывается изнутри того же класса (через this.method()), что обходит AOP-прокси и отключает управление транзакциями.
  • Решение: Инжектировать сам бин или выносить транзакционную логику в отдельный сервис.

6. Несовместимость версий зависимостей

  • Проблема: Конфликты транзитивных зависимостей, особенно в больших проектах.
  • Решение: Использовать spring-boot-dependencies BOM (Bill of Materials) для согласованных версий. Анализировать дерево зависимостей (mvn dependency:tree или gradle dependencies).

Ответ 18+ 🔞

А, ну вот, классика, блядь! Spring Boot, говоришь? Ну, инструмент, конечно, охуенный, но иногда он такую пиздопроебибну устраивает, что волосы дыбом встают. Слушай сюда, разжуем по косточкам.

1. Конфликты автоконфигурации и этих самых... бинов.

  • В чём пиздец: Напихали в проект кучу библиотек, а они там по-тихому свои бины регистрируют. Потом ты пытаешься @Autowired какой-нибудь DataSource, а он тебе в ответ: «NoUniqueBeanDefinitionException, мудила, а какой из трёх тебе, собственно, впендюрить?». Удивление пиздец.
  • Как выкрутиться: Нужно указать Spring'у, кто тут главный. Либо @Primary (этот бин — царь и бог, бери его), либо @Qualifier (эй, Spring, дай-ка мне именно того, который «secondaryDataSource», а не левую шнягу).

    @Configuration
    public class AppConfig {
        @Bean
        @Primary // Этот чувак будет выбран автоматом, когда мозги у Spring'а закипят
        public DataSource primaryDataSource() { ... }
    
        @Bean
        public DataSource secondaryDataSource() { ... }
    }
    
    @Service
    public class MyService {
        // А тут мы чётко говорим: «Нет, дружок, давай того, второго»
        @Autowired
        @Qualifier("secondaryDataSource")
        private DataSource dataSource;
    }

2. Циркулярные зависимости, ёпта.

  • В чём пиздец: Представь: Сервис А говорит — «Я не создамся, пока нет Сервиса Б». А Сервис Б орёт — «А я нихуя не сделаю, пока нет Сервиса А!». И стоят, упёршись рогами. Spring смотрит на это и выдаёт BeanCurrentlyInCreationException. Чистая драма, блядь.
  • Как выкрутиться:
    • Рефакторить, блядь. Вынести общую логику в третий компонент, пусть они оба от него зависят — как дети от папы.
    • Сделать одного ленивым. @Lazy одной из зависимостей. Типа «окей, я про тебя вспомню позже, когда реально понадобишься». Цикл рвётся.
    • Использовать сеттер вместо конструктора. Хотя это и не самый красивый способ, иногда работает.

3. Профили и свойства — тёмный лес.

  • В чём пиздец: У тебя application.yml, потом application-prod.yml, плюс переменные окружения, плюс аргументы командной строки. И в итоге в проде работает не то, что ты ожидал, потому что какая-то хуйня перезаписала твоё свойство. Подозрение ебать чувствую, что где-то косяк.
  • Как выкрутиться: Выучить, блядь, официальный порядок. Кто кого перебивает. И использовать @ConfigurationProperties — тогда хоть IDE подскажет, если опечатку сделаешь.

4. Долгий запуск — терпения ноль ебать.

  • В чём пиздец: Жмёшь «Run», идёшь чай делать, возвращаешься — а он всё ещё грузится. Контекст раздулся до овердохуища, сканирует пол-интернета.
  • Как выкрутиться:
    • Включить ленивую инициализацию (spring.main.lazy-initialization=true). Пусть бины создаются, когда к ним первый раз обратятся, а не все скопом на старте.
    • Сузить круг сканирования. Зачем ему весь пакет com смотреть? Укажи @SpringBootApplication(scanBasePackages = "com.myapp").
    • Обновляйся до свежих версий, там с иерархическими контекстами лучше.

5. Транзакции, которые нихуя не транзакции.

  • В чём пиздец: Классика жанра! Пометил метод @Transactional, вызываешь его из другого метода того же класса (просто this.method()). А Spring-то работает через прокси! И вызов внутри класса обходит эту проксю, и транзакция не открывается. И ты потом сидишь и думаешь: «Почему данные не сохранились? Я же всё правильно сделал!». Сам от себя охуел.
  • Как выкрутиться: Либо инжектить сам бин в себя (звучит как извращение, но работает), либо выносить транзакционную логику в отдельный сервисный слой.

6. Несовместимость версий — пидары налетели.

  • В чём пиздец: Подключил какую-нибудь библиотеку для красоты, а она тащит за собой старую версию какого-нибудь commons-logging, которая конфликтует с тем, что уже есть в Spring Boot. И приложение падает с каким-нибудь ClassNotFoundException на ровном месте. Кот, сука, собака!
  • Как выкрутиться: Spring Boot BOM — твой лучший друг, он держит версии в узде. Смотри дерево зависимостей (mvn dependency:tree). Если видишь одну и ту же либу в разных версиях — пристрели лишнюю через <exclusions> в Maven или exclude в Gradle.

Вот так вот, блядь. Инструмент мощный, но если не понимать, как он устроен внутри, можно на ровном месте обосраться. Главное — не паниковать, читать логи и помнить, что ты не первый, кто с этим столкнулся.