Как Spring Boot подгружает модули
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как Spring Boot подгружает модули (Auto-configuration)
Короткий ответ
Spring Boot использует механизм Auto-configuration (автоконфигурация) с @EnableAutoConfiguration и условными аннотациями (@ConditionalOnClass, @ConditionalOnMissingBean и т.д.) для автоматической загрузки и конфигурации модулей на основе classpath и properties файлов.
Основной механизм: Spring Boot Auto-Configuration
Как это работает
1. Приложение запускается
↓
2. Spring Boot сканирует META-INF/spring.factories
↓
3. Загружает все классы auto-configuration
↓
4. Для каждого класса проверяет условия (@ConditionalOn*)
↓
5. Если условия выполнены → конфигурирует модуль
↓
6. Готово
Файл spring.factories
Это волшебный файл в META-INF директории каждой Spring Boot библиотеки.
Пример: spring-boot-starter-web
# Файл: spring-boot-starter-web/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
Пример: spring-boot-starter-data-jpa
# Файл: spring-boot-starter-data-jpa/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
Условные аннотации (Conditional Annotations)
Это условия, которые определяют, должна ли загружаться конфигурация.
1. @ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(name = "javax.servlet.ServletContext")
public class WebServerAutoConfiguration {
// Эта конфигурация загружается только если в classpath есть ServletContext
// (то есть если добавлена зависимость spring-boot-starter-web)
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
2. @ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonAutoConfiguration {
// Эта конфигурация загружается только если пользователь НЕ определил
// собственный ObjectMapper bean
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
3. @ConditionalOnProperty
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnProperty(name = "spring.h2.console.enabled", havingValue = "true")
public class H2ConsoleAutoConfiguration {
// Загружается только если в application.properties/yml есть:
// spring.h2.console.enabled=true
@Bean
public H2Console h2Console() {
return new H2Console();
}
}
4. @ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TransactionAutoConfiguration {
// Загружается только если в контексте уже существует DataSource bean
@Bean
@ConditionalOnBean(DataSource.class)
public TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
Порядок загрузки модулей
@AutoConfigureAfter и @AutoConfigureBefore
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Configuration;
// Загружается после DataSourceAutoConfiguration
@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class JpaAutoConfiguration {
// Зависит от DataSource, поэтому должна загрузиться после
}
// Загружается ДО других конфигураций
@Configuration
@AutoConfigureBefore({JpaAutoConfiguration.class})
public class DataSourceAutoConfiguration {
// Должна быть загружена первой
}
Практический пример: создание собственного Spring Boot Starter
Пример: Custom Email Starter
Шаг 1: Создаём конфигурацию
// Файл: com/example/mail/MailAutoConfiguration.java
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@Configuration
@ConditionalOnClass(name = "javax.mail.Session")
@EnableConfigurationProperties(MailProperties.class)
public class MailAutoConfiguration {
private final MailProperties mailProperties;
public MailAutoConfiguration(MailProperties mailProperties) {
this.mailProperties = mailProperties;
}
@Bean
@ConditionalOnMissingBean
public MailSender mailSender() {
SimpleMailSender sender = new SimpleMailSender();
sender.setHost(mailProperties.getHost());
sender.setPort(mailProperties.getPort());
sender.setUsername(mailProperties.getUsername());
sender.setPassword(mailProperties.getPassword());
return sender;
}
}
Шаг 2: Создаём properties класс
// Файл: com/example/mail/MailProperties.java
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app.mail")
public class MailProperties {
private String host = "localhost";
private int port = 25;
private String username;
private String password;
// Getters and setters
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Шаг 3: Регистрируем конфигурацию
# Файл: src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mail.MailAutoConfiguration
Шаг 4: Используем в приложении
// application.properties
app.mail.host=smtp.gmail.com
app.mail.port=587
app.mail.username=myemail@gmail.com
app.mail.password=mypassword
@Service
public class EmailService {
private MailSender mailSender;
public EmailService(MailSender mailSender) {
this.mailSender = mailSender; // Auto-configured!
}
public void sendEmail(String to, String subject, String body) {
mailSender.send(to, subject, body);
}
}
Как отключить определённую auto-configuration
Способ 1: application.properties
spring.autoconfigure.exclude=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
Способ 2: Аннотация
@SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Процесс инициализации Spring Boot
public class MyApplication {
public static void main(String[] args) {
// 1. Создаётся SpringApplication
SpringApplication app = new SpringApplication(MyApplication.class);
// 2. Загружаются все enableAutoConfiguration из spring.factories
// 3. Для каждой конфигурации проверяются условия (@ConditionalOn*)
// 4. Если условия выполнены → конфигурация регистрируется
// 5. Создаётся ApplicationContext
// 6. В контексте создаются beans согласно конфигурациям
// 7. Запускается приложение
ApplicationContext context = app.run(args);
}
}
Debugging auto-configurations
Способ 1: Логирование
# application.properties
logging.level.org.springframework.boot.autoconfigure=DEBUG
logging.level.org.springframework.boot.autoconfigure.condition=DEBUG
Способ 2: Actuator endpoint
# Зависит от spring-boot-starter-actuator
curl http://localhost:8080/actuator/conditions
# Выведет информацию о всех auto-configurations:
# - какие загружены
# - какие пропущены и почему
Способ 3: Программно
@Component
public class ConfigurationLogger {
@Autowired
private ConditionEvaluationReport conditionEvaluationReport;
@PostConstruct
public void logConfigurations() {
conditionEvaluationReport.getMatched().forEach(record -> {
System.out.println("Loaded: " + record.getSourceClassName());
});
conditionEvaluationReport.getUnmatched().forEach(record -> {
System.out.println("Skipped: " + record.getSourceClassName());
record.getConditions().forEach(condition -> {
System.out.println(" Reason: " + condition.getCondition() + " - " + condition.getOutcome());
});
});
}
}
Порядок загрузки beans
1. Spring Boot сканирует META-INF/spring.factories
└─ Находит все EnableAutoConfiguration классы
2. Для каждого класса проверяет @ConditionalOn*
├─ @ConditionalOnClass — есть ли класс в classpath
├─ @ConditionalOnBean — есть ли bean в контексте
├─ @ConditionalOnProperty — есть ли свойство в конфиге
└─ ... и т.д.
3. Если условия выполнены:
├─ Регистрирует конфигурацию
├─ Создаёт beans из этой конфигурации
└─ Применяет @AutoConfigureAfter/@AutoConfigureBefore
4. Контекст готов к работе
Best Practices
-
Используй @ConditionalOnMissingBean для гибкости:
@Bean @ConditionalOnMissingBean public ObjectMapper objectMapper() { ... } // Пользователь может переопределить -
Документируй property конфигурации:
@ConfigurationProperties(prefix = "app.mail") public class MailProperties { /** * SMTP host for sending emails */ private String host; } -
Используй @ConditionalOnClass для опциональных зависимостей:
@ConditionalOnClass(name = "javax.servlet.ServletContext") public class WebConfiguration { } -
Тестируй auto-configurations:
@SpringBootTest class MailAutoConfigurationTest { @Test void shouldConfigureMailSender() { ApplicationContextRunner runner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(MailAutoConfiguration.class)) .withPropertyValues("app.mail.host=smtp.example.com"); runner.run(context -> { assertThat(context).hasSingleBean(MailSender.class); }); } }
Вывод
Spring Boot подгружает модули через:
- spring.factories — регистр всех auto-configurations
- Условные аннотации (@ConditionalOn*) — определяют, когда загружать
- Порядок (@AutoConfigureAfter/Before) — контролирует последовательность
- Properties — параметризируют конфигурацию
Этот механизм делает Spring Boot "магическим" и позволяет добавлять зависимости, которые "просто работают" без явной конфигурации.