Принцип автоматической настройки Spring Boot

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

Эта статья участвует"Проект "Звезда раскопок"", чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.

Мы знаем, что после создания проекта Spring Boot он может работать без сбоев даже без какой-либо настройки благодаря автоматической настройке Spring Boot.

Spring Boot по умолчанию использует application.properties или application.yml в качестве глобального файла конфигурации, в котором мы можем настроить различные свойства автоматической конфигурации (server.port, logging.level.*, spring.config.active.no-profile) и т. д. ), чтобы изменить его и заставить его работать, задумывались ли вы когда-нибудь о том, обоснованы ли эти свойства? Ответ положительный.

Официальная документация Spring Boot: общие свойства приложенияВсе свойства конфигурации перечислены и объяснены в , Мы можем настроить Spring Boot в соответствии с официальными документами, но в Spring Boot имеется большое количество свойств конфигурации, и настроить их, опираясь только на официальные документы, очень проблематично. Только когда мы поймем принцип автоматической настройки Spring Boot, мы сможем более легко и эффективно настроить Spirng Boot. Этот раздел раскрывает для вас автоконфигурацию SpringBoot.

Механизм пружинных заводов

Автоматическая конфигурация Spring Boot основана на механизме Spring Factory.

Механизм Spring Factory — это механизм обнаружения служб в Spring Boot, который очень похож на механизм Java SPI. Spring Boot автоматически просканирует файлы META-INF/spring.factories в пути к классам всех пакетов Jar, прочитает содержимое и создаст их экземпляры.Этот механизм также является основой Spring Boot Starter.

spring.factories 

Файл spring.factories по своей природе подобен файлу свойств, который содержит один или несколько наборов пар ключ-значение (key=vlaue), где значением ключа является полное имя интерфейса, а значением — Полное имя класса реализации интерфейса Полное имя Интерфейс может иметь несколько классов реализации Используйте "," для разделения разных классов реализации, например: \

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

Принцип реализации Spring Factory

Класс SpringFactoriesLoader определен в пакете spring-core.Этот класс сканирует файлы META-INF/spring.factories в пути к классам всех пакетов Jar и получает конфигурацию указанного интерфейса. В классе SpringFactoriesLoader определены два внешних метода, как показано в следующей таблице.

возвращаемое значение метод описывать
List  loadFactories(Class factoryType, @Nullable ClassLoader classLoader) Статический метод; получает экземпляр своего класса реализации в соответствии с интерфейсом; этот метод возвращает список объектов класса реализации.
List loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)  Открытый статический метод; получает имя своего класса реализации в соответствии с интерфейсом l; этот метод возвращает список имен классов класса реализации

Ключом к двум вышеупомянутым методам является получение файла spring.factories из указанного ClassLoader и его анализ для получения списка имен классов.Конкретный код выглядит следующим образом. Метод loadFactories() может получить объект класса реализации указанного интерфейса.Конкретный код выглядит следующим образом.

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryType, "'factoryType' must not be null");
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    // 调用loadFactoryNames获取接口的实现类
    List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    if (logger.isTraceEnabled()) {
        logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
    }
    // 遍历 factoryNames 数组,创建实现类的对象
    List<T> result = new ArrayList(factoryImplementationNames.size());
    Iterator var5 = factoryImplementationNames.iterator();
    //排序
    while(var5.hasNext()) {
        String factoryImplementationName = (String)var5.next();
        result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    }

    AnnotationAwareOrderComparator.sort(result);
    return result;
}

Метод loadFactoryNames() может получить коллекцию имен классов своих классов реализации в соответствии с интерфейсом Конкретный код выглядит следующим образом.

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    //获取自动配置类
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

Метод loadSpringFactories() может считывать содержимое конфигурации файла META-INF/spring.factories в пути к классам всех пакетов Jar в проекте и возвращать его в виде коллекции Map.Конкретный код выглядит следующим образом.

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();

    try {
        //扫描所有 Jar 包类路径下的 META-INF/spring.factories 文件
        Enumeration urls = classLoader.getResources("META-INF/spring.factories");

        while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                //将扫描到的 META-INF/spring.factories 文件中内容包装成 properties 对象
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    //提取 properties 对象中的 key 值
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    //提取 proper 对象中的 value 值(多个类的完全限定名使用逗号连接的字符串)
                    // 使用逗号为分隔符转换为数组,数组内每个元素都是配置类的完全限定名
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;
                    //遍历配置类数组,并将数组转换为 list 集合
                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }
            //将 propertise 对象的 key 与由配置类组成的 List 集合一一对应存入名为 result 的 Map 中
            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            //返回 result
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
     }
}

Автоматическая загрузка конфигурации

Автоматическая настройка Spring Boot также реализована на основе механизма Spring Factory.Содержимое автоматической настройки Spring Boot задается в META-INF/spring.factories в пути к классам spring-boot-autoconfigure-xxx.jar следующим образом.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

В приведенной выше конфигурации значение состоит из нескольких xxxAutoConfiguration (разделенных запятыми), каждый xxxAutoConfiguration является классом автоматической конфигурации. Когда Spring Boot запустится, он будет использовать механизм Spring-Factories для создания экземпляров этих xxxAutoConfigurations и добавления их в контейнер в качестве компонентов для реализации автоматической конфигурации Spring Boot.

Аннотация @SpringBootApplication

Аннотация @SpringBootApplication используется в основном начальном классе всех проектов Spring Boot, что является одной из самых важных аннотаций в Spring Boot и ключом к автоматической настройке Spring Boot.  @SpringBootApplication — это составная мета-аннотация, которая в основном содержит две аннотации: @SpringBootConfiguration и @EnableAutoConfiguration, где аннотация @EnableAutoConfiguration является ядром автоматической настройки SpringBoot.

image.png

Аннотация @EnableAutoConfiguration

Аннотация @EnableAutoConfiguration используется для включения функции автоматической настройки Spring Boot.Он использует аннотацию @Import, предоставляемую инфраструктурой Spring, для импорта компонентов автоматической настройки в контейнер через класс (селектор) AutoConfigurationImportSelector.

AutoConfigurationImportSelector 类Рисунок 2: Аннотация @EnableAutoConfiguration

Класс AutoConfigurationImportSelector

Класс AutoConfigurationImportSelector реализует интерфейс DeferredImportSelector, а AutoConfigurationImportSelector также содержит статический внутренний класс AutoConfigurationGroup, который реализует группу внутреннего интерфейса интерфейса DeferredImportSelector (новое в Spring 5). Класс AutoConfigurationImportSelector содержит 3 метода, как показано в следующей таблице.

возвращаемое значение объявление метода описывать метод внутреннего класса внутренний класс
Class<? extends Group> getImportGroup() Этот метод получает класс, реализующий интерфейс Group, и создает его экземпляр. нет  
void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) Этот метод используется для введения автоматически конфигурируемой коллекции да AutoConfigurationGroup
Iterable selectImports() Пройдите коллекцию классов автоконфигурации (коллекцию типа Entry) и проанализируйте классы конфигурации в коллекции один за другим. да

Группа автоконфигурации Порядок выполнения каждого метода в AutoConfigurationImportSelector следующий.

  1. Метод getImportGroup()
  2. метод процесса()
  3. метод selectImports()

Ниже мы представим три вышеупомянутых метода и процесс их вызова соответственно.

1. Метод getImportGroup()

Метод getImportGroup() в классе AutoConfigurationImportSelector в основном используется для получения класса, реализующего интерфейс DeferredImportSelector.Group, код выглядит следующим образом.

    public Class<? extends Group> getImportGroup() {
        //获取实现了 DeferredImportSelector.Gorup 接口的 AutoConfigurationImportSelector.AutoConfigurationGroup 类
        return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
    }

2. метод процесса()

Основным методом статического внутреннего класса AutoConfigurationGroup является process(), который считывает содержимое файла spring.factories, вызывая метод getAutoConfigurationEntry() для получения коллекции классов автоконфигурации. Код выглядит следующим образом.

public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
        return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
    });
    //拿到 META-INF/spring.factories中的EnableAutoConfiguration,并做排除、过滤处理
    //AutoConfigurationEntry里有需要引入配置类和排除掉的配置类,最终只要返回需要配置的配置类
    AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
    //加入缓存,List<AutoConfigurationEntry>类型
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

    while(var4.hasNext()) {
        String importClassName = (String)var4.next();
        //加入缓存,Map<String, AnnotationMetadata>类型
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

Метод getAutoConfigurationEntry() получает полное имя класса автоконфигурации путем вызова метода getCandidateConfigurations() и кэширует его в переменных-членах после исключения, фильтрации и т. д. Конкретный код выглядит следующим образом.

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);
        //删除list 集合中重复的配置类
        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);
    }
}

В методе getCandidateConfigurations() вызовите метод loadFactoryNames() SpringFactoriesLoader в соответствии с механизмом Spring Factory и получите набор имен классов его класса реализации (класс автоконфигурации) в соответствии с EnableAutoConfiguration.class (интерфейс автоконфигурации) , как показано на следующем рисунке.

getCandidateConfigurations 方法

Рисунок 3: метод getCandidateConfigurations

3. метод process() После выполнения всех вышеперечисленных методов AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports() отфильтрует и исключит классы автоматической конфигурации, полученные после обработки методом process(), и, наконец, добавит все классы автоматической конфигурации в контейнер .

public Iterable<DeferredImportSelector.Group.Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    } else {
        //获取所有需要排除的配置类
        Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().
                map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
        //获取所有经过自动化配置过滤器的配置类
        Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.
                AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
        //排除过滤后配置类中需要排除的类
        processedConfigurations.removeAll(allExclusions);
        return (Iterable)this.sortAutoConfigurations(processedConfigurations,
                this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
            return new DeferredImportSelector.Group.Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
        }).collect(Collectors.toList());
    }
}

Проверка и модификация автоматической конфигурации

Все классы автоматической конфигурации (xxxAutoConfiguration) в файле spring.factories должны быть добавлены в контейнер как компоненты при определенных условиях, и содержимое конфигурации вступит в силу. Эти ограничения отражаются в Spring Boot в виде производных аннотаций @Conditional, как показано в следующей таблице.

аннотация Действующие условия
@ConditionalOnJava Вступает в силу, когда приложение использует указанную версию Java
@ConditionalOnBean Вступает в силу, когда указанный bean-компонент существует в контейнере
@ConditionalOnMissingBean  Вступает в силу, когда указанный bean-компонент не существует в контейнере
@ConditionalOnExpression Вступает в силу, когда выполняется указанное выражение SpEL.
@ConditionalOnClass Вступает в силу, когда указанный класс существует
@ConditionalOnMissingClass Вступает в силу, когда указанный класс не существует
@ConditionalOnSingleCandidate  Вступает в силу, когда в контейнере существует только один указанный bean-компонент или этот bean-компонент является предпочтительным bean-компонентом.
@ConditionalOnProperty Вступает в силу, когда указанное свойство существует в системе с указанным значением.
@ConditionalOnResource Вступает в силу, когда указанный файл ресурсов существует в пути к классам
@ConditionalOnWebApplication Вступает в силу, когда текущее приложение является веб-приложением
@ConditionalOnNotWebApplication Текущее приложение не является веб-приложением, чтобы оно вступило в силу.

Давайте возьмем ServletWebServerFactoryAutoConfiguration в качестве примера, чтобы представить, как работает автоматическая настройка Spring Boot.

ServletWebServerFactoryAutoConfiguration 

Код ServletWebServerFactoryAutoConfiguration выглядит следующим образом.

@Configuration(   //表示这是一个配置类,与 xml 配置文件等价,也可以给容器中添加组件

        proxyBeanMethods = false
)
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})//判断当前项目有没有 ServletRequest 这个类
@ConditionalOnWebApplication(// 判断当前应用是否是 web 应用,如果是,当前配置类生效 
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
//启动指定类的属性配置(ConfigurationProperties)功能;将配置文件中对应的值和 ServerProperties 绑定起来;并把 ServerProperties 加入到ioc容器中
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }

    @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties, ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
        return new ServletWebServerFactoryCustomizer(serverProperties, (List) webListenerRegistrars.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    @ConditionalOnClass(
            name = {"org.apache.catalina.startup.Tomcat"}
    )
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnMissingFilterBean({ForwardedHeaderFilter.class})
    @ConditionalOnProperty(
            value = {"server.forward-headers-strategy"},
            havingValue = "framework"
    )
    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
        FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean(filter, new ServletRegistrationBean[0]);
        registration.setDispatcherTypes(DispatcherType.REQUEST, new DispatcherType[]{DispatcherType.ASYNC, DispatcherType.ERROR});
        registration.setOrder(-2147483648);
        return registration;
    }

    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        private ConfigurableListableBeanFactory beanFactory;

        public BeanPostProcessorsRegistrar() {
        }

        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }

        }

        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (this.beanFactory != null) {
                this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class, WebServerFactoryCustomizerBeanPostProcessor::new);
                this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
            }
        }

        private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<T> beanClass, Supplier<T> instanceSupplier) {
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }

        }
    }
}

В классе используются следующие аннотации:

  • @Configuration: используется для определения класса конфигурации, который можно использовать для замены файла конфигурации xml в Spring;
  • @Bean: Внутри класса, аннотированного @Configuration, он может содержать один или несколько методов, аннотированных @Bean, для создания Bean-компонента и добавления его в контейнер Spring; эта аннотация эквивалентна файлу конфигурации Spring, имя метода эквивалентно идентификатор или имя атрибута метода, а возвращаемое значение метода эквивалентно атрибуту класса;

В дополнение к аннотациям @Configuration и @Bean класс использует 5 производных аннотаций @Conditional:

  • @ConditionalOnClass({ServletRequest.class}): Определите, существует ли класс ServletRequest в текущем проекте. Если он существует, класс конфигурации вступит в силу.
  • @ConditionalOnWebApplication(type = Type.SERVLET): определяет, является ли текущее приложение веб-приложением, и если да, то вступает в силу текущий класс конфигурации.
  • @ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"}): определите, существует ли класс Tomcat, если он существует, метод вступит в силу.
  • @ConditionalOnMissingFilterBean({ForwardedHeaderFilter.class}): определите, есть ли в контейнере фильтр ForwardedHeaderFilter, если нет, метод вступит в силу.
  • @ConditionalOnProperty(value = {"server.forward-headers-strategy"},havingValue = "framework"): определите, существует ли server.forward-headers-strategy = framework в файле конфигурации, если нет, метод вступит в силу.

ServerProperties 

Класс ServletWebServerFactoryAutoConfiguration также использует аннотацию @EnableConfigurationProperties, с помощью которой импортируется класс ServerProperties, и часть его исходного кода выглядит следующим образом.

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private ServerProperties.ForwardHeadersStrategy forwardHeadersStrategy;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Shutdown shutdown;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression;
    @NestedConfigurationProperty
    private final Http2 http2;
    private final ServerProperties.Servlet servlet;
    private final ServerProperties.Tomcat tomcat;
    private final ServerProperties.Jetty jetty;
    private final ServerProperties.Netty netty;
    private final ServerProperties.Undertow undertow;

    public ServerProperties() {
        this.shutdown = Shutdown.IMMEDIATE;
        this.compression = new Compression();
        this.http2 = new Http2();
        this.servlet = new ServerProperties.Servlet();
        this.tomcat = new ServerProperties.Tomcat();
        this.jetty = new ServerProperties.Jetty();
        this.netty = new ServerProperties.Netty();
        this.undertow = new ServerProperties.Undertow();
    }
    ....
}

Мы видим, что ServletWebServerFactoryAutoConfiguration использует аннотацию @EnableConfigurationProperties, а класс ServerProperties использует аннотацию @ConfigurationProperties. На самом деле это обычное использование в механизме автоконфигурации Spring Boot. Spring Boot предоставляет нам большое количество классов автоконфигурации XxxAutoConfiguration и XxxProperties, каждый класс автоконфигурации XxxAutoConfiguration использует аннотацию @EnableConfigurationProperties, а каждый XxxProperties использует аннотацию @ConfigurationProperties. Функция аннотации @ConfigurationProperties состоит в том, чтобы связать все свойства этого класса с соответствующей конфигурацией в файле конфигурации, чтобы получить или изменить конфигурацию, но функция @ConfigurationProperties предоставляется контейнером, а класс, аннотированный он должен находиться в контейнере A компонента , иначе эта функция будет недоступна. Функция аннотации @EnableConfigurationProperties — внедрить указанный класс в IOC-контейнер в виде компонента и включить его функцию @ConfigurationProperties. Следовательно, комбинация @ConfigurationProperties + @EnableConfigurationProperties может реализовать функцию привязки конфигурации для класса XxxProperties. Класс автоматической конфигурации XxxAutoConfiguration отвечает за использование свойств в XxxProperties для автоматической настройки, а XxxProperties отвечает за привязку свойств автоматической конфигурации к соответствующей конфигурации файла конфигурации, чтобы пользователи могли изменять автоматическую конфигурацию по умолчанию через файл конфигурации. То есть классы, которые действительно «ограничивают» то, какие свойства мы можем настроить в файле конфигурации, — это классы XxxxProperties, которые однозначно соответствуют набору свойств, определенных в файле конфигурации, начиная с ключевого слова prefix.

Примечание. XxxAutoConfiguration и XxxProperties не находятся во взаимно однозначном соответствии, в большинстве случаев существует связь «многие ко многим», то есть один XxxAutoConfiguration может использовать свойства в нескольких XxxProperties одновременно, а свойства в один класс XxxProperties также может использоваться несколькими XxxAutoConfigurations.