Автоконфигурация Spring Boot Starter — очень мощная вещь, а также важная и умная конструкция. Но если вы не понимаете, как это работает, может возникнуть путаница при определении того, какая автоматическая настройка будет включена. Давайте посмотрим на это с точки зрения исходного кода.
Начало работы: концепции автоконфигурации
Автоконфигурация и конфигурация Java Spring — это не одно и то же. Подробнее см. определение в документации Spring Boot.
Часть 0: процесс запуска приложения Spring Boot
ВидетьСправочная статья.
Часть 1: Знакомство с spring.factories
Начните с @SpringBootApplication
@SpringBootApplication
Аннотации определяются следующим образом:
// 前面略
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
Первый взгляд@ComponentScan
раздел, используяAutoConfigurationExcludeFilter
В качестве фильтра исключения сканирование пакетов буквально исключает автоматически сконфигурированные пакеты. Таким образом, если Spring считает класс автоматически сконфигурированным, он не будет сканировать его (для создания объявленных bean-компонентов). Конкретная логика далее обсуждаться не будет.
Дело в том, что@EnableAutoConfiguration
Эта аннотация:
// 省略不重要的内容
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
обрати внимание на@Import(AutoConfigurationImportSelector.class)
Это предложение.@Import
Мы все с ним знакомы, он в конфигурации XML<import>
заменять. тем не мениеAutoConfigurationImportSelector
Этот класс не@Configuration
Модификатор, а не класс конфигурации Java. Так что это странно.
При просмотре исходного кода вы должны сначала просмотреть комментарии.
Посмотрим@Import
Что показывают комментарии:
Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes
Привет! Оказывается, он может не только импортировать классы конфигурации Java, но и обрабатыватьImportSelector
иImportBeanDefinitionRegistrar
.
В центре внимания этого разделаImportSelector
Перед этим также необходимо упомянуть@AutoConfigurationPackage
:
/**
* Indicates that the package containing the annotated class should be registered with
* {@link AutoConfigurationPackages}.
*/
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
иAutoConfigurationPackages.Registrar
ровно одинImportBeanDefinitionRegistrar
, она имеетregisterBeanDefinitions
Метод интерфейса может напрямую зарегистрировать Bean. Позже (расширенное чтение) вы можете увидеть, что,ImportBeanDefinitionRegistrar
Что более важно, это герой, стоящий за различными аннотациями «@EnableXX».
Хорошо, посмотри еще разImportSelector
интерфейс:
// 省略不重要注释
/**
* Interface that determine which @{@link Configuration} class(es) should be imported
* based on a given selection criteria, usually one or more annotation attributes.
*/
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
Поэтому он используется для определения@Configuration
Декорированный класс конфигурации Java, конфигурация которого должна быть импортирована. На основе всей аннотационной информации этого класса конфигурации Java (хранящейся вAnnotationMetadata
в).
Хорошо, следующий шаг, давайте посмотримAutoConfigurationImportSelector
Как добиться.
Класс AutoConfigurationImportSelector
такая параImportSelector
Реализация интерфейса следующая:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
один из нихgetAutoConfigurationEntry
метод вызывается сноваgetCandidateConfigurations
метод, заключающийся в следующем:
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader}
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
Итак, чтобы добраться сюда, он начнет с пакетаMETA-INF/spring.factories
Прочитайте классы автоконфигурации, которые необходимо импортировать из файла. Более подробный код ниже уже не выложен, его можно посмотреть в деталяхSpringFactoriesLoader
этот класс.
Следующий вопрос,selectImports
Кем это было вызвано? Честно говоря, ссылка звонка слишком глубокая, и в ней легко запутаться. Ниже кратко описан основной процесс.
Часть 2: процесс загрузки класса конфигурации Java Spring
Начните с ConfigurationClassPostProcessor
В качестве постпроцессора Spring функция этого класса заключается в обработке классов конфигурации Java, украшенных @Configuration. Сосредоточьтесь на его основном подходе:
// 省略非关键代码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
}
Ключевая логика заключается в вызовеConfigurationClassParser
Категорияparse
метод, а затем анализирует всеConfigurationClass
, анализирует и загружает внутренне определенные bean-компоненты.
ConfigurationClassParser#parse()
Метод parse() в основном вызываетсяprocessConfigurationClass
метод.
// 省略非核心代码
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
Этот метод делает три вещи:
- Определите, следует ли пропустить этот класс Java Config на этапе PARSE_CONFIGURATION (в соответствии с условным суждением).
- перечислить
doProcessConfigurationClass()
сделать конкретный анализ - Поместите класс конфигурации для параметров метода в
configurationClasses
середина
И второй шагdoProcessConfigurationClass()
Что такое конкретный анализ? На самом деле это рекурсивный обход различных @Imports, внутренних классов, объявленных @ComponentScan и т. д. текущего класса конфигурации Java.parse()
метод (илиprocessConfigurationClass()
метод), разрешите этот класс. Конечным результатом является добавление всех классов конфигурации Java, которые можно найти.configurationClasses
в контейнере.
Часть 3: снова посмотрите на процесс загрузки @SpringBootApplication
Поскольку @SpringBootApplication на самом деле является классом конфигурации Java с @Configuration, из части II известно, что он также будет анализироваться таким же образом.
И аннотации, содержащиеся в @SpringBootApplication, наконец, импортированы.AutoConfigurationImportSelector
класс, поэтому этот класс будет называться. Ниже приведен вызов этого классаgetCandidateConfigurations
Скриншот результата при использовании метода (упомянутого в первой части):
На этом этапе все классы автоконфигурации, определенные в Spring Boot Starter, будут просканированы и проанализированы.
Добавлено: реализация @Conditional
В части 2 упоминалось, чтоprocessConfigurationClass
Первая строка этого метода — определить, выполняется ли условное условие. Когда вам нужно отладить, почему автоматическая настройка действует/не действует, вы можете сосредоточиться на этом.
Суммировать:
@Import используется для импорта классов конфигурации, а методы импорта в основном делятся на следующие три типа.
- Непосредственно импортируйте класс конфигурации, будьте
@Configuration
Модифицированный класс. -
ImportSelector
Класс реализации интерфейса возвращает массив имен классов конфигурации, а затем импортирует эти классы конфигурации. -
ImportBeanDefinitionRegistar
Класс реализации интерфейса, зарегистрировать бин непосредственно в методе интерфейса.
ImportSelector
Класс реализации интерфейсаAutoConfigurationImportSelector
Затем контракт с каждым стартером в ClassPathMETA-INF/spring.factories
Работа по чтению классов автоконфигурации, которые необходимо импортировать из файла.
@SpringBootApplication
Аннотации наследуются косвенноAutoConfigurationImportSelector
функция.
В дальнейшем чтении вы сможете увидеть, как работают различные аннотации @Enable*.
использованная литература
- Глава 14 "Глубокого анализа исходного кода Spring", второе издание
- Документация по весенней загрузке
- Дальнейшее чтение:Таким образом, вы сможете понять принцип автоматической настройки SpringBoot.
- Дальнейшее чтение:Как работают аннотации SpringBoot @Enable*
- Дальнейшее чтение:Подробное объяснение использования методов и интерфейсов ловушек Spring.