Buat Konfigurasi Auto Kustom dengan Spring Boot

1. Gambaran keseluruhan

Ringkasnya, konfigurasi automatik Spring Boot mewakili cara untuk mengkonfigurasi aplikasi Spring secara automatik berdasarkan kebergantungan yang ada di classpath.

Ini dapat menjadikan pembangunan lebih cepat dan mudah dengan menghilangkan keperluan untuk menentukan kacang tertentu yang termasuk dalam kelas konfigurasi automatik.

Pada bahagian berikut, kita akan melihat pembuatan konfigurasi automatik Spring Boot khusus kita .

2. Pergantungan Maven

Mari kita mulakan dengan kebergantungan yang kita perlukan:

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

Versi terbaru spring-boot-starter-data-jpa dan mysql-connector-java boleh dimuat turun dari Maven Central.

3. Membuat Konfigurasi Auto Tersuai

Untuk membuat konfigurasi automatik tersuai, kita perlu membuat kelas yang diberi penjelasan sebagai @Configuration dan mendaftarkannya.

Mari buat konfigurasi khusus untuk sumber data MySQL :

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

Langkah wajib seterusnya ialah mendaftarkan kelas sebagai calon konfigurasi automatik, dengan menambahkan nama kelas di bawah org.springframework.boot.autoconfigure.EnableAutoConfiguration dalam sumber fail standard / META-INF / spring.factories :

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

Sekiranya kami mahu kelas konfigurasi automatik kami mempunyai keutamaan daripada calon konfigurasi automatik yang lain, kami dapat menambahkan anotasi @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE) .

Konfigurasi automatik dirancang menggunakan kelas dan kacang yang ditandai dengan anotasi @ Syarat supaya konfigurasi automatik atau bahagian tertentu dapat diganti.

Perhatikan bahawa konfigurasi automatik hanya berlaku jika kacang yang dikonfigurasi secara automatik tidak ditentukan dalam aplikasi. Sekiranya anda menentukan kacang anda, maka yang lalai akan diganti.

3.1. Syarat Kelas

Keadaan kelas membolehkan kami menentukan bahawa kacang konfigurasi akan disertakan jika kelas yang ditentukan hadir menggunakan anotasi @ConditionalOnClass , atau jika kelas tidak hadir menggunakan anotasi @ConditionalOnMissingClass .

Mari tentukan bahawa konfigurasi MySQLC kami hanya akan dimuat jika kelas DataSource ada, dalam hal ini kami dapat menganggap aplikasi akan menggunakan pangkalan data:

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

3.2. Keadaan Kekacang

Jika kita mahu termasuk kacang hanya jika kacang yang ditetapkan hadir atau tidak , kita boleh menggunakan @ConditionalOnBean dan @ConditionalOnMissingBean penjelasan.

Untuk mencontohkannya, mari tambahkan entitiManagerFactory bean ke kelas konfigurasi kami, dan tentukan kami hanya mahu kacang ini dibuat jika kacang yang disebut dataSource ada dan jika kacang yang disebut entitiManagerFactory 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 TransactionManager kacang yang hanya akan dimuatkan jika kacang jenis JpaTransactionManager belum ditakrifkan:

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

3.3. Keadaan Harta Tanah

The @ConditionalOnProperty anotasi digunakan untuk menentukan jika konfigurasi yang akan dimuatkan berdasarkan kehadiran dan nilai harta Spring Alam Sekitar .

Pertama, mari tambahkan fail sumber harta tanah untuk konfigurasi kami yang akan menentukan dari mana hartanah akan dibaca:

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

Kita dapat mengkonfigurasi kacang DataSource utama yang akan digunakan untuk membuat sambungan ke pangkalan data sedemikian rupa sehingga hanya akan dimuat jika ada harta yang disebut usemysql .

Kita boleh menggunakan atribut havingValue untuk menentukan nilai tertentu dari sifat usemysql yang harus dipadankan.

Mari kita tentukan dataSource bean dengan nilai lalai yang menyambung ke pangkalan data tempatan yang disebut myDb jika sifat usemysql ditetapkan ke tempatan :

@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 harta ditetapkan kepada adat, yang sumber data kacang akan ditetapkan menggunakan nilai harta peribadi untuk URL pangkalan data, pengguna, dan kata laluan:

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

The mysql.properties fail akan mengandungi usemysql harta:

usemysql=local

Sekiranya aplikasi yang menggunakan MySQLAutoconfiguration ingin mengganti sifat lalai, semua yang perlu dilakukan adalah menambahkan nilai yang berbeza untuk sifat mysql.url , mysql.user dan mysql.pass dan baris khusus usemysql = dalam fail mysql.properties .

3.4. Keadaan Sumber

Menambah anotasi @ConditionalOnResource bermaksud bahawa konfigurasi hanya akan dimuat apabila sumber tertentu ditentukan .

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

Pilihan lain untuk melumpuhkan konfigurasi automatik tertentu adalah dengan menetapkan properti spring.autoconfigure.exclude :

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

6. Kesimpulan

Dalam tutorial ini, kami telah menunjukkan cara membuat konfigurasi automatik Spring Boot tersuai. Kod sumber lengkap contoh boleh didapati di GitHub.

Ujian JUnit dapat dijalankan dengan menggunakan profil autokonfigurasi : mvn clean install -Pautoconfiguration .