Настройка приложения с помощью
В статье
![image](https://habrastorage.org/r/w780q1/webt/mo/zr/p5/mozrp5nbh46i7pj74eb_vcvtegi.jpeg)
Про отличия между двумя подходами сказано здесь — ConfigurationProperties vs. Value
На картинке выше основной состав и принцип работы. Доступные компоненты системы, это Spring компоненты, просто классы, различные константы, переменные и пр. можно указать в файле application.properties, при этом еще на момент указания средой разработки будут предложены варианты, сделаны проверки. При старте приложения указанные значения будут проверенны на соответствие типа, ограничениям и если все удовлетворяет, то будет выполнен старт приложения. Например очень удобно настраивать функционал приложения из списка доступных Spring компонент, ниже покажу как.
Для создания настройки приложения с использованием ConfigurationProperties, можно начать с класса свойств. В нем собственно указаны свойства, компоненты системы которые хотим настраивать.
В классе prefix=«demo» будет использоваться в application.properties, как префикс к свойству.
Тут я объявил два spring бина
В классе AppProperties я указал ссылку на некоторый доступный сервис приложения, его я буду менять в application.properties, у меня будет две его реализации и я буду подключать одну из них в application.properties.
Вот их реализация
Вот этого уже теперь достаточно что бы начать настраивать application.properties. Но всякий раз когда вносятся изменения в класс с ConfigurationProperties, надо сделать rebuild проекта, после чего в проекте появится файл
При попытке ввести неверный тип IDE сообщит
![image](https://habrastorage.org/r/w1560/webt/ak/-5/je/ak-5jed8lmxyx_kntpgdkxfwpq0.png)
Даже если оставить как есть и попытаться стартовать приложение, то будет вполне внятная ошибка
![image](https://habrastorage.org/r/w1560/webt/dd/dd/eh/ddddehnvr34gb6yqnqz4e_atyfk.png)
Дополнительные метаданные, это только для удобства работы с application.properties в IDE, если это не надо, можно не делать. Для этого есть возможность указать в дополнительном файле подсказки (hints) и др. информацию для среды. Для этого скопирую созданный файл spring-configuration-metadata.json в
В ней можно будет перечислить например допустимые значения для demo.vehicle
В поле «name» указано св-во «demo.vehicle», а в «values» список допустимых значений. Теперь если сделать rebuild проекта и перейти в файл application.properties, то при вводе demo.vehicle получу список допустимых значений
![image](https://habrastorage.org/r/w1560/webt/vj/mn/xt/vjmnxtghgzaqivoncujmyo4waya.png)
При вводе отличного от предложенного, но того же типа, редактор подсветит, но приложение в этом случае будет стартовать, так как это не строгое ограничение, а всего лишь подсказка.
![image](https://habrastorage.org/r/w1560/webt/ut/we/pd/utwepdo1srvrqay1_0tnydppzek.png)
Ранее в проекте я объявил два сервиса MyDemoService1 и MyDemoService2 оба они имплементируют интерфейс DemoService, теперь можно настроить чтобы application.properties были доступны только сервисы имплементирующие этот интерфейс и соответственно в AppProperties классе инициализировался выбранный. Для этого есть Providers их можно указать в additional-spring-configuration-metadata. Провайдеры есть нескольких типов их можно посмотреть в документации, я покажу пример для одного, —
Пример Providers для DemoService:
После чего в application.properties для параметра demo.service будет доступен выбор двух сервисов, можно посмотреть их описание (description из определения).
![image](https://habrastorage.org/r/w1560/webt/g-/yu/a_/g-yua_aivu8lqdolzl79wklsvfe.png)
Теперь удобно выбирать нужный сервис, менять функционал приложения. Есть один момент для объектных настроек, Spring надо немного помочь конвертировать строку которая указана в настройке, в объект. Для этого делается небольшой класс наследник от Converter.
На диаграмме классов проекта видно как эти сервисы отделены от основного приложения и доступны через AppProperties.
![image](https://habrastorage.org/r/w1560/webt/_v/j6/pu/_vj6pu9u1f0muedhbeofasoephy.png)
Вывод в консоли
![image](https://habrastorage.org/r/w1560/webt/79/9-/jv/799-jvf9960khsdtgueano7qpyk.png)
Структура проекта
![image](https://habrastorage.org/r/w1560/webt/b-/cj/sk/b-cjskq5ho3coflko0u6hhx-plu.png)
Полный файл additional-spring-configuration-metadata.json
Материалы Configuration Metadata
@ConfigurationProperties
, как альтернатива использованию @Value
. В статье
- Настройка и изменение функционала приложения через application.properties с использованием ConfigurationProperties
- Интеграция application.properties с IDE
- Проверка значений настроек
![image](https://habrastorage.org/webt/mo/zr/p5/mozrp5nbh46i7pj74eb_vcvtegi.jpeg)
Про отличия между двумя подходами сказано здесь — ConfigurationProperties vs. Value
На картинке выше основной состав и принцип работы. Доступные компоненты системы, это Spring компоненты, просто классы, различные константы, переменные и пр. можно указать в файле application.properties, при этом еще на момент указания средой разработки будут предложены варианты, сделаны проверки. При старте приложения указанные значения будут проверенны на соответствие типа, ограничениям и если все удовлетворяет, то будет выполнен старт приложения. Например очень удобно настраивать функционал приложения из списка доступных Spring компонент, ниже покажу как.
Класс свойств
Для создания настройки приложения с использованием ConfigurationProperties, можно начать с класса свойств. В нем собственно указаны свойства, компоненты системы которые хотим настраивать.
AppProperties.java
@ConfigurationProperties(prefix = "demo")
@Validated
public class AppProperties {
private String vehicle;
@Max(value = 999, message = "Value 'Property' should not be greater than 999")
private Integer value;
private Map<String,Integer> contexts;
private StrategyEnum strategyEnum;
private Resource resource;
private DemoService service;
public String getVehicle() {
return vehicle;
}
public void setVehicle(String vehicle) {
this.vehicle = vehicle;
}
public Map<String, Integer> getContexts() {
return contexts;
}
public void setContexts(Map<String, Integer> contexts) {
this.contexts = contexts;
}
public StrategyEnum getStrategyEnum() {
return strategyEnum;
}
public void setStrategyEnum(StrategyEnum strategyEnum) {
this.strategyEnum = strategyEnum;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public DemoService getService() {
return service;
}
public void setService(DemoService service) {
this.service = service;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
@Override
public String toString() {
return "MyAppProperties{" +
"\nvehicle=" + vehicle +
"\n,contexts=" + contexts +
"\n,service=" + service +
"\n,value=" + value +
"\n,strategyEnum=" + strategyEnum +
'}';
}
}
В классе prefix=«demo» будет использоваться в application.properties, как префикс к свойству.
Класс приложения SpringApplication и pom.xml проекта
@SpringBootApplication
@EnableConfigurationProperties({AppProperties.class})
@ImportResource(value= "classpath:context.xml")
public class DemoConfigProcessorApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoConfigProcessorApplication.class, args);
AppProperties properties = context.getBean(AppProperties.class);
String perform = properties.getService().perform(properties.getVehicle());
System.out.println("perform: " + perform);
System.out.println(properties.toString());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demoConfigProcessor</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demoConfigProcessor</name>
<description>Demo project for Spring Boot Configuration Processor</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Тут я объявил два spring бина
Spring контекст (context.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="service1" class="com.example.demoConfigProcessor.MyDemoService1">
<description>Description MyDemoService 1</description>
</bean>
<bean id="service2" class="com.example.demoConfigProcessor.MyDemoService2">
<description>Description MyDemoService 2</description>
</bean>
</beans>
В классе AppProperties я указал ссылку на некоторый доступный сервис приложения, его я буду менять в application.properties, у меня будет две его реализации и я буду подключать одну из них в application.properties.
![image](https://habrastorage.org/webt/ow/mi/xp/owmixpdglgevstznek9hwrh8g68.png)
Вот их реализация
DemoService
public interface DemoService {
String perform(String value);
}
public class MyDemoService1 implements DemoService {
@Override
public String perform(String value) {
return "Service №1: perform routine maintenance work on <" + value +">";
}
}
public class MyDemoService2 implements DemoService {
@Override
public String perform(String value) {
return "Service №2: perform routine maintenance work on <" + value +">";
}
}
Вот этого уже теперь достаточно что бы начать настраивать application.properties. Но всякий раз когда вносятся изменения в класс с ConfigurationProperties, надо сделать rebuild проекта, после чего в проекте появится файл
\target\classes\META-INF\spring-configuration-metadata.json
. Собственно его IDE использует для редактирования в файле application.properties. Его структуру я укажу в ссылке в материалах. Этот файл будет создан на основе класса AppProperties. Если теперь открыть файл application.properties и начать вводить «demo», то среда начнет показывать доступные свойства![image](https://habrastorage.org/webt/eu/5u/vi/eu5uvi7ryx0tpjg22fxbd1xr0cy.png)
При попытке ввести неверный тип IDE сообщит
![image](https://habrastorage.org/webt/ak/-5/je/ak-5jed8lmxyx_kntpgdkxfwpq0.png)
Даже если оставить как есть и попытаться стартовать приложение, то будет вполне внятная ошибка
![image](https://habrastorage.org/webt/dd/dd/eh/ddddehnvr34gb6yqnqz4e_atyfk.png)
Добавление дополнительных метаданных
Дополнительные метаданные, это только для удобства работы с application.properties в IDE, если это не надо, можно не делать. Для этого есть возможность указать в дополнительном файле подсказки (hints) и др. информацию для среды. Для этого скопирую созданный файл spring-configuration-metadata.json в
\src\main\resources\META-INF\
и переименую его в additional-spring-configuration-metadata.json
. В этом файле меня будет интересовать только секция «hints»: [] В ней можно будет перечислить например допустимые значения для demo.vehicle
"hints": [
{
"name": "demo.vehicle",
"values": [
{
"value": "car make A",
"description": "Car brand A is allowed."
},
{
"value": "car make B",
"description": "Car brand B is allowed."
}
]
}]
В поле «name» указано св-во «demo.vehicle», а в «values» список допустимых значений. Теперь если сделать rebuild проекта и перейти в файл application.properties, то при вводе demo.vehicle получу список допустимых значений
![image](https://habrastorage.org/webt/vj/mn/xt/vjmnxtghgzaqivoncujmyo4waya.png)
При вводе отличного от предложенного, но того же типа, редактор подсветит, но приложение в этом случае будет стартовать, так как это не строгое ограничение, а всего лишь подсказка.
![image](https://habrastorage.org/webt/ut/we/pd/utwepdo1srvrqay1_0tnydppzek.png)
Ранее в проекте я объявил два сервиса MyDemoService1 и MyDemoService2 оба они имплементируют интерфейс DemoService, теперь можно настроить чтобы application.properties были доступны только сервисы имплементирующие этот интерфейс и соответственно в AppProperties классе инициализировался выбранный. Для этого есть Providers их можно указать в additional-spring-configuration-metadata. Провайдеры есть нескольких типов их можно посмотреть в документации, я покажу пример для одного, —
spring-bean-reference
. Этот тип показывает имена доступных bean-компонентов в текущем проекте. Список ограничивается базовым классом или интерфейсом. Пример Providers для DemoService:
"hints": [
{
"name": "demo.service",
"providers": [
{
"name": "spring-bean-reference",
"parameters": {
"target": "com.example.demoConfigProcessor.DemoService"
}
}
]
}
]
После чего в application.properties для параметра demo.service будет доступен выбор двух сервисов, можно посмотреть их описание (description из определения).
![image](https://habrastorage.org/webt/g-/yu/a_/g-yua_aivu8lqdolzl79wklsvfe.png)
Теперь удобно выбирать нужный сервис, менять функционал приложения. Есть один момент для объектных настроек, Spring надо немного помочь конвертировать строку которая указана в настройке, в объект. Для этого делается небольшой класс наследник от Converter.
ServiceConverter
@Component
@ConfigurationPropertiesBinding
public class ServiceConverter implements Converter<String, DemoService> {
@Autowired
private ApplicationContext applicationContext;
@Override
public DemoService convert(String source) {
return (DemoService) applicationContext.getBean(source);
}
}
На диаграмме классов проекта видно как эти сервисы отделены от основного приложения и доступны через AppProperties.
![image](https://habrastorage.org/webt/_v/j6/pu/_vj6pu9u1f0muedhbeofasoephy.png)
Validation propertyК полям класса AppProperties можно добавить проверки доступные в рамках JSR 303. Про это я писал здесь. Получится проверяемый, удобный файл конфигурации приложения.
Вывод в консоли
![image](https://habrastorage.org/webt/79/9-/jv/799-jvf9960khsdtgueano7qpyk.png)
Структура проекта
![image](https://habrastorage.org/webt/b-/cj/sk/b-cjskq5ho3coflko0u6hhx-plu.png)
Полный файл additional-spring-configuration-metadata.json
additional-spring-configuration-metadata
{
"groups": [
{
"name": "demo",
"type": "com.example.demoConfigProcessor.AppProperties",
"sourceType": "com.example.demoConfigProcessor.AppProperties"
}
],
"properties": [
{
"name": "demo.contexts",
"type": "java.util.Map<java.lang.String,java.lang.Integer>",
"sourceType": "com.example.demoConfigProcessor.AppProperties"
},
{
"name": "demo.vehicle",
"type": "java.lang.String",
"sourceType": "com.example.demoConfigProcessor.AppProperties"
}
],
"hints": [
{
"name": "demo.vehicle",
"values": [
{
"value": "car make A",
"description": "Car brand A is allowed."
},
{
"value": "car make B",
"description": "Car brand B is allowed."
}
]
},
{
"name": "demo.service",
"providers": [
{
"name": "spring-bean-reference",
"parameters": {
"target": "com.example.demoConfigProcessor.DemoService"
}
}
]
}
]
}
Материалы Configuration Metadata