1. Introducción
Spring Boot tiene muchas características útiles, incluida la configuración externa y el fácil acceso a las propiedades definidas en los archivos de propiedades . Un tutorial anterior describía varias formas en las que se podía hacer esto.
Ahora vamos a explorar la anotación @ConfigurationProperties con mayor detalle.
2. Configuración
Este tutorial utiliza una configuración bastante estándar. Comenzamos agregando spring-boot-starter-parent como padre en nuestro pom.xml :
org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE
Para poder validar las propiedades definidas en el archivo, también necesitamos una implementación de JSR-303, y hibernate-validator es uno de ellos.
Vamos a agregarlo también a nuestro pom.xml :
org.hibernate hibernate-validator 6.0.16.Final
La página "Introducción a Hibernate Validator" tiene más detalles.
3. Propiedades simples
La documentación oficial advierte que aislemos las propiedades de configuración en POJO separados .
Así que comencemos por hacer eso:
@Configuration @ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters }
Usamos @Configuration para que Spring cree un bean Spring en el contexto de la aplicación.
@ConfigurationProperties funciona mejor con propiedades jerárquicas que tienen el mismo prefijo; por lo tanto, agregamos un prefijo de correo .
El marco de Spring usa establecedores de beans de Java estándar, por lo que debemos declarar establecedores para cada una de las propiedades.
Nota: Si no usamos @Configuration en el POJO, entonces necesitamos agregar @EnableConfigurationProperties (ConfigProperties.class) en la clase principal de la aplicación Spring para vincular las propiedades al POJO:
@SpringBootApplication @EnableConfigurationProperties(ConfigProperties.class) public class EnableConfigurationDemoApplication { public static void main(String[] args) { SpringApplication.run(EnableConfigurationDemoApplication.class, args); } }
¡Eso es! Spring vinculará automáticamente cualquier propiedad definida en nuestro archivo de propiedades que tenga el prefijo mail y el mismo nombre que uno de los campos de la clase ConfigProperties .
Spring usa algunas reglas relajadas para las propiedades vinculantes. Como resultado, las siguientes variaciones están vinculadas a la propiedad hostName :
mail.hostName mail.hostname mail.host_name mail.host-name mail.HOST_NAME
Por lo tanto, podemos usar el siguiente archivo de propiedades para configurar todos los campos:
#Simple properties [email protected] mail.port=9000 [email protected]
3.1. Spring Boot 2.2
A partir de Spring Boot 2.2, Spring encuentra y registra las clases @ConfigurationProperties mediante el escaneo de classpath . Por lo tanto, no es necesario anotar tales clases con @Component (y otras meta-anotaciones como @Configuration), o incluso usar @EnableConfigurationProperties:
@ConfigurationProperties(prefix = "mail") public class ConfigProperties { private String hostName; private int port; private String from; // standard getters and setters }
El escáner classpath habilitado por @SpringBootApplication encuentra la clase ConfigProperties , aunque no anotamos esta clase con @Component.
Además, podemos usar la anotación @ConfigurationPropertiesScan para escanear ubicaciones personalizadas para las clases de propiedades de configuración:
@SpringBootApplication @ConfigurationPropertiesScan("com.baeldung.configurationproperties") public class EnableConfigurationDemoApplication { public static void main(String[] args) { SpringApplication.run(EnableConfigurationDemoApplication.class, args); } }
De esta manera, Spring buscará clases de propiedades de configuración solo en el paquete com.baeldung.properties .
4. Propiedades anidadas
Podemos tener propiedades anidadas en Listas, Mapas y Clases.
Creemos una nueva clase de Credenciales para usar con algunas propiedades anidadas:
public class Credentials { private String authMethod; private String username; private String password; // standard getters and setters }
También necesitamos actualizar la clase ConfigProperties para usar una lista, un mapa y la clase Credentials :
public class ConfigProperties { private String host; private int port; private String from; private List defaultRecipients; private Map additionalHeaders; private Credentials credentials; // standard getters and setters }
El siguiente archivo de propiedades establecerá todos los campos:
#Simple properties [email protected] mail.port=9000 [email protected] #List properties mail.defaultRecipients[0][email protected] mail.defaultRecipients[1][email protected] #Map Properties mail.additionalHeaders.redelivery=true mail.additionalHeaders.secure=true #Object properties mail.credentials.username=john mail.credentials.password=password mail.credentials.authMethod=SHA1
5. Uso de @ConfigurationProperties en un método @Bean
También podemos utilizar el @ConfigurationProperties anotación en @Bean métodos -annotated.
Este enfoque puede ser particularmente útil cuando queremos vincular propiedades a un componente de terceros que está fuera de nuestro control.
Creemos una clase de elemento simple que usaremos en el siguiente ejemplo:
public class Item { private String name; private int size; // standard getters and setters }
Ahora veamos cómo podemos usar @ConfigurationProperties en un método @Bean para vincular propiedades externalizadas a la instancia de Item :
@Configuration public class ConfigProperties { @Bean @ConfigurationProperties(prefix = "item") public Item item() { return new Item(); } }
En consecuencia, cualquier propiedad con prefijo de elemento se asignará a la instancia de Elemento administrada por el contexto de Spring.
6. Validación de propiedad
@ConfigurationProperties provides validation of properties using the JSR-303 format. This allows all sorts of neat things.
For example, let's make the hostName property mandatory:
@NotBlank private String hostName;
Next, let's make the authMethod property from 1 to 4 characters long:
@Length(max = 4, min = 1) private String authMethod;
Then the port property from 1025 to 65536:
@Min(1025) @Max(65536) private int port;
Finally, the from property must match an email address format:
@Pattern(regexp = "^[a-z0-9._%+-][email protected][a-z0-9.-]+\\.[a-z]{2,6}$") private String from;
This helps us reduce a lot of if – else conditions in our code, and makes it look much cleaner and more concise.
If any of these validations fail, then the main application would fail to start with an IllegalStateException.
The Hibernate Validation framework uses standard Java bean getters and setters, so it's important that we declare getters and setters for each of the properties.
7. Property Conversion
@ConfigurationProperties supports conversion for multiple types of binding the properties to their corresponding beans.
7.1. Duration
We'll start by looking at converting properties into Duration objects.
Here we have two fields of type Duration:
@ConfigurationProperties(prefix = "conversion") public class PropertyConversion { private Duration timeInDefaultUnit; private Duration timeInNano; ... }
This is our properties file:
conversion.timeInDefaultUnit=10 conversion.timeInNano=9ns
As a result, the field timeInDefaultUnit will have a value of 10 milliseconds, and timeInNano will have a value of 9 nanoseconds.
The supported units are ns, us, ms, s, m, h and d for nanoseconds, microseconds, milliseconds, seconds, minutes, hours, and days, respectively.
The default unit is milliseconds, which means if we don't specify a unit next to the numeric value, Spring will convert the value to milliseconds.
We can also override the default unit using @DurationUnit:
@DurationUnit(ChronoUnit.DAYS) private Duration timeInDays;
This is the corresponding property:
conversion.timeInDays=2
7.2. DataSize
Similarly, Spring Boot @ConfigurationProperties supports DataSize type conversion.
Let's add three fields of type DataSize:
private DataSize sizeInDefaultUnit; private DataSize sizeInGB; @DataSizeUnit(DataUnit.TERABYTES) private DataSize sizeInTB;
These are the corresponding properties:
conversion.sizeInDefaultUnit=300 conversion.sizeInGB=2GB conversion.sizeInTB=4
In this case, the sizeInDefaultUnit value will be 300 bytes, as the default unit is bytes.
The supported units are B, KB, MB, GB, and TB. We can also override the default unit using @DataSizeUnit.
7.3. Custom Converter
We can also add our own custom Converter to support converting a property to a specific class type.
Let's add a simple class Employee:
public class Employee { private String name; private double salary; }
Then we'll create a custom converter to convert this property:
conversion.employee=john,2000
We will convert it to a file of type Employee:
private Employee employee;
We will need to implement the Converter interface, then use @ConfigurationPropertiesBinding annotation to register our custom Converter:
@Component @ConfigurationPropertiesBinding public class EmployeeConverter implements Converter { @Override public Employee convert(String from) { String[] data = from.split(","); return new Employee(data[0], Double.parseDouble(data[1])); } }
8. Immutable @ConfigurationProperties Binding
As of Spring Boot 2.2, we can use the @ConstructorBinding annotation to bind our configuration properties.
This essentially means that @ConfigurationProperties-annotated classes may now be immutable.
@ConfigurationProperties(prefix = "mail.credentials") @ConstructorBinding public class ImmutableCredentials { private final String authMethod; private final String username; private final String password; public ImmutableCredentials(String authMethod, String username, String password) { this.authMethod = authMethod; this.username = username; this.password = password; } public String getAuthMethod() { return authMethod; } public String getUsername() { return username; } public String getPassword() { return password; } }
As we can see, when using @ConstructorBinding, we need to provide the constructor with all the parameters we'd like to bind.
Tenga en cuenta que todos los campos de ImmutableCredentials son finales. Además, no existen métodos de establecimiento.
Además, es importante enfatizar que para usar el enlace del constructor, necesitamos habilitar explícitamente nuestra clase de configuración con @EnableConfigurationProperties o con @ConfigurationPropertiesScan .
9. Conclusión
En este artículo, exploramos la anotación @ConfigurationProperties y destacamos algunas de las funciones útiles que proporciona, como la vinculación relajada y la validación de Bean.
Como de costumbre, el código está disponible en Github.