Статьи

Функция переключения в Spring Boot 2

Нравится вам это или нет, разработка программного обеспечения — это совместная деятельность. Интеграционная работа всегда демонизировалась и рассматривалась как необходимое зло. Есть несколько подходов, которые пытаются решить проблему эффективной интеграции. Переключатель функций принадлежит этой группе. В этой статье вы увидите на практике, как переключатели функций, также известные как флаги функций, могут использоваться в приложении Spring Boot.

1. Что такое функция переключения?

Проще говоря, переключатели Feature — это переменные, которые позволяют выполнять альтернативные пути в приложении на основе их текущих значений. Сохраняя различные сценарии выполнения, вы можете изменять поведение приложения без изменения кода.

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

Обычно вы читаете о флагах функций в качестве альтернативы ветвлению исходного кода функций , однако на практике оба метода могут использоваться вместе. Например, вы можете использовать ветви функций для разработки новых пользовательских историй в приложении, в то время как переключатели функций могут применяться для управления доступом к функциям в отдельных средах (например, клиенты с различными требованиями).

Несмотря на множество применений, переключатели функций также имеют свои недостатки. Самый большой из них — это сложность . Без правильной стратегии они могут быстро выйти из-под контроля и стать кошмаром обслуживания. К счастью, если вы следуете нескольким хорошим практикам и организуете приложение вокруг функций , работа с флагами функций должна быть намного проще.

2. Выбор бобов с функцией переключения

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

2.1 Абстракция зависимостей

Представьте, что у вас есть конечная точка сети, которая возвращает список продуктов, выбранных из хранилища базы данных. Ваша цель — создать переключатель функций, который позволит переключать реализацию репозитория на ту, которая использует веб-сервис в качестве источника данных.

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

Ниже приведен пример конечной точки REST продукта, который зависит от интерфейса ProductRepository .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@RestController
@RequestMapping("/products")
class ProductController {
 
   private final ProductRepository productRepository;
 
   ProductController(ProductRepository productRepository) {
       this.productRepository = productRepository;
   }
 
   @GetMapping
   Collection<Product> getAll() {
       return productRepository.findAll();
   }
 
}

На данный момент у нас есть только одна реализация интерфейса. Вскоре мы добавим еще один, который вы активируете с помощью функции переключения.

1
2
3
4
@Repository
class DbProductRepository implements ProductRepository {
    //...
}

2.2 Функция переключения в приложении. Свойства

Поскольку файл application.properties используется для настройки вашего приложения Spring Boot, это отличное место для установки вашего флажка переключения функций.

1
feature.toggles.productsFromWebService=true

Установите флаг в ложь перед фиксацией кода. Таким образом, по умолчанию у ваших товарищей по команде будет отключена новая функция. Если кто-то хочет активировать эту функцию, он может изменить значение флага на true в локальной среде разработки.

2.3 Создание условного компонента

Следующим шагом будет создание альтернативной реализации интерфейса, который вы хотите активировать с помощью функции переключения. Чтобы создать экземпляр компонента на основе значения созданного свойства, вы можете использовать аннотацию Spring Boot, которая называется @ConditionalOnProperty . Установите имя свойства переключателя и значение, которое должно его активировать. Значение должно быть таким же, как и в файле application.properties .

1
2
3
4
5
6
7
8
@Repository
@ConditionalOnProperty(
       name = "feature.toggles.productsFromWebService",
       havingValue = "true"
)
class WebServiceProductRepository implements ProductRepository {
    //...
}

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

1
2
3
4
5
6
7
@Repository
@ConditionalOnProperty(
       name = "feature.toggles.productsFromWebService",
       havingValue = "false",
       matchIfMissing = true
)
class DbProductRepository implements ProductRepository {

Мы используем ту же функцию, что и ранее, только ее значение изменилось. Установка свойства matchIfMissing не является обязательной. Таким образом, если вы удалите переключатель функции из файла application.properties , этот компонент будет создан, даже если значение отсутствует.

3. Как отключить контроллер с помощью функции переключения

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

1
2
3
4
5
6
@RestController
@RequestMapping("/coupons")
@ConditionalOnProperty(name = "feature.toggles.coupons", havingValue = "true")
class CouponController {
  //...
}

Application.properties должен содержать следующую строку.

1
feature.toggles.coupons=true

Если вы не установите значение в true, Spring не будет создавать экземпляр контроллера. Клиент просто получит код состояния 404 HTTP.

К сожалению, аннотация @ConditionalOnProperty не может использоваться для одного метода @RequestMapping . В качестве обходного пути вы можете переместить желаемое отображение в отдельный компонент контроллера. В качестве альтернативы можно просто ввести значение переключателя функции и создать оператор if в теле метода отображения. Тем не менее, вы должны использовать это решение с осторожностью. Если вам интересно, почему вы найдете ответ в следующем абзаце.

01
02
03
04
05
06
07
08
09
10
11
12
13
private final boolean couponsToggled;
 
CouponController(@Value("${feature.toggles.coupons}") boolean couponsToggled) {
   this.couponsToggled = couponsToggled;
}
 
@GetMapping
List<String> listCouponNames() {
   if (!couponsToggled) {
       throw new NotSupportedException();
   }
   //...
}

4. Многофункциональное управление переключателем

Как вы можете прочитать о переключателях функций в bliki Мартина Фаулера , флаги функций имеют тенденцию распространяться по всей базе кода и могут быстро стать неуправляемыми . Даже если в вашем приложении имеется всего несколько переключателей функций, лучше абстрагировать хранилище ваших флагов от точек принятия решений, в которых они используются.

4.1 Избегание связи между флагами функций

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

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

4.2 Извлечение решения о переключении функций в Spring Boot

Если у вас есть отдельный компонент для переключения функций, вы можете легко добавить все флаги из файла application.properties с помощью аннотации @ConfigurationProperties . Здесь вы можете увидеть пример реализации:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Component
@Component
@ConfigurationProperties("feature")
public class FeatureDecisions {
 
   private Map<String, Boolean> toggles = new HashMap<>();
 
   public Map<String, Boolean> getToggles() {
       return toggles;
   }
 
   public boolean couponEnabled() {
       return toggles.getOrDefault("coupons", false);
   }
 
}

Класс выше возьмет все свойства, которые начинаются с feature.toggles, и поместит их в карту переключателей . Как видите, есть метод couponEnabled (), который вы можете использовать, чтобы абстрагировать точку принятия решения от логики, стоящей за этим решением.

Кроме того, вам также понадобится дополнительная зависимость, чтобы включить обработку для @ConfigurationProperties .

1
2
3
4
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

5. Конечная точка привода для переключения функций

Поскольку у вас уже есть все переключатели функций в одном месте, все, что вам нужно сделать сейчас, это представить список с помощью пользовательской конечной точки привода. Следующий пример покажет вам, как это сделать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Component
@Endpoint(id = "feature-toggles")
class FeatureToggleInfoEndpoint {
 
   private final FeatureDecisions featureDecisions;
 
   FeatureToggleInfoEndpoint(FeatureDecisions featureDecisions) {
       this.featureDecisions = featureDecisions;
   }
 
   @ReadOperation
   public Map<String, Boolean> featureToggles() {
       return featureDecisions.getToggles();
   }
 
}

Если вы работаете со стандартной установкой Spring Boot 2 Actuator, конечная точка не будет открыта через HTTP . Чтобы протестировать его в своем браузере, необходимо включить конечную точку привода, добавив ее идентификатор в веб-фильтр включения в файле application.properties .

1
management.endpoints.web.exposure.include=health,info,feature-toggles

После запуска приложения перейдите по адресу http: // localhost: 8080 / actator / feature-toggles, чтобы увидеть результаты, возвращаемые конечной точкой:

В зависимости от ваших потребностей, вы также можете реализовать возможность переключения функций во время выполнения, используя @WriteOperation на созданной конечной точке. Этот пример охватывает только часть вывода.

Вывод

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

Вы можете найти рабочий пример приложения в репозитории Github . Если вам понравился пост и вы нашли его полезным, поделитесь им со своими подписчиками. Я также с нетерпением жду ваших вопросов и комментариев ниже статьи.

Смотреть оригинальную статью здесь: функция переключения в Spring Boot 2

Мнения, высказанные участниками Java Code Geeks, являются их собственными.