«1. Обзор
Проще говоря, автоконфигурация Spring Boot представляет собой способ автоматической настройки приложения Spring на основе зависимостей, присутствующих в пути к классам.
Это может ускорить и упростить разработку, устраняя необходимость определения определенных bean-компонентов, включенных в классы автоконфигурации.
В следующем разделе мы рассмотрим создание нашей пользовательской автоматической конфигурации Spring Boot.
2. Зависимости Maven
Начнем с зависимостей, которые нам нужны:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
Последние версии spring-boot-starter-data-jpa и mysql-connector-java можно загрузить с Maven Central .
3. Создание пользовательской автоконфигурации
Чтобы создать пользовательскую автоконфигурацию, нам нужно создать класс с аннотацией @Configuration и зарегистрировать его.
Давайте создадим пользовательскую конфигурацию для источника данных MySQL:
@Configuration
public class MySQLAutoconfiguration {
//...
}
Следующий обязательный шаг — регистрация класса в качестве кандидата на автоматическую настройку, путем добавления имени класса в ключ org.springframework.boot .autoconfigure.EnableAutoConfiguration в стандартном файле resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baeldung.autoconfiguration.MySQLAutoconfiguration
Если мы хотим, чтобы наш класс автоконфигурации имел приоритет над другими кандидатами на автоконфигурацию, мы можем добавить @AutoConfigureOrder(Ordered .HIGHEST_PRECEDENCE) аннотацию.
Автоконфигурация разработана с использованием классов и bean-компонентов, помеченных аннотациями @Conditional, чтобы можно было заменить автоконфигурацию или определенные ее части.
Обратите внимание, что автоконфигурация действует только в том случае, если автоконфигурируемые bean-компоненты не определены в приложении. Если вы определите свой bean-компонент, то значение по умолчанию будет переопределено.
3.1. Условия класса
Условия класса позволяют указать, что компонент конфигурации будет включен, если указанный класс присутствует с помощью аннотации @ConditionalOnClass или если класс отсутствует с помощью аннотации @ConditionalOnMissingClass.
Давайте укажем, что наша MySQLConfiguration будет загружаться только в том случае, если присутствует класс DataSource, и в этом случае мы можем предположить, что приложение будет использовать базу данных:
@Configuration
@ConditionalOnClass(DataSource.class)
public class MySQLAutoconfiguration {
//...
}
3.2. Условия компонента
Если мы хотим включить компонент только в том случае, если указанный компонент присутствует или нет, мы можем использовать аннотации @ConditionalOnBean и @ConditionalOnMissingBean.
Чтобы проиллюстрировать это, давайте добавим bean-компонент entityManagerFactory в наш класс конфигурации и укажем, что мы хотим, чтобы этот bean-компонент был создан только в том случае, если bean-компонент с именем dataSource присутствует и если bean-компонент с именем entityManagerFactory еще не определен:
@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;
}
~~ ~ Давайте также настроим bean-компонент transactionManager, который будет загружаться только в том случае, если bean-компонент типа JpaTransactionManager еще не определен:
@Bean
@ConditionalOnMissingBean(type = "JpaTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
3.3. Условия свойств
Аннотация @ConditionalOnProperty используется для указания, будет ли загружаться конфигурация на основе наличия и значения свойства Spring Environment.
Во-первых, давайте добавим исходный файл свойств для нашей конфигурации, который будет определять, откуда будут считываться свойства:
@PropertySource("classpath:mysql.properties")
public class MySQLAutoconfiguration {
//...
}
Мы можем настроить основной bean-компонент DataSource, который будет использоваться для создания соединений с базой данных в таких таким образом, что он будет загружаться только в том случае, если присутствует свойство, называемое usemysql.
Мы можем использовать атрибут haveValue, чтобы указать определенные значения свойства usemysql, которые должны сопоставляться.
Давайте определим bean-компонент dataSource со значениями по умолчанию, который подключается к локальной базе данных с именем myDb, если для свойства usemysql установлено значение 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;
}
Если для свойства usemysql установлено значение custom, bean-компонент dataSource будет настроен с использованием пользовательские значения свойств для URL-адреса базы данных, пользователя и пароля:
@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;
}
Файл mysql.properties будет содержать свойство usemysql:
usemysql=local
Если приложение, использующее MySQLAutoconfiguration, хочет переопределить свойства по умолчанию, все, что нужно сделать, это добавить разные значения для свойств mysql.url, mysql.user и mysql.pass и строку usemysql=custom в файле mysql.properties.
3.4. Условия ресурсов
«Добавление аннотации @ConditionalOnResource означает, что конфигурация будет загружаться только при наличии указанного ресурса.
Давайте определим метод с именем AdditionalProperties(), который будет возвращать объект Properties, содержащий специфичные для Hibernate свойства, которые будут использоваться bean-компонентом entityManagerFactory, только если присутствует файл ресурсов mysql.properties:
@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;
}
Мы можем добавить специальные свойства Hibernate в файл mysql.properties:
mysql-hibernate.dialect=org.hibernate.dialect.MySQLDialect
mysql-hibernate.show_sql=true
mysql-hibernate.hbm2ddl.auto=create-drop
3.5. Пользовательские условия
Если мы не хотим использовать какие-либо условия, доступные в Spring Boot, мы также можем определить пользовательские условия, расширив класс SpringBootCondition и переопределив метод getMatchOutcome().
Давайте создадим условие с именем HibernateCondition для нашего метода AdditionalProperties(), которое будет проверять, присутствует ли класс HibernateEntityManager в пути к классам:
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))));
}
}
Затем мы можем добавить условие в метод AdditionalProperties():
@Conditional(HibernateCondition.class)
Properties additionalProperties() {
//...
}
3.6. Условия приложения
Мы также можем указать, что конфигурация может быть загружена только внутри/вне веб-контекста, добавив аннотацию @ConditionalOnWebApplication или @ConditionalOnNotWebApplication.
4. Проверка автоматической настройки
Давайте создадим очень простой пример для проверки нашей автоматической настройки. Мы создадим класс сущности с именем MyUser и интерфейс MyUserRepository, используя Spring Data:
@Entity
public class MyUser {
@Id
private String email;
// standard constructor, getters, setters
}
public interface MyUserRepository
extends JpaRepository<MyUser, String> { }
@SpringBootApplication
public class AutoconfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(AutoconfigurationApplication.class, args);
}
}
Чтобы включить автоматическую настройку, мы можем использовать одну из аннотаций @SpringBootApplication или @EnableAutoConfiguration:
@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);
}
}
~~ ~ Далее давайте напишем тест JUnit, который сохраняет объект MyUser:
Поскольку мы не определили нашу конфигурацию DataSource, приложение будет использовать созданную нами автоконфигурацию для подключения к базе данных MySQL с именем myDb.
Строка подключения содержит свойство createDatabaseIfNotExist=true, поэтому база данных может не существовать. Однако необходимо создать пользователя mysqluser или пользователя, указанного в свойстве mysql.user, если он присутствует.
web - 2017-04-12 00:01:33,956 [main] INFO o.s.j.d.DriverManagerDataSource - Loaded JDBC driver: com.mysql.cj.jdbc.Driver
Мы можем проверить журнал приложения, чтобы увидеть, что используется источник данных MySQL:
5. Отключение классов автоконфигурации
@Configuration
@EnableAutoConfiguration(
exclude={MySQLAutoconfiguration.class})
public class AutoconfigurationApplication {
//...
}
Если мы хотим исключить загрузку автоконфигурации, мы можно добавить аннотацию @EnableAutoConfiguration с атрибутом exclude или excludeName в класс конфигурации:
spring.autoconfigure.exclude=com.baeldung.autoconfiguration.MySQLAutoconfiguration
Другой способ отключить определенные автоконфигурации — установить свойство spring.autoconfigure.exclude:
6. Выводы
В этом руководстве мы показали, как создать пользовательскую автоконфигурацию Spring Boot. Полный исходный код примера можно найти на GitHub.