чтение урожая
👍🏻Понять принцип автоматической настройки SpringBoot
1. Что такое SpringBoot
SpringBoot
родился, чтобы упроститьSpring
громоздкийXML
Конфигурация, ее суть по-прежнему фреймворк Spring.После использования SpringBoot можно запускать службу без использования какой-либо XML-конфигурации, чтобы мы могли быстрее построить приложение при использовании микросервисной архитектуры.
Проще говоря, SpringBoot — это не новый фреймворк, он по умолчанию настраивает множество способов использования фреймворка.
Во-вторых, характеристики SpringBoot
- Фиксированные конфигурации предоставляются для упрощения конфигурации, т.е.
约定大于配置
- Автоматически настройте Spring и сторонние библиотеки, насколько это возможно, т.е.
自动装配
- Встроенный контейнер для создания автономных приложений Spring.
- Упростите тестирование с помощью встроенных JUnit, Spring Boot Test и других сред тестирования, упрощающих тестирование.
- Предоставляет готовые к работе функции, такие как метрики, проверки работоспособности и внешняя конфигурация.
- Генерация кода вообще не требуется, не требуется настройка XML.
3. Стартовый класс
Давайте рассмотрим принцип запуска SpringBoot, не буду вдаваться в некоторые детали, остановимся только на анализе основной линии.
Уведомление:Версия этой статьи для SpringBoot — 2.6.1.
3.1 @SpringBootApplication
Все происходит из класса запуска, происходящего из SpringBoot, и мы обнаруживаем, что есть аннотация к основному методу:@SpringBootApplication
@SpringBootApplication
public class SpringbootWorkApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWorkApplication.class, args);
}
}
@SpringBootApplication
Аннотация к классу указывает на то, что этот класс является основным классом конфигурации SpringBoot, и SpringBoot должен запускать основной метод этого класса для запуска приложения SpringBoot; его сущность представляет собой комбинированную аннотацию, мы нажимаем, чтобы просмотреть метаинформацию этого класса в основном включает 3 Примечание:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
-
@SpringBootConfiguration
(Внутри находится @Configuration, помечающий текущий класс как класс конфигурации, по сути просто делающий слой инкапсуляции и меняющий имя) -
@EnableAutoConfiguration
(Включить автоматическую настройку) -
@ComponentScan
(сканирование пакетов)
Примечание: @Inherited — это идентификатор, используемый для изменения аннотаций.Если класс использует аннотацию, измененную с помощью @Inherited, его подклассы также наследуют эту аннотацию.
Давайте проанализируем функции этих трех аннотаций одну за другой.
3.1.1 @SpringBootConfiguration
давай продолжим@SpringBootConfiguration
Войдите, чтобы просмотреть исходный код следующим образом:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@Configuration
Аннотировано в классе, указывающее, что это Springboot配置类
. Компоненты могут быть введены в контейнер.
3.1.2 @ComponentScan
-
@ComponentScan
: настроить директивы сканирования компонентов для класса Configuration. - обеспечены
Spring XML
из<context:component-scan>
Поддержка параллелизма элементов. - Могу
basePackageClasses
илиbasePackages
чтобы определить конкретные пакеты для сканирования. Если конкретный пакет не определен, он будет получен из класса, в котором объявлена аннотация.包开始扫描
.
3.1.3 @EnableAutoConfiguration
- @EnableAutoConfiguration, как следует из названия:
开启自动导入配置
- Эта аннотация находится в центре внимания SpringBoot, мы подробно объясним ниже.
В-четвертых, @EnableAutoConfiguration
- Нажмите, чтобы увидеть, что содержит аннотация
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自动导包
@Import({AutoConfigurationImportSelector.class}) //自动配置导入选择
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
4.1 @AutoConfigurationPackage
- Автоматически импортировать пакеты конфигурации
- Нажмите, чтобы просмотреть код:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@Import
Для аннотации Spring импортируйте файл конфигурации, импортируйте компонент для контейнера в springboot, а импортированный компонент определяется внутренним классом AutoConfigurationPackages.class.Registrar.class
Выполните логику, чтобы решить, как импортировать.
4.1.1 @Import({Registrar.class})
Щелкните Registrar.class, чтобы просмотреть исходный код следующим образом:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//断点
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
Примечание. Регистратор реализуетImportBeanDefinitionRegistrar
Класс можно импортировать в контейнер Spring, аннотировав @Import.
Это место ломается
беги посмотреть(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])
ценностьcom.ljw.springbootwork
: имя пакета, в котором находится текущий класс запуска.
в заключении:@AutoConfigurationPackage предназначен для сканирования и регистрации всех компонентов в пакете, где основной класс конфигурации (класс, отмеченный @SpringBootApplication) находится в контейнере Spring.
4.2 @Import({AutoConfigurationImportSelector.class})
Роль: AutoConfigurationImportSelector开启自动配置类的导包的选择器
, то есть какие классы вносятся, выборочно ввозятся
Щелкните AutoConfigurationImportSelector.class, чтобы ввести исходный код.В этом классе есть два метода, как следует из названия:
- selectImports: выберите компоненты, которые необходимо импортировать
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
- getAutoConfigurationEntry: возвращает AutoConfigurationImportSelector.AutoConfigurationEntry в соответствии с AnnotationMetadata импортированного класса @Configuration.
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 这打个断点,看看 返回的数据
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//删除重复项
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//检查
this.checkExcludedClasses(configurations, exclusions);
//删除需要排除的依赖
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
this.getCandidateConfigurations(annotationMetadata, атрибуты) точка останова здесьДлина массива конфигураций составляет 133, а все суффиксы файлов
**AutoConfiguration
в заключении:Это все классы конфигурации-кандидаты.После дедупликации необходимые исключенные зависимости удаляются, а конечными компонентами являются все компоненты, требуемые этой средой. При автоматической настройке нам не нужно записывать значение нашей собственной конфигурации, а класс конфигурации имеет значения по умолчанию.
Давайте продолжим смотреть, как вернуть компоненты, которые необходимо настроить.
4.2.1 getCandidateConfigurations(annotationMetadata, attributes)
Методы, как показано ниже:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
Вот утверждение: Assert.notEmpty(configurations, "В META-INF/spring.factories не найдены классы автоматической настройки. Если вы используете пользовательскую упаковку, убедитесь, что файл правильный.");
Означает: «В META-INF/spring.factories не найдены классы автоконфигурации. Если вы используете пользовательскую оболочку, убедитесь, что файл правильный».
в заключении:То есть, если методу loadFactoryNames() необходимо найти возвращаемый класс автоматической конфигурации, он не сообщит об ошибке.
4.2.1.1 getSpringFactoriesLoaderFactoryClass()
Мы щелкнули и обнаружили: что возвращает this.getSpringFactoriesLoaderFactoryClass(), так этоEnableAutoConfiguration.class
эта аннотация. Эта аннотация совпадает с аннотацией маркировки в @SpringBootApplication.
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
в заключении:Получите класс, который может загружать класс автоконфигурации, то есть класс автоконфигурации SpringBoot по умолчанию — EnableAutoConfiguration.
4.2.2 SpringFactoriesLoader
Механизм загрузки фабрик SpringFactoriesLoader — это обычный метод загрузки, предоставляемый Spring.Он должен быть только в файле META-INF/spring.factories модуля.Ключом в этом файле формата свойств является полное имя интерфейса, аннотация, или абстрактный класс, значение. Это классы реализации, разделенные запятыми ",", и использует SpringFactoriesLoader для внедрения соответствующих классов реализации в контейнер Spirng.
Примечание: загрузится所有jar包
META-INF/spring.factories по пути к классам по пути, чтобы было более одного файла.
4.2.2.1 loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
Точка останова для просмотра factoryTypeName:
первыйEnableAutoConfiguration.class
перешел кfactoryType
потомString factoryTypeName = factoryType.getName();
,такfactoryTypeName
значение org.springframework.boot.autoconfigure.EnableAutoConfiguration
4.2.2.2 loadSpringFactories()
Затем посмотрите на роль метода loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
//断点查看
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//注意这里:META-INF/spring.factories
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
//断点
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
//去重,断点查看result值
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
Здесь FACTORIES_RESOURCE_LOCATION определено выше: META-INF/spring.factories
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Где находится файл META-INF/spring.factories? ?
Будет прочитан файл META-INF/spring.factories в текущем пути к классам всех импортированных пакетов Java, например:
Значение результата просмотра точки останова выглядит следующим образом:
Функция этого метода состоит в том, чтобы загрузить все файлы зависимого пути META-INF/spring.factories и сохранить их через структуру карты.Ключом являются некоторые идентификационные фабричные классы, определенные в файле, а значением является класс, реализованный некоторыми фабриками. которые могут быть настроены автоматически.Значение сохраняется в списке.и идти тяжело.
оглядываясь назадloadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
так какloadFactoryNames
Перенесен первый параметрEnableAutoConfiguration.class
,такfactoryType
значение такжеEnableAutoConfiguration.class
,ТакfactoryTypeName
значениеEnableAutoConfiguration
. Полученное значение состоит в том, что ключ в файле META-INF/spring.factories
Значение org.springframework.boot.autoconfigure.EnableAutoConfiguration
getOrDefault
когда Map
Когда в коллекции есть этот ключ, используйте это значение ключа, если нет, используйте пустой массив значений по умолчанию.
в заключении:
- loadSpringFactories() Этот метод предназначен для загрузки полного имени класса реализации фабрики данного типа из "META-INF/spring.factories" и помещения его на карту.
- loadFactoryNames() основан на процессе запуска SpringBoot.Когда необходимо загрузить класс автоматической конфигурации, передается параметр org.springframework.boot.autoconfigure.EnableAutoConfiguration, а ключом является org.springframework.boot.autoconfigure. EnableAutoConfiguration из карты.Значения, которые добавляются в контейнер посредством отражения, а затем используются для автоматической настройки, с чего начинается автоматическая настройка Springboot.
- Только после того, как эти классы автоматической конфигурации войдут в контейнер, начнет запускаться следующий класс автоматической конфигурации.
- Когда требуются другие конфигурации, такие как конфигурация, связанная с мониторингом: прослушиватель, для получения соответствующей конфигурации прослушивателя передаются различные параметры.
5. Сводная диаграмма процесса
Шесть часто используемых условных аннотаций
-
При загрузке класса автоматической конфигурации загружаются не все конфигурации spring.factories, а динамически загружаются с учетом аннотаций, таких как @Conditional
-
@Conditional на самом деле является аннотацией нижнего уровня spring, что означает оценку различных условий в соответствии с разными условиями.Если указанные условия выполняются, то конфигурация в классе конфигурации вступит в силу.
-
Общие условные аннотации:
- @ConditionalOnClass: вступает в силу, когда класс существует в пути к классам.
- @ConditionalOnMissingClass: вступает в силу, когда класс не существует в пути к классам.
- @ConditionalOnBean: вступает в силу, когда этот тип bean-компонента существует в контейнере DI.
- @ConditionalOnMissingBean: вступает в силу, когда тип bean-компонента не существует в контейнере DI.
- @ConditionalOnSingleCandidate: вступает в силу, когда в контейнере DI есть только один bean-компонент этого типа или есть только один @Primary
- @ConditionalOnExpression : когда выражение SpEL оценивается как истинное
- @ConditionalOnProperty: вступает в силу, когда настройки или значения параметров согласованы.
- @ConditionalOnResource: вступает в силу, когда указанный файл существует.
- @ConditionalOnJndi: вступает в силу, когда указанный JNDI существует.
- @ConditionalOnJava: вступает в силу, когда указанная версия Java существует.
- @ConditionalOnWebApplication: эффективно в среде веб-приложений.
- @ConditionalOnNotWebApplication: действует в среде, отличной от веб-приложений.
Семь, @Import поддерживает три способа импорта
- Класс конфигурации с @Configuration
- Реализация ImportSelector
- Реализация ImportBeanDefinitionRegistrar
- 👍🏻: Если у вас есть прибыль, пожалуйста, поставьте лайк и поддержите!
- ❤️: Любимые статьи для удобного просмотра!
- 💬: Обменивайтесь комментариями и развивайтесь друг с другом!