Глубокое погружение в мир аннотации Spring Boot @Conditional с проработанными примерами реализаций классов доступа к БД Mongo и MySQL. В моем посте «Почему Spring Boot?» было рассмотрено создание Spring Boot приложения, из которого вы едва ли сможете понять, что происходит за кулисами. Возможно, вы хотите понять магию автоконфигурации Spring Boot. Перед этим вы должны узнать о Spring аннотации @Conditional, от которой зависит вся магия автоконфигурации Spring Boot. При разработке приложений на основе Spring может возникнуть необходимость условной регистрации bean-компонентов. Например, вы можете зарегистрировать bean-компонент DataSource, указывающий на базу данных DEV при запуске приложения локально, и указывать на другую базу данных PRODUCTION при работе в рабочей среде. Вы можете перенести параметры подключения к базе данных в файл properties и использовать файл, соответствующий среде, но вам нужно изменить конфигурацию всякий раз, когда вам нужно указать другую среду и создать приложение. Для решения этой проблемы в Spring 3.1 была введена концепция Profiles (профиль). Вы можете зарегистрировать несколько бинов одного типа и связать их с одним или несколькими профилями. При запуске приложения вы можете активировать нужные профили и компоненты, связанные с активированными профилями, и только эти профили будут зарегистрированы. Затем вы можете указать активный профиль, используя System Property -Dspring.profiles.active = DEV. Этот подход работает для простых случаев, таких как включение или отключение регистрации компонентов на основе активированных профилей. Но если вы хотите зарегистрировать bean-компоненты, основанные на некоторой условной логике, тогда сам подход профилей недостаточен. Чтобы обеспечить большую гибкость условной регистрации bean-компонентов Spring, Spring 4 ввел концепцию @Conditional. Используя подход @Conditional, вы можете зарегистрировать компонент, условно основанный на любом произвольном условии. Например, вы можете зарегистрировать компонент, когда: Это всего лишь несколько примеров, и вы можете использовать любое условие, которое захотите. Давайте посмотрим, как работает Spring's @Conditional. Предположим, у нас есть интерфейс UserDAO с методами для получения данных из хранилища данных. У нас есть две реализации интерфейса UserDAO, а именно JdbcUserDAO, который общается с базой данных MySQL, и MongoUserDAO, который общается с MongoDB. Мы можем захотеть включить только один интерфейс JdbcUserDAO и MongoUserDAO на основе System Property, скажем, dbType. Если приложение запускается с использованием команды: java -jar myapp.jar -DdbType = MySQL, то мы хотим включить JdbcUserDAO. В противном случае, если приложение запускается с использованием команды: java -jar myapp.jar -DdbType = MONGO, мы хотим включить MongoUserDAO. Пусть реализации JdbcUserDAO и MongoUserDAO интерфейса UserDAO выглядят следующим образом: Мы можем реализовать Condition MySQLDatabaseTypeCondition, позволяющий проверить, что System Property dbType равно «MYSQL» следующим образом: Мы можем реализовать Condition MongoDBDatabaseTypeCondition, чтобы проверить, что System Property dbType равно «MONGODB» следующим образом: Теперь мы можем условно настроить bean-компоненты JdbcUserDAO и MongoUserDAO, используя @Conditional следующим образом: Если мы запустим приложение следующим образом: java -jar myapp.jar -DdbType = MYSQL, тогда будет зарегистрирован только bean-компонент JdbcUserDAO.  Но если вы установите System property: -DdbType = MONGODB, то будет зарегистрирован только bean-компонент MongoUserDAO. Теперь мы увидели, как условно зарегистрировать бин на основе System Property. Предположим, что мы хотим зарегистрировать bean-компонент MongoUserDAO только в том случае, если в classpath доступен Java класс MongoDB драйвера "com.mongodb.Server", если нет, мы хотим зарегистрировать bean-компонент JdbcUserDAO. Для этого мы можем реализовать Condition для проверки наличия или отсутствия класса драйвера MongoDB "com.mongodb.Server" следующим образом: Мы только что увидели, как зарегистрировать bean-компоненты условно на основании наличия или отсутствия класса в classpath. Что если мы хотим зарегистрировать bean-компонент MongoUserDAO, только если другой Spring-компонент типа UserDAO уже не зарегистрирован. Мы можем реализовать Condition, чтобы проверить, существует ли какой-либо bean-компонент определенного типа, следующим образом: Что если мы хотим зарегистрировать bean-компонент MongoUserDAO, только если в файле конфигурации установлено свойство app.dbType = MONGO? Мы можем реализовать интерфейс Condition следующим образом: Мы только что увидели различные варианты реализации интерфейса Condition. Но есть еще более элегантный способ реализовать Condition с использованием Аннотаций. Вместо создания реализации Condition для MYSQL и MongoDB мы можем создать аннотацию DatabaseType следующим образом: Затем мы можем реализовать класс DatabaseTypeCondition, чтобы использовать значение DatabaseType для определения включить или отключить регистрацию компонента следующим образом: Теперь мы можем использовать аннотацию @DatabaseType в наших определениях компонентов следующим образом: Здесь мы получаем метаданные из аннотации DatabaseType и проверяем значение dbType свойства системы, чтобы определить, включить или отключить регистрацию компонента. Мы увидели множество примеров, позволяющих понять, как мы можем зарегистрировать бины условно, используя аннотацию @Conditional. Spring Boot широко использует функцию @Conditional для регистрации bean-компонентов условно на основе различных критериев. Вы можете найти различные реализации интерфейса Condition, которые SpringBoot использует в классе org.springframework.boot.autoconfigure пакета spring-boot-autoconfigure-{version}.jar. Мы узнали как Spring Boot использует функцию @Conditional для условной проверки, регистрировать бин или нет. Но что именно запускает механизм автоконфигурации? Это то, что мы рассмотрим в следующем разделе. Ключ к волшебству автоконфигурации Spring Boot — аннотация @EnableAutoConfiguration. Обычно мы аннотируем наш класс, являющейся точкой входа в приложение, либо с помощью @SpringBootApplication, либо, если мы хотим настроить значения по умолчанию, мы можем использовать следующие аннотации: Аннотация @EnableAutoConfiguration включает автоматическую настройку Spring ApplicationContext путем сканирования компонентов пути к классам и регистрации бинов, соответствующих различным условиям. Spring Boot предоставляет различные классы AutoConfiguration в spring-boot-autoconfigure-{version}.jar, которые отвечают за регистрацию различных компонентов. Обычно классы AutoConfiguration аннотируются @Configuration, чтобы пометить его как класс конфигурации Spring, и аннотируются @EnableConfigurationProperties для привязки свойств настройки и одного или нескольких методов регистрации условного компонента. Например, рассмотрим класс org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration. Здесь DataSourceAutoConfiguration аннотируется @ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class}), что означает, что автоконфигурация bean-компонентов в DataSourceAutoConfiguration будет рассматриваться, только если классы DataSource.class и EmbeddedDatabaseType.class доступны в classpath. Класс также аннотируется @EnableConfigurationProperties(DataSourceProperties.class), который позволяет автоматически связывать свойства в application.properties со свойствами класса DataSourceProperties. При такой конфигурации все свойства, которые начинаются со spring.datasource.*, будут автоматически привязаны к объекту DataSourceProperties. Вы также можете увидеть некоторые внутренние классы и методы определения bean-компонентов, которые аннотируются условными аннотациями SpringBoot, такими как @ConditionalOnMissingBean, @ConditionalOnClass и @ConditionalOnProperty и т.д. Эти определения bean-компонентов будут зарегистрированы в ApplicationContext, только если эти условия будут выполнены. Вы также можете изучить многие другие классы автоконфигурации в spring-boot-autoconfigure-{version}.jar, такие как: Я надеюсь, что теперь у вас есть понимание того, как работает автоконфигурация Spring Boot используя различные классы автоконфигурации вместе с функциями @Conditional.