Créer une configuration automatique personnalisée avec Spring Boot

1. Vue d'ensemble

En termes simples, la configuration automatique de Spring Boot représente un moyen de configurer automatiquement une application Spring en fonction des dépendances présentes sur le chemin de classe.

Cela peut rendre le développement plus rapide et plus facile en éliminant le besoin de définir certains beans inclus dans les classes de configuration automatique.

Dans la section suivante, nous allons jeter un œil à la création de notre configuration automatique Spring Boot personnalisée .

2. Dépendances de Maven

Commençons par les dépendances dont nous avons besoin:

 org.springframework.boot spring-boot-starter-data-jpa 2.2.2.RELEASE   mysql mysql-connector-java 8.0.19 

Les dernières versions de spring-boot-starter-data-jpa et mysql-connector-java peuvent être téléchargées depuis Maven Central.

3. Création d'une configuration automatique personnalisée

Pour créer une configuration automatique personnalisée, nous devons créer une classe annotée en tant que @Configuration et l'enregistrer.

Créons une configuration personnalisée pour une source de données MySQL :

@Configuration public class MySQLAutoconfiguration { //... }

La prochaine étape obligatoire consiste à enregistrer la classe en tant que candidat à la configuration automatique, en ajoutant le nom de la classe sous la clé org.springframework.boot.autoconfigure.EnableAutoConfiguration dans le fichier standard resources / META-INF / spring.factories :

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.baeldung.autoconfiguration.MySQLAutoconfiguration

Si nous voulons que notre classe de configuration automatique ait la priorité sur les autres candidats à la configuration automatique, nous pouvons ajouter l' annotation @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE) .

La configuration automatique est conçue à l'aide de classes et de beans marqués d' annotations @Conditionnelles afin que la configuration automatique ou des parties spécifiques de celle-ci puissent être remplacées.

Notez que la configuration automatique n'est effective que si les beans auto-configurés ne sont pas définis dans l'application. Si vous définissez votre bean, celui par défaut sera remplacé.

3.1. Conditions de classe

Les conditions de classe nous permettent de spécifier qu'un bean de configuration sera inclus si une classe spécifiée est présente à l'aide de l' annotation @ConditionalOnClass , ou si une classe est absente à l'aide de l' annotation @ConditionalOnMissingClass .

Précisons que notre MySQLConfiguration ne sera chargée que si la classe DataSource est présente, auquel cas nous pouvons supposer que l'application utilisera une base de données:

@Configuration @ConditionalOnClass(DataSource.class) public class MySQLAutoconfiguration { //... }

3.2. Conditions du haricot

Si nous voulons inclure un bean uniquement si un bean spécifié est présent ou non , nous pouvons utiliser les annotations @ConditionalOnBean et @ConditionalOnMissingBean .

Pour illustrer cela, ajoutons un bean entityManagerFactory à notre classe de configuration, et spécifions que nous voulons que ce bean soit créé uniquement si un bean appelé dataSource est présent et si un bean appelé entityManagerFactory n'est pas déjà défini:

@Bean @ConditionalOnBean(name = "dataSource") @ConditionalOnMissingBean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan("com.baeldung.autoconfiguration.example"); em.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); if (additionalProperties() != null) { em.setJpaProperties(additionalProperties()); } return em; }

Configurons également un bean transactionManager qui ne sera chargé que si un bean de type JpaTransactionManager n'est pas déjà défini:

@Bean @ConditionalOnMissingBean(type = "JpaTransactionManager") JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory); return transactionManager; }

3.3. Conditions de propriété

L' annotation @ConditionalOnProperty est utilisée pour spécifier si une configuration sera chargée en fonction de la présence et de la valeur d'une propriété Spring Environment .

Tout d'abord, ajoutons un fichier source de propriété pour notre configuration qui déterminera où les propriétés seront lues:

@PropertySource("classpath:mysql.properties") public class MySQLAutoconfiguration { //... }

Nous pouvons configurer le bean DataSource principal qui sera utilisé pour créer des connexions à la base de données de manière à ce qu'il ne soit chargé que si une propriété appelée usemysql est présente.

Nous pouvons utiliser l'attribut hadValue pour spécifier certaines valeurs de la propriété usemysql à mettre en correspondance.

Définissons le bean dataSource avec des valeurs par défaut qui se connectent à une base de données locale appelée myDb si la propriété usemysql est définie sur local :

@Bean @ConditionalOnProperty( name = "usemysql", havingValue = "local") @ConditionalOnMissingBean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true"); dataSource.setUsername("mysqluser"); dataSource.setPassword("mysqlpass"); return dataSource; }

Si la propriété usemysql est définie sur custom, le bean dataSource sera configuré à l'aide des valeurs de propriétés personnalisées pour l'URL de la base de données, l'utilisateur et le mot de passe:

@Bean(name = "dataSource") @ConditionalOnProperty( name = "usemysql", havingValue = "custom") @ConditionalOnMissingBean public DataSource dataSource2() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl(env.getProperty("mysql.url")); dataSource.setUsername(env.getProperty("mysql.user") != null ? env.getProperty("mysql.user") : ""); dataSource.setPassword(env.getProperty("mysql.pass") != null ? env.getProperty("mysql.pass") : ""); return dataSource; }

Le fichier mysql.properties contiendra la propriété usemysql :

usemysql=local

Si une application qui utilise MySQLAutoconfiguration souhaite remplacer les propriétés par défaut, il lui suffit d'ajouter des valeurs différentes pour les propriétés mysql.url , mysql.user et mysql.pass et la ligne usemysql = custom dans le fichier mysql.properties .

3.4. Conditions des ressources

L'ajout de l' annotation @ConditionalOnResource signifie que la configuration ne sera chargée que lorsqu'une ressource spécifiée est présente .

Let's define a method called additionalProperties() that will return a Properties object containing Hibernate-specific properties to be used by the entityManagerFactory bean, only if the resource file mysql.properties is present:

@ConditionalOnResource( resources = "classpath:mysql.properties") @Conditional(HibernateCondition.class) Properties additionalProperties() { Properties hibernateProperties = new Properties(); hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("mysql-hibernate.hbm2ddl.auto")); hibernateProperties.setProperty("hibernate.dialect", env.getProperty("mysql-hibernate.dialect")); hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("mysql-hibernate.show_sql") != null ? env.getProperty("mysql-hibernate.show_sql") : "false"); return hibernateProperties; }

We can add the Hibernate specific properties to the mysql.properties file:

mysql-hibernate.dialect=org.hibernate.dialect.MySQLDialect mysql-hibernate.show_sql=true mysql-hibernate.hbm2ddl.auto=create-drop

3.5. Custom Conditions

If we don't want to use any of the conditions available in Spring Boot, we can also define custom conditions by extending the SpringBootCondition class and overriding the getMatchOutcome() method.

Let's create a condition called HibernateCondition for our additionalProperties() method that will verify whether a HibernateEntityManager class is present on the classpath:

static class HibernateCondition extends SpringBootCondition { private static String[] CLASS_NAMES = { "org.hibernate.ejb.HibernateEntityManager", "org.hibernate.jpa.HibernateEntityManager" }; @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("Hibernate"); return Arrays.stream(CLASS_NAMES) .filter(className -> ClassUtils.isPresent(className, context.getClassLoader())) .map(className -> ConditionOutcome .match(message.found("class") .items(Style.NORMAL, className))) .findAny() .orElseGet(() -> ConditionOutcome .noMatch(message.didNotFind("class", "classes") .items(Style.NORMAL, Arrays.asList(CLASS_NAMES)))); } }

Then we can add the condition to the additionalProperties() method:

@Conditional(HibernateCondition.class) Properties additionalProperties() { //... }

3.6. Application Conditions

We can also specify that the configuration can be loaded only inside/outside a web context, by adding the @ConditionalOnWebApplication or @ConditionalOnNotWebApplication annotation.

4. Testing the Auto-Configuration

Let's create a very simple example to test our auto-configuration. We will create an entity class called MyUser, and a MyUserRepository interface using Spring Data:

@Entity public class MyUser { @Id private String email; // standard constructor, getters, setters }
public interface MyUserRepository extends JpaRepository { }

To enable auto-configuration, we can use one of the @SpringBootApplication or @EnableAutoConfiguration annotations:

@SpringBootApplication public class AutoconfigurationApplication { public static void main(String[] args) { SpringApplication.run(AutoconfigurationApplication.class, args); } }

Next, let's write a JUnit test that saves a MyUser entity:

@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest( classes = AutoconfigurationApplication.class) @EnableJpaRepositories( basePackages = { "com.baeldung.autoconfiguration.example" }) public class AutoconfigurationTest { @Autowired private MyUserRepository userRepository; @Test public void whenSaveUser_thenOk() { MyUser user = new MyUser("[email protected]"); userRepository.save(user); } }

Since we have not defined our DataSource configuration, the application will use the auto-configuration we have created to connect to a MySQL database called myDb.

The connection string contains the createDatabaseIfNotExist=true property, so the database does not need to exist. However, the user mysqluser or the one specified through the mysql.user property if it is present, needs to be created.

We can check the application log to see that the MySQL data source is being used:

web - 2017-04-12 00:01:33,956 [main] INFO o.s.j.d.DriverManagerDataSource - Loaded JDBC driver: com.mysql.cj.jdbc.Driver

5. Disabling Auto-Configuration Classes

If we wanted to exclude the auto-configuration from being loaded, we could add the @EnableAutoConfiguration annotation with exclude or excludeName attribute to a configuration class:

@Configuration @EnableAutoConfiguration( exclude={MySQLAutoconfiguration.class}) public class AutoconfigurationApplication { //... }

Une autre option pour désactiver des configurations automatiques spécifiques consiste à définir la propriété spring.autoconfigure.exclude :

spring.autoconfigure.exclude=com.baeldung.autoconfiguration.MySQLAutoconfiguration

6. Conclusions

Dans ce didacticiel, nous avons montré comment créer une configuration automatique Spring Boot personnalisée. Le code source complet de l'exemple est disponible à l'adresse over sur GitHub.

Le test JUnit peut être exécuté à l'aide du profil d' autoconfiguration : mvn clean install -Pautoconfiguration .