Buat Konfigurasi Otomatis Kustom dengan Spring Boot

1. Ikhtisar

Sederhananya, konfigurasi otomatis Spring Boot menunjukkan cara untuk secara otomatis mengkonfigurasi aplikasi Spring berdasarkan dependensi yang ada di classpath.

Ini dapat membuat pengembangan lebih cepat dan lebih mudah dengan menghilangkan kebutuhan untuk mendefinisikan kacang tertentu yang termasuk dalam kelas konfigurasi otomatis.

Pada bagian berikut, kita akan melihat cara membuat konfigurasi otomatis Spring Boot kustom kita .

2. Ketergantungan Maven

Mari kita mulai dengan dependensi yang kita butuhkan:

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

Versi terbaru dari spring-boot-starter-data-jpa dan mysql-connector-java dapat diunduh dari Maven Central.

3. Membuat Konfigurasi Otomatis Kustom

Untuk membuat konfigurasi otomatis khusus, kita perlu membuat kelas yang dianotasi sebagai @Configuration dan mendaftarkannya.

Mari buat konfigurasi khusus untuk sumber data MySQL :

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

Langkah wajib berikutnya adalah mendaftarkan kelas sebagai kandidat konfigurasi otomatis, dengan menambahkan nama kelas di bawah kunci org.springframework.boot.autoconfigure.EnableAutoConfiguration di file standar resources / META-INF / spring.factories :

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

Jika kita ingin kelas konfigurasi otomatis kita memiliki prioritas di atas kandidat konfigurasi otomatis lainnya, kita dapat menambahkan anotasi @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE) .

Konfigurasi otomatis dirancang menggunakan kelas dan kacang yang ditandai dengan anotasi @Conditional sehingga konfigurasi otomatis atau bagian tertentu darinya dapat diganti.

Perhatikan bahwa konfigurasi otomatis hanya berlaku jika kacang yang dikonfigurasi secara otomatis tidak ditentukan dalam aplikasi. Jika Anda mendefinisikan kacang Anda, maka yang default akan diganti.

3.1. Kondisi Kelas

Kondisi kelas memungkinkan kita untuk menentukan bahwa kacang konfigurasi akan disertakan jika kelas yang ditentukan ada menggunakan anotasi @ConditionalOnClass , atau jika kelas tidak ada menggunakan anotasi @ConditionalOnMissingClass .

Mari kita tentukan bahwa MySQLConfiguration kita hanya akan dimuat jika kelas DataSource ada, dalam hal ini kita dapat mengasumsikan aplikasi akan menggunakan database:

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

3.2. Kondisi Kacang

Jika kita ingin menyertakan kacang hanya jika kacang ditentukan hadir atau tidak , kita dapat menggunakan @ConditionalOnBean dan @ConditionalOnMissingBean penjelasan.

Sebagai contoh, mari tambahkan kacang entityManagerFactory ke kelas konfigurasi kita, dan tentukan kita hanya ingin kacang ini dibuat jika kacang bernama dataSource ada dan jika kacang bernama entityManagerFactory belum ditentukan:

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

Mari kita juga mengkonfigurasi kacang transactionManager yang hanya akan dimuat jika kacang jenis JpaTransactionManager belum didefinisikan:

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

3.3. Kondisi Properti

The @ConditionalOnProperty penjelasan digunakan untuk menentukan apakah konfigurasi akan dimuat berdasarkan keberadaan dan nilai properti musim semi Lingkungan .

Pertama, mari tambahkan file sumber properti untuk konfigurasi kita yang akan menentukan dari mana properti akan dibaca:

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

Kita dapat mengkonfigurasi bean DataSource utama yang akan digunakan untuk membuat koneksi ke database sedemikian rupa sehingga hanya akan dimuat jika properti bernama usemysql ada.

Kita bisa menggunakan atribut havingValue untuk menentukan nilai tertentu dari properti usemysql yang harus cocok.

Mari definisikan bean dataSource dengan nilai default yang terhubung ke database lokal yang disebut myDb jika properti usemysql disetel ke lokal :

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

Jika usemysql properti diatur ke kustom, yang DataSource kacang akan dikonfigurasi menggunakan nilai properti kustom untuk URL database, user, dan 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; }

File mysql.properties akan berisi properti usemysql :

usemysql=local

Jika aplikasi yang menggunakan MySQLAutoconfiguration ingin mengganti properti default, yang perlu dilakukan hanyalah menambahkan nilai yang berbeda untuk properti mysql.url , mysql.user dan mysql.pass dan baris usemysql = custom di file mysql.properties .

3.4. Kondisi Sumber Daya

Menambahkan anotasi @ConditionalOnResource berarti bahwa konfigurasi hanya akan dimuat saat resource yang ditentukan ada .

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

Opsi lain untuk menonaktifkan konfigurasi otomatis tertentu adalah dengan menyetel properti spring.autoconfigure.exclude :

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

6. Kesimpulan

Dalam tutorial ini, kami telah menunjukkan cara membuat konfigurasi otomatis Spring Boot kustom. Kode sumber lengkap dari contoh tersebut dapat ditemukan di GitHub.

Tes JUnit dapat dijalankan menggunakan profil konfigurasi otomatis : mvn clean install -Pautoconfiguration .