Какие основные способы настройки и объявления бинов в Spring?

Ответ

Spring Framework предоставляет три основных подхода к конфигурации бинов (управляемых объектов), от классического до современного.

1. Конфигурация с помощью аннотаций (Рекомендуемый современный подход)

Основан на автоматическом сканировании классов и использовании аннотаций.

  • Объявление бина: Используются стереотипные аннотации. Spring обнаруживает их при включенном сканировании (@ComponentScan).

    @Component          // Универсальный бин
    @Service            // Бин бизнес-логики (семантический аналог @Component)
    @Repository         // Бин уровня доступа к данным (добавляет трансляцию исключений)
    @Controller / @RestController // Бин веб-уровня (MVC/REST)
    public class MyService { ... }
  • Внедрение зависимостей (Dependency Injection - DI):

    @Service
    public class OrderService {
        // 1. Внедрение через поле (Field Injection) - менее предпочтительно
        @Autowired
        private PaymentService paymentService;
    
        // 2. Внедрение через конструктор (Constructor Injection) - РЕКОМЕНДУЕТСЯ
        private final UserRepository userRepo;
        @Autowired // Необязательно с Spring 4.3+, если конструктор один
        public OrderService(UserRepository userRepo) {
            this.userRepo = userRepo;
        }
    
        // 3. Внедрение через сеттер (Setter Injection)
        private NotificationService notifier;
        @Autowired
        public void setNotifier(NotificationService notifier) {
            this.notifier = notifier;
        }
    }

2. Java-based конфигурация (@Configuration и @Bean)

Позволяет явно объявлять бины в Java-классах. Полезно для настройки сторонних библиотек или сложной логики создания бина.

@Configuration
public class AppConfig {
    @Bean // Объявляет метод как фабрику бина. Имя бина = имя метода.
    public DataSource dataSource() {
        return new HikariDataSource(...); // Создание и настройка сложного объекта
    }

    @Bean
    public OrderService orderService(UserRepository repo) {
        // Spring автоматически внедрит бин UserRepository как параметр
        return new OrderService(repo);
    }
}

3. XML-конфигурация (Устаревший, но поддерживаемый подход)

<beans>
    <!-- Объявление бина -->
    <bean id="myRepository" class="com.example.MyRepositoryImpl"/>
    <!-- Внедрение зависимости -->
    <bean id="myService" class="com.example.MyService">
        <constructor-arg ref="myRepository"/> <!-- Через конструктор -->
        <!-- или -->
        <property name="repository" ref="myRepository"/> <!-- Через сеттер -->
    </bean>
</beans>

Ключевые моменты:

  • @Autowired автоматически связывает бины по типу. Для разрешения неоднозначности используется @Qualifier("beanName").
  • Область видимости (Scope) бина задается аннотацией @Scope (например, "singleton" (по умолчанию), "prototype", "request", "session").
  • Современные приложения обычно используют комбинацию @ComponentScan (для своих классов) и @Bean (для внешней конфигурации).

Ответ 18+ 🔞

Слушай, а вот Spring Framework, он же, блядь, как шведский стол — подходи и выбирай, как тебе твои бины настраивать. Три способа, от дедовского XML до современной магии с аннотациями. Сейчас разжую, только не подавись.

Первый, самый модный — через аннотации (все эти @Component, @Service)

Вот это, блядь, магия чистой воды. Вместо того чтобы в XML-файлах, как в каменном веке, прописывать каждую сущность, ты просто пишешь над классом волшебное слово. Spring сканирует проект, видит эти аннотации и сам, охуев от такой наглости, создаёт из них бины.

@Component          // Универсальный бин, типа "создай из этого класса бин, нахуй"
@Service            // То же самое, но для сервисов (семантический сахар, блядь)
@Repository         // Для работы с данными (ещё и исключения ловит, хитрая жопа)
@Controller         // Для веба, чтобы запросы обрабатывать
public class MyService { ... }

А чтобы один бин в другой запихнуть (это называется Dependency Injection, или "впендюрить зависимость"), есть тоже варианты:

@Service
public class OrderService {
    // Способ 1: Внедрение в поле. Быстро, но говнисто. Не видно зависимостей, тестировать неудобно.
    @Autowired
    private PaymentService paymentService;

    // Способ 2: Внедрение через конструктор. Вот это, ёпта, красота! РЕКОМЕНДУЮ.
    private final UserRepository userRepo; // final, Карл! Безопасно и понятно.
    @Autowired // А эту аннотацию можно и не писать, если конструктор один — Spring и так догадается.
    public OrderService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    // Способ 3: Через сеттер. Ну, на любителя. Бывает нужно.
    private NotificationService notifier;
    @Autowired
    public void setNotifier(NotificationService notifier) {
        this.notifier = notifier;
    }
}

Второй способ — Java-based конфигурация (@Configuration)

Бывает, класс не твой (из библиотеки) или бин нужно собрать по сложной схеме, как шкаф из Икеи. Вот тут и выручает @Configuration. Ты сам, своими руками, в коде говоришь Spring'у: "Создай бин вот таким, блядь, способом".

@Configuration // Говорит: "В этом классе лежат рецепты бинов, читай внимательно"
public class AppConfig {
    @Bean // Метод, который возвращает объект. Spring вызовет его и результат засунет в контекст.
    public DataSource dataSource() {
        return new HikariDataSource(...); // Вот тебе, Spring, настрой пул соединений сам!
    }

    @Bean
    public OrderService orderService(UserRepository repo) { // Смотри-ка, параметр! Spring сам подставит нужный бин.
        return new OrderService(repo); // Конструируем сервис вручную.
    }
}

Третий — XML. Устаревший, но как памятник архитектуры.

Этим, блядь, пугают junior'ов. Всё вручную, тегами. Выглядит как древние скрижали.

<beans>
    <!-- Объявляем бин, типа "возьми этот класс и сделай из него бин с именем myRepository" -->
    <bean id="myRepository" class="com.example.MyRepositoryImpl"/>
    <!-- А теперь создаём сервис и засовываем в него репозиторий -->
    <bean id="myService" class="com.example.MyService">
        <constructor-arg ref="myRepository"/> <!-- Пропихнули через конструктор -->
        <!-- или так, через сеттер -->
        <property name="repository" ref="myRepository"/>
    </bean>
</beans>

А теперь, блядь, ключевые моменты, чтобы не облажаться:

  • @Autowired ищет бин по типу. Если нашёл два бина одного типа — будет конфликт, пиздец. Спасёт @Qualifier("имя_бина"), он уточнит, какой именно.
  • Область видимости (Scope). По умолчанию бин — синглтон (один на весь мир). Но можно сделать "prototype" (новый при каждом запросе), "request" (на время HTTP-запроса). Задаётся аннотацией @Scope.
  • В нормальном, недопетухом проекте используют гибрид: свои классы помечают @Component (или его производными), а для настройки всяких внешних библиотек — пишут @Configuration классы с @Bean. XML оставь для поддержки легаси-кода, которому, блядь, больше лет, чем тебе.