Crea una configurazione automatica personalizzata con Spring Boot

1. Panoramica

In poche parole, l'autoconfigurazione di Spring Boot rappresenta un modo per configurare automaticamente un'applicazione Spring in base alle dipendenze presenti nel classpath.

Ciò può rendere lo sviluppo più veloce e più semplice eliminando la necessità di definire determinati bean inclusi nelle classi di configurazione automatica.

Nella sezione seguente, daremo un'occhiata alla creazione della nostra configurazione automatica Spring Boot personalizzata .

2. Dipendenze di Maven

Cominciamo con le dipendenze di cui abbiamo bisogno:

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

Le ultime versioni di spring-boot-starter-data-jpa e mysql-connector-java possono essere scaricate da Maven Central.

3. Creazione di una configurazione automatica personalizzata

Per creare una configurazione automatica personalizzata, dobbiamo creare una classe annotata come @Configuration e registrarla.

Creiamo una configurazione personalizzata per un'origine dati MySQL :

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

Il passaggio successivo obbligatorio è la registrazione della classe come candidato alla configurazione automatica, aggiungendo il nome della classe sotto la chiave org.springframework.boot.autoconfigure.EnableAutoConfiguration nel file standard resources / META-INF / spring.factories :

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

Se vogliamo che la nostra classe di configurazione automatica abbia la priorità sugli altri candidati alla configurazione automatica, possiamo aggiungere l' annotazione @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE) .

La configurazione automatica è progettata utilizzando classi e bean contrassegnati con annotazioni @Conditional in modo che la configurazione automatica o parti specifiche di essa possano essere sostituite.

Notare che la configurazione automatica è attiva solo se i bean configurati automaticamente non sono definiti nell'applicazione. Se definisci il tuo bean, quello predefinito verrà sovrascritto.

3.1. Condizioni del corso

Le condizioni della classe ci consentono di specificare che un bean di configurazione verrà incluso se una classe specificata è presente utilizzando l' annotazione @ConditionalOnClass , o se una classe è assente utilizzando l' annotazione @ConditionalOnMissingClass .

Specifichiamo che la nostra configurazione MySQLC verrà caricata solo se è presente la classe DataSource , nel qual caso possiamo supporre che l'applicazione utilizzerà un database:

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

3.2. Condizioni dei fagioli

Se vogliamo includere un bean solo se un bean specificato è presente o meno , possiamo utilizzare le annotazioni @ConditionalOnBean e @ConditionalOnMissingBean .

Per esemplificare ciò, aggiungiamo un bean entityManagerFactory alla nostra classe di configurazione e specifichiamo che vogliamo che questo bean venga creato solo se è presente un bean chiamato dataSource e se un bean chiamato entityManagerFactory non è già definito:

@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; }

Configuriamo anche un bean transactionManager che verrà caricato solo se un bean di tipo JpaTransactionManager non è già definito:

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

3.3. Condizioni della proprietà

L' annotazione @ConditionalOnProperty viene utilizzata per specificare se una configurazione verrà caricata in base alla presenza e al valore di una proprietà Spring Environment .

Innanzitutto, aggiungiamo un file di origine delle proprietà per la nostra configurazione che determinerà da dove verranno lette le proprietà:

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

Possiamo configurare il bean DataSource principale che verrà utilizzato per creare connessioni al database in modo tale che venga caricato solo se è presente una proprietà denominata usemysql .

Possiamo utilizzare l'attributo havingValue per specificare determinati valori della proprietà usemysql che devono essere confrontati.

Definiamo il bean dataSource con valori predefiniti che si connettono a un database locale chiamato myDb se la proprietà usemysql è impostata su 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; }

Se la proprietà usemysql è impostata su custom, il bean dataSource verrà configurato utilizzando i valori delle proprietà personalizzate per l'URL del database, l'utente e la password:

@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; }

Il file mysql.properties conterrà la proprietà usemysql :

usemysql=local

Se un'applicazione che utilizza MySQLAutoconfiguration desidera sovrascrivere le proprietà predefinite, tutto ciò che deve fare è aggiungere valori diversi per le proprietà mysql.url , mysql.user e mysql.pass e la riga usemysql = custom nel file mysql.properties .

3.4. Condizioni delle risorse

L' aggiunta dell'annotazione @ConditionalOnResource significa che la configurazione verrà caricata solo quando è presente una risorsa specificata .

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 { //... }

Un'altra opzione per disabilitare le configurazioni automatiche specifiche è l'impostazione della proprietà spring.autoconfigure.exclude :

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

6. Conclusioni

In questo tutorial, abbiamo mostrato come creare una configurazione automatica Spring Boot personalizzata. Il codice sorgente completo dell'esempio può essere trovato su GitHub.

Il test JUnit può essere eseguito utilizzando il profilo di autoconfigurazione : mvn clean install -Pautoconfiguration .