Приложения Integration Testing Spring обязуются создавать небольшие выделенные фрагменты конфигурации и собирать их либо во время обычного запуска приложения, либо во время тестов. Даже в последнем случае разные фрагменты могут быть собраны в разных тестах.
Тем не менее, эта практика не обрабатывает сценарий использования, где я хочу использовать приложение в двух разных средах. В качестве примера я мог бы использовать источник данных JNDI в развернутых средах и прямое соединение при разработке на моей локальной машине. Сборка различных комбинаций фрагментов невозможна, так как я хочу запускать приложение в обоих случаях, а не тестировать его.
Мое единственное требование состоит в том, чтобы по умолчанию использовался источник данных JNDI, а при активации флага — профиля должен переключаться на прямое соединение. В этом случае рефлекс Павлова должен был добавить простое условие в @Configuration классе.
@Configuration
public class MyConfiguration {
@Autowired
private Environment env;
@Bean
public DataSource dataSource() throws Exception {
if (env.acceptsProfiles("dev")) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:file:~/conditional");
dataSource.setUsername("sa");
return dataSource;
}
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("java:comp/env/jdbc/conditional");
}
}
Начало использования таких операторов управления потоком — это начало конца, так как в будущем это приведет к добавлению операторов управления потоком, что, в свою очередь, приведет к запутанному беспорядку конфигурации спагетти и, в конечном итоге, к не поддерживаемому приложению.
Spring Boot предлагает хорошую альтернативу для обработки этого варианта использования с различными вариантами @ConditionalXXX аннотаций. Их использование имеет следующие преимущества при выполнении работы: простота использования, удобочитаемость и ограниченность. Хотя последний момент может показаться недостатком, это самый большой актив IMHO (в отличие от плагинов Maven). Код является мощным, и с большой властью должна прийти большая ответственность, что вряд ли возможно в ходе проекта с крайними сроками и давлением со стороны руководства. Это основная причина, по которой один из моих коллег выступает за XML вместо JavaConfig: с XML вы уверены, что не будет никаких злоупотреблений, пока проект работает.
Но давайте остановимся на философии и вернемся к @ConditionalXXX аннотациям. По сути, добавление такой аннотации к @Bean методу вызовет этот метод и поместит компонент в фабрику на основе специального условия. Их много, вот несколько важных:
- Зависит от версии Java, новее или старше —
@ConditionalOnJava - Зависит от бобов, присутствующих на фабрике
@ConditionalOnBean, и наоборот, зависит от имени бобов, которых нет.@ConditionalOnMissingBean - Зависит от класса, присутствующего на пути к классам
@ConditionalOnClass, и его противоположности@ConditionalOnMissingClass - Будь то веб-приложение или нет —
@ConditionalOnWebApplicationи@ConditionalOnNotWebApplication - и т.п.
Обратите внимание, что весь список существующих условий можно просмотреть в пакете org.springframework.boot.autoconfigure.condition в Spring Boot .
С помощью этой информации мы можем перенести приведенный выше фрагмент в более надежную реализацию:
@Configuration
public class MyConfiguration {
@Bean
@Profile("dev")
public DataSource dataSource() throws Exception {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:file:~/localisatordb");
dataSource.setUsername("sa");
return dataSource;
}
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource fakeDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("java:comp/env/jdbc/conditional");
}
}
Конфигурация теперь аккуратно разделена на два разных метода, первый метод будет вызываться только тогда, когда dev профиль активен, а второй будет, когда первый метод не вызывается, следовательно, когда dev профиль не активен.
Наконец, лучшее, что есть в этой функции, это то, что она легко расширяема, так как зависит только от @Conditional аннотации и Condition интерфейса (которые являются частью самой Spring, а не Spring Boot).
Вот простой пример в формате Maven / IntelliJ, с которым вы можете поиграть. Повеселись!
