Spring Boot очень прост и удобен в использовании. Он скрывает много контента, и вам не нужно об этом заботиться. Но хорошему разработчику может быть интересно узнать, что именно происходит за автоматической настройкой Spring Boot?
Spring Boot не является новой технологией, но стартер Spring Boot помогает нам настроить несколько bean-компонентов, управляемых Spring. Когда наш проект зависит от этих jar-файлов и запускает приложение Spring, в контейнере Spring уже есть объекты в пакете, которые создаются и управляются.
Короче говоря, автоконфигурация Spring Boot представляет собой способ автоматической настройки приложения Spring на основе зависимостей, присутствующих в пути к классам. Вы также можете по определению исключить определенные bean-компоненты, содержащиеся в классах автоконфигурации. Это может сделать разработку быстрее и проще.
Суть автоматической настройки Springboot заключается в автоматической настройке различных компонентов Spring. Затем приложение может использовать компонент напрямую с помощью методов внедрения, таких как @Autowired. Например, автоматически настройте такие компоненты, как redisTemplate и jdbcTemplate.
Создать приложение Spring Boot очень просто: просто создайте стартовый класс, содержащий main.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class App
{
public static void main(String[] args)
{
ApplicationContext ctx = SpringApplication.run(App.class, args);
}
}
Вышеупомянутый класс называется классом запуска приложения Spring Boot. Он направляет и запускает приложение Spring с помощью метода Java main(). Обычно он содержит следующее содержимое:
Эта аннотация на самом деле представляет собой ярлык с тремя примененными аннотациями.
@SpringBootConfiguration — это новая аннотация, появившаяся в Spring Boot2. Ранее мы использовали аннотацию @Configuration. Вы можете заменить ее на @Configuration. Оба они выполняют одну и ту же функцию. Это указывает на то, что класс является классом конфигурации и его следует сканировать на предмет дальнейшей конфигурации и определений компонентов.
Эта аннотация используется для включения автоматической настройки контекста приложения Spring, пытаясь угадать и настроить bean-компоненты, которые могут вам понадобиться. Классы автоконфигурации обычно применяются на основе вашего пути к классам и определяемых вами bean-компонентов. Автоматическая конфигурация пытается быть максимально умной и отступает по мере того, как вы определяете больше своей собственной конфигурации. Вы всегда можете вручную исключить любую конфигурацию, которую вы не хотите применять, двумя способами:
Используйте исключение Имя() Используйте свойство Spring.autoconfigure.exclude в файле свойств.
Эта аннотация обеспечивает поддержку параллельно с элементом Spring XML context:comComponent-scan. Либо basePackageClasses(), либо basePackages() могут определять конкретные пакеты для сканирования. Если конкретный пакет не определен, будет проверен пакет того класса, в котором объявлена эта аннотация.
Чтобы создать пользовательскую автоконфигурацию, нам нужно создать класс, аннотированный @Configuration, и зарегистрировать его. Давайте создадим собственную конфигурацию для источника данных MySQL:
@Configuration
public class MySQLAutoconfiguration {
//...
}
Следующим необходимым шагом является регистрация класса в качестве кандидата на автоконфигурацию, добавив его имя в свойство org.springframework.boot.autoconfigure.EnableAutoConfiguration в стандартном файле resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.peterwanghao.samples.springboot.autoconfiguration.MySQLAutoconfiguration
Если мы хотим, чтобы наш класс автоконфигурации имел приоритет над другими кандидатами автоконфигурации, мы можем добавить аннотацию @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE). Автоконфигурация разработана с использованием классов и компонентов, отмеченных аннотацией @Conditional, чтобы можно было заменить автоконфигурацию или определенные ее части. Обратите внимание, что автоконфигурация работает только в том случае, если в приложении не определены автоконфигурируемые bean-компоненты. Если вы определяете компонент, значение по умолчанию будет переопределено.
Условия класса позволяют нам указывать классы, указанные с помощью аннотации @ConditionalOnClass, или использовать аннотацию @ConditionalOnMissingClass для указания классов, которые не существуют в пути к классам. Укажем, что MySQLConfiguration будет загружаться только в том случае, если присутствует класс DataSource, и в этом случае можно предположить, что приложение будет использовать базу данных:
@Configuration
@ConditionalOnClass(DataSource.class)
public class MySQLAutoconfiguration {
//...
}
Если мы хотим включить компонент только в том случае, если указанный компонент существует, мы можем использовать аннотации @ConditionalOnBean и @ConditionalOnMissingBean. В качестве примера давайте добавим bean-компонентentityManagerFactory в наш класс конфигурации и укажем, что если bean-компонент с именем dataSource существует, а bean-компонент с именемentityManagerFactory еще не определен, мы создаем этот bean-компонент:
@Bean
@ConditionalOnBean(name = "dataSource")
@ConditionalOnMissingBean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.peterwanghao.samples.springboot.autoconfiguration.example");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
if (additionalProperties() != null) {
em.setJpaProperties(additionalProperties());
}
return em;
}
Давайте настроим bean-компонент transactionManager, который будет загружаться только в том случае, если еще не определен bean-компонент типа JpaTransactionManager:
@Bean
@ConditionalOnMissingBean(type = "JpaTransactionManager")
JpaTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
Аннотация @ConditionalOnProperty используется для указания, будет ли загружаться конфигурация в зависимости от наличия и значения свойства среды Spring. Сначала давайте добавим в конфигурацию исходный файл свойств, чтобы определить, откуда читать свойства:
@PropertySource("classpath:mysql.properties")
public class MySQLAutoconfiguration {
//...
}
Мы можем настроить основной компонент DataSource, который будет использоваться для создания соединения с базой данных и будет загружаться только в том случае, если присутствует свойство с именем usemysql. Мы можем использовать свойство hasValue для указания определенных значений свойств usemysql, которые должны совпадать. Если для атрибута usemysql установлено значение local, давайте определим bean-компонент dataSource со значением по умолчанию, который подключается к локальной базе данных с именем myDb:
@Bean
@ConditionalOnProperty(name = "usemysql", havingValue = "local")
@ConditionalOnMissingBean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true&&serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
Если для атрибута usemysql установлено значение custom, компонент источника данных будет настроен с использованием URL-адреса базы данных, пользователя и пароля значения пользовательского атрибута:
@Bean(name = "dataSource")
@ConditionalOnProperty(name = "usemysql", havingValue = "custom")
@ConditionalOnMissingBean
public DataSource dataSource2() {
final 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 в файл mysql.properties, а также добавить usemysql. = пользовательская строка.
Добавление аннотации @ConditionalOnResource означает, что конфигурация будет загружена только тогда, когда указанный ресурс существует.
Давайте определим метод extraProperties(), который будет возвращать объект Properties, содержащий свойства, специфичные для Hibernate, используемые bean-компонентомentityManagerFactory, только если присутствует файл ресурсов mysql.properties:
@ConditionalOnResource(resources = "classpath:mysql.properties")
@Conditional(HibernateCondition.class)
final Properties additionalProperties() {
final 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.MySQL5InnoDBDialect mysql-hibernate.show_sql=true mysql-hibernate.hbm2ddl.auto=create-drop
Если мы не хотим использовать какие-либо доступные условия в Spring Boot, мы также можем определить собственные условия, расширив класс SpringBootCondition и переопределив метод getMatchOutcome().
Давайте создадим условие HibernateCondition для метода extraProperties(), которое будет проверять, присутствует ли класс HibernateEntityManager в пути к классам:
static class HibernateCondition extends SpringBootCondition {
private static final 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))));
}
}
Затем мы можем добавить условия к методу extraProperties():
@Conditional(HibernateCondition.class)
Properties additionalProperties() {
//...
}
Мы также можем указать, что конфигурацию можно загружать только внутри/вне веб-контекста, добавив аннотации @ConditionalOnWebApplication или @ConditionalOnNotWebApplication.
Давайте создадим очень простой пример для проверки нашей автоматической настройки. Мы будем использовать Spring Data для создания класса сущности с именем MyUser и интерфейса MyUserRepository:
@Entity
public class MyUser {
@Id
private String email;
public MyUser() {
}
public MyUser(String email) {
super();
this.email = email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public interface MyUserRepository extends JpaRepository<MyUser, String> {
}
Чтобы включить автоматическую настройку, мы можем использовать аннотацию @SpringBootApplication или @EnableAutoConfiguration:
@SpringBootApplication
public class AutoconfigurationApplication {
public static void main(String[] args) {
SpringApplication.run(AutoconfigurationApplication.class, args);
}
}
Далее давайте напишем тест JUnit, сохраняющий сущность MyUser:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = AutoconfigurationApplication.class)
@EnableJpaRepositories(basePackages = { "com.peterwanghao.samples.springboot.autoconfiguration.example" })
public class AutoconfigurationLiveTest {
@Autowired
private MyUserRepository userRepository;
@Test
public void whenSaveUser_thenOk() {
MyUser user = new MyUser("user@email.com");
userRepository.save(user);
}
}
Если мы хотим исключить автоконфигурацию из загрузки, мы можем добавить аннотацию @EnableAutoConfiguration с атрибутом Exclude или ExcludeName в класс конфигурации:
@Configuration
@EnableAutoConfiguration(exclude={MySQLAutoconfiguration.class})
public class AutoconfigurationApplication {
//...
}
Другой способ отключить определенную автоконфигурацию — установить свойство Spring.autoconfigure.exclude:
spring.autoconfigure.exclude=com.peterwanghao.samples.springboot.autoconfiguration.MySQLAutoconfiguration
В этом руководстве мы рассказали, как Spring Boot автоматически загружает классы конфигурации и конкретную реализацию, скрытую за ними. Показывает, как создать собственную автоконфигурацию Spring Boot.
На этом завершается статья об использовании автоматической конфигурации, одного из четырех основных артефактов SpringBoot.