Разберитесь с принципом автоматической настройки SpringBoot в одной статье

Spring Boot Java задняя часть
Разберитесь с принципом автоматической настройки SpringBoot в одной статье

чтение урожая

👍🏻Понять принцип автоматической настройки 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.

Это место ломаетсяimage.png

беги посмотреть(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])ценностьcom.ljw.springbootwork: имя пакета, в котором находится текущий класс запуска.

в заключении:@AutoConfigurationPackage предназначен для сканирования и регистрации всех компонентов в пакете, где основной класс конфигурации (класс, отмеченный @SpringBootApplication) находится в контейнере Spring.

4.2 @Import({AutoConfigurationImportSelector.class})

Роль: AutoConfigurationImportSelector开启自动配置类的导包的选择器, то есть какие классы вносятся, выборочно ввозятся

Щелкните AutoConfigurationImportSelector.class, чтобы ввести исходный код.В этом классе есть два метода, как следует из названия:

  1. 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());
    }
}
  1. 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, атрибуты) точка останова здесьimage.pngДлина массива конфигураций составляет 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:image.png

первый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, например:image.png

Значение результата просмотра точки останова выглядит следующим образом:image.png

Функция этого метода состоит в том, чтобы загрузить все файлы зависимого пути META-INF/spring.factories и сохранить их через структуру карты.Ключом являются некоторые идентификационные фабричные классы, определенные в файле, а значением является класс, реализованный некоторыми фабриками. которые могут быть настроены автоматически.Значение сохраняется в списке.и идти тяжело.

image.png

оглядываясь назадloadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());

так какloadFactoryNamesПеренесен первый параметрEnableAutoConfiguration.class,такfactoryTypeзначение такжеEnableAutoConfiguration.class,ТакfactoryTypeNameзначениеEnableAutoConfiguration. Полученное значение состоит в том, что ключ в файле META-INF/spring.factories Значение org.springframework.boot.autoconfigure.EnableAutoConfiguration

image.png

getOrDefault когда MapКогда в коллекции есть этот ключ, используйте это значение ключа, если нет, используйте пустой массив значений по умолчанию.

в заключении:

  • loadSpringFactories() Этот метод предназначен для загрузки полного имени класса реализации фабрики данного типа из "META-INF/spring.factories" и помещения его на карту.
  • loadFactoryNames() основан на процессе запуска SpringBoot.Когда необходимо загрузить класс автоматической конфигурации, передается параметр org.springframework.boot.autoconfigure.EnableAutoConfiguration, а ключом является org.springframework.boot.autoconfigure. EnableAutoConfiguration из карты.Значения, которые добавляются в контейнер посредством отражения, а затем используются для автоматической настройки, с чего начинается автоматическая настройка Springboot.
  • Только после того, как эти классы автоматической конфигурации войдут в контейнер, начнет запускаться следующий класс автоматической конфигурации.
  • Когда требуются другие конфигурации, такие как конфигурация, связанная с мониторингом: прослушиватель, для получения соответствующей конфигурации прослушивателя передаются различные параметры.

5. Сводная диаграмма процесса

SpringBoot自动配置流程.png

Шесть часто используемых условных аннотаций

  • При загрузке класса автоматической конфигурации загружаются не все конфигурации 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 поддерживает три способа импорта

  1. Класс конфигурации с @Configuration
  2. Реализация ImportSelector
  3. Реализация ImportBeanDefinitionRegistrar
  • 👍🏻: Если у вас есть прибыль, пожалуйста, поставьте лайк и поддержите!
  • ❤️: Любимые статьи для удобного просмотра!
  • 💬: Обменивайтесь комментариями и развивайтесь друг с другом!