Erstellen Sie eine benutzerdefinierte Auto-Konfiguration mit Spring Boot

1. Überblick

Einfach ausgedrückt, stellt die automatische Konfiguration von Spring Boot eine Möglichkeit dar, eine Spring-Anwendung basierend auf den Abhängigkeiten im Klassenpfad automatisch zu konfigurieren.

Dies kann die Entwicklung beschleunigen und vereinfachen, da die Definition bestimmter Beans in den Autokonfigurationsklassen entfällt.

Im folgenden Abschnitt werden wir einen Blick auf das Erstellen unserer benutzerdefinierten Spring Boot-Autokonfiguration werfen.

2. Abhängigkeiten von Maven

Beginnen wir mit den Abhängigkeiten, die wir brauchen:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.11</version>
</dependency>

Die neuesten Versionen von spring-boot-starter-data-jpa und mysql-connector-java kann heruntergeladen werden von Maven Central.

3. Erstellen einer benutzerdefinierten automatischen Konfiguration

  • Um eine benutzerdefinierte Autokonfiguration zu erstellen, müssen Sie eine als @ Configuration kommentierte Klasse erstellen und registrieren. **

Erstellen wir eine benutzerdefinierte Konfiguration für eine MySQL -Datenquelle:

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

Der nächste obligatorische Schritt ist das Registrieren der Klasse als Kandidat für die automatische Konfiguration, indem der Name der Klasse unter dem Schlüssel org.springframework.boot.autoconfigure.EnableAutoConfiguration in der Standarddatei resources/META-INF/spring.factories hinzugefügt wird:

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

Wenn unsere Auto-Configuration-Klasse Vorrang vor anderen Auto-Configuration-Kandidaten haben soll, können wir die Annotation @ AutoConfigureOrder (Ordered.HIGHEST PRECEDENCE) __ hinzufügen.

Die Autokonfiguration wird unter Verwendung von Klassen und Beans entworfen, die mit @ Conditional -Annotationen markiert sind, sodass die Autokonfiguration oder bestimmte Teile davon ersetzt werden können.

  • Beachten Sie, dass die Autokonfiguration nur wirksam ist, wenn die automatisch konfigurierten Beans nicht in der Anwendung definiert sind. Wenn Sie Ihre Bean definieren, wird die Standardeinstellung überschrieben. **

3.1. Klassenbedingungen

Klassenbedingungen ermöglichen es uns anzugeben, dass eine Konfigurations-Bean eingeschlossen wird, wenn eine angegebene Klasse vorhanden ist, mit der Annotation @ ConditionalOnClass , oder wenn eine Klasse nicht vorhanden ist , die Annotation @ ConditionalOnMissingClass verwendet.

Nehmen wir an, dass unsere MySQLConfiguration nur geladen wird, wenn die Klasse DataSource vorhanden ist. In diesem Fall können wir davon ausgehen, dass die Anwendung eine Datenbank verwendet:

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

3.2. Bohnenbedingungen

Wenn Sie nur eine Bean einschließen möchten, wenn eine bestimmte Bean vorhanden ist , können Sie die Annotationen @ ConditionalOnBean und @ ConditionalOnMissingBean verwenden.

Um dies zu veranschaulichen, fügen wir unserer Konfigurationsklasse eine entityManagerFactory -Bean hinzu und geben an, dass diese Bean nur dann erstellt werden soll, wenn eine Bean mit dem Namen dataSource vorhanden ist und wenn eine Bean mit dem Namen entityManagerFactory noch nicht definiert ist:

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

Lassen Sie uns auch eine transactionManager -Bean konfigurieren, die nur geladen wird, wenn noch keine Bean des Typs JpaTransactionManager definiert ist:

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

3.3. Eigentumsbedingungen

Die Annotation @ ConditionalOnProperty wird verwendet, um anzugeben, ob eine Konfiguration basierend auf dem Vorhandensein und dem Wert einer Spring Environment-Eigenschaft geladen wird .

Zuerst fügen wir eine Eigenschaftsquelldatei für unsere Konfiguration hinzu, die bestimmt, woher die Eigenschaften gelesen werden:

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

Wir können das Haupt- DataSource Bean konfigurieren, mit dem Verbindungen zur Datenbank hergestellt werden, und zwar so, dass es nur geladen wird, wenn eine Eigenschaft namens usemysql vorhanden ist.

Wir können das Attribut havingValue verwenden, um bestimmte Werte der usemysql -Eigenschaft anzugeben, die abgeglichen werden müssen.

Definieren Sie die dataSource -Bean mit Standardwerten, die eine Verbindung zu einer lokalen Datenbank namens myDb herstellen, wenn die usemysql -Eigenschaft auf local gesetzt ist:

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

Wenn die usemysql -Eigenschaft auf custom gesetzt ist, wird die dataSource -Bean mit benutzerdefinierten Eigenschaftswerten für die Datenbank-URL, den Benutzer und das Kennwort konfiguriert:

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

Die Datei mysql.properties enthält die Eigenschaft usemysql :

usemysql=local

Wenn eine Anwendung, die MySQLAutoconfiguration verwendet, die Standardeigenschaften überschreiben möchte, müssen Sie lediglich verschiedene Werte für die Eigenschaften mysql.url , mysql.user und mysql.pass und die Zeile usemysql = custom in der Datei mysql.properties hinzufügen.

3.4. Ressourcenkonditionen

Durch das Hinzufügen der Annotation @ ConditionalOnResource wird die Konfiguration nur geladen, wenn eine angegebene Ressource vorhanden ist .

Definieren Sie eine Methode namens additionalProperties () , die ein Properties -Objekt mit Hibernat-spezifischen Eigenschaften zurückgibt, das von der entityManagerFactory -Bean verwendet wird, nur wenn die Ressourcendatei mysql.properties vorhanden ist:

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

Wir können die Hibernate-spezifischen Eigenschaften zur Datei mysql.properties hinzufügen:

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

3.5. Kundenspezifische Bedingungen

Wenn Sie keine der in Spring Boot verfügbaren Bedingungen verwenden möchten, können Sie auch benutzerdefinierte Bedingungen definieren, indem Sie die SpringBootCondition -Klasse erweitern und die __getMatchOutcome () - Methode überschreiben.

Erstellen wir eine Bedingung namens HibernateCondition für unsere additionalProperties () -Methode, die überprüft, ob eine HibernateEntityManager -Klasse im Klassenpfad vorhanden ist:

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

Dann können wir die Bedingung der Methode additionalProperties () hinzufügen:

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

3.6. Bewerbungsbedingungen

Wir können auch angeben, dass die Konfiguration nur innerhalb/außerhalb eines Web-Kontexts geladen werden kann, indem Sie die Annotation @ ConditionalOnWebApplication oder @ ConditionalOnNotWebApplication hinzufügen.

4. Testen der automatischen Konfiguration

Lassen Sie uns ein sehr einfaches Beispiel erstellen, um unsere automatische Konfiguration zu testen. Wir erstellen eine Entitätsklasse mit dem Namen MyUser und eine MyUserRepository -Schnittstelle mit Spring Data:

@Entity
public class MyUser {
    @Id
    private String email;

   //standard constructor, getters, setters
}
public interface MyUserRepository
  extends JpaRepository<MyUser, String> { }

Um die automatische Konfiguration zu aktivieren, können wir eine der Annotationen @ SpringBootApplication oder @ EnableAutoConfiguration verwenden:

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

Als Nächstes schreiben wir einen JUnit -Test, der eine MyUser -Entität speichert:

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

Da wir unsere DataSource -Konfiguration nicht definiert haben, verwendet die Anwendung die von uns erstellte automatische Konfiguration, um eine Verbindung zu einer MySQL -Datenbank mit dem Namen myDb herzustellen.

  • Die Verbindungszeichenfolge enthält die Eigenschaft createDatabaseIfNotExist = true , daher muss die Datenbank nicht vorhanden sein. Der Benutzer mysqluser oder der durch die mysql.user -Eigenschaft angegebene Benutzer, falls vorhanden, muss jedoch erstellt werden.

Wir können das Anwendungsprotokoll überprüfen, um festzustellen, dass die Datenquelle MySQL verwendet wird:

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

5. Deaktivieren von Auto-Configuration-Klassen

Wenn wir die automatische Konfiguration vom Laden ausschließen möchten, können Sie die Annotation @ EnableAutoConfiguration mit dem Attribut exclude oder excludeName zu einer Konfigurationsklasse hinzufügen:

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

Eine weitere Option zum Deaktivieren bestimmter Autokonfigurationen ist das Festlegen der Eigenschaft spring.autoconfigure.exclude :

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

6. Schlussfolgerungen

In diesem Lernprogramm haben wir gezeigt, wie Sie eine benutzerdefinierte Spring Boot-Autokonfiguration erstellen. Den vollständigen Quellcode des Beispiels finden Sie auf GitHub unter https://github.com/eugenp/tutorials/tree/master/spring-boot-autoconfiguration.html .

Der JUnit-Test kann mit dem Profil autoconfiguration ausgeführt werden: mvn clean install -Pautoconfiguration .