ホームページ > Java > &#&チュートリアル > SpringBootのスタートアップコードと自動アセンブリのソースコードとは何ですか?

SpringBootのスタートアップコードと自動アセンブリのソースコードとは何ですか?

WBOY
リリース: 2023-05-11 19:25:04
転載
754 人が閲覧しました

インターネットの急速な発展に伴い、さまざまなコンポーネントが際限なく出現し、フレームワークの統合が必要なコンポーネントがますます増えています。各コンポーネントは、Spring コンテナと統合するために関連するコードを実装する必要があります。 SpringMVC フレームワークの設定は非常に煩雑であり、XML ファイルに依存しています。サードパーティ コンポーネントの迅速な統合を促進し、設定ファイルへの依存を減らすために、開発者が必要としない設定よりも規約の理論を採用した SpringBoot が登場しました。開発用に設定しすぎます。 SpringBoot の下部で使用される Spring は、デフォルトで N 個の複数のコンポーネントの自動アセンブリを統合します。 SpringBoot の使用は簡単で、メイン クラスに @SpringBootApplication を追加し、SpringApplication.run() を呼び出してメイン クラスに渡します。コードは次のとおりです。

@SpringBootApplication
public class StartApp {
    public static void main(String[] args) {
        SpringApplication.run(StartApp.class);
    }
}
ログイン後にコピー

上記のソースコードからわかるように、SpringApplication.run() が SpringBoot のプログラムの入り口です。この記事では、SpringApplication.run() と @SpringBootApplication アノテーションの 2 つの側面からこれを分析します。

1. SpringBoot スタートアップ コードのメインライン分析

SpringApplication.run(StartApp.class) の重要なコードは、最初に SpringApplication クラスを作成し、次に run メソッドを実行することです。コードは次のとおりです,

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}
ログイン後にコピー

1. SpringApplication の構築メソッドのコードは次のとおりです

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 设置资源加载器
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置应用主配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 获取web服务器类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 从spring.factories 文件中获取 ApplicationContextInitializer 的实现类
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 从spring.factories 文件中获取 ApplicationListener 监听器的实现类
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 设置main启动类
    this.mainApplicationClass = deduceMainApplicationClass();
}
ログイン後にコピー

構築メソッドのメイン ロジック:

1. メインを設定しますアプリケーションの構成クラス。後で run メソッド内で使用されます。これは BeanDefinitionHolder にカプセル化され、コンテキストのレジストリにロードされます。

2. Web サーバーの種類を取得します。これは、後の run メソッドで特定の Web サービスの種類を作成するために使用されます。

3. spring.factories ファイルから ApplicationContextInitializer の実装クラスを取得し、SpringApplication インスタンスに設定します

4. spring.factories ファイルから ApplicationListener リスナーの実装クラスを取得し、 SpringApplication インスタンスに設定します

5. メインの起動クラスを設定します

getSpringFactoriesInstances メソッドの主なロジックは次のとおりです: META-INF/spring.factories ファイルから特定の実装クラス文字列を取得します。インターフェイスに文字列 Be インスタンス化をオブジェクトとして配置します。コードは次のとおりです:

// 获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 根据type 从META-INF/spring.factories获取 具体的实现类字符串列表
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化具体的实现类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
ログイン後にコピー

META-INF/spring.factories ファイルの ApplicationContextInitializer.class に対応する実装クラス文字列は、META-INF/spring の

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
ログイン後にコピー

ApplicationListener. .factories ファイル。クラスに対応する実装クラス文字列は、

org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
ログイン後にコピー

2 です。run メソッドのコードは次のとおりです。

StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置了一个名为 java.awt.headless 的系统属性
// 其实是想设计应用程序,即使没有检测到显示器,也允许其启动
// 对于服务器来说,是不需要显示器的 ,所以要这样设置
configureHeadlessProperty();
// 获取 SpringApplicationRunListener 加载的是 EventPublishingRunListener
// 获取启动时的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发启动事件
listeners.starting();
try {
    // 构造一个应用程序的参数持有类
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 创建并配置环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    // 配置需要忽略的BeanInfo信息
    configureIgnoreBeanInfo(environment);
    Banner printedBanner = printBanner(environment);
    // 创建上下文对象
    context = createApplicationContext();
    // 加载配置的启动异常处理器
    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                     new Class[] { ConfigurableApplicationContext.class }, context);
    // 刷新前操作
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    // 刷新应用上下文 完成 Spring 容器的初始化
    refreshContext(context);
    // 刷新后操作
    afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    }
    // 启动完成事件
    listeners.started(context);
    // 执行 ApplicationRunner 和 CommandLineRunner 实现类
    callRunners(context, applicationArguments);
}
catch (Throwable ex) {
    // 事件广播启动出错了
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
}

try {
    // 运行事件
    listeners.running(context);
}
catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, null);
    throw new IllegalStateException(ex);
}
return context;
ログイン後にコピー

run メソッドのメイン ロジックは次のとおりです。

1. spring.factories ファイルから SpringApplicationRunListener (リスニング イベント パブリッシャー) の実装クラスを取得し、起動イベントや起動完了イベントなどの関連イベントをコンテキスト ライフ サイクルで実行します。

2. Web アプリケーション コンテキスト オブジェクトを作成し、webApplicationType に基づいて特定の Web サービス タイプを作成します。

3. 更新する前に、メイン構成クラスのリソースを BeanDefinitionHolder にカプセル化し、それをコンテキスト レジストリにロードします。

4. アプリケーション コンテキストを更新して、Spring コンテナの初期化を完了します。

5. ApplicationRunner インターフェイスと CommandLineRunner インターフェイスを実装する実行クラス。

2. SpringBoot 自動アセンブリ原理の分析

1. 自動アセンブリの予備知識@Import

@SpringBootApplication アノテーション @Import アノテーションの主な用途、@Import ソースコードは次のとおりです:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    /**
	 * {@link Configuration @Configuration}, {@link ImportSelector},
	 * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
	 */
    Class<?>[] value();
}
ログイン後にコピー

@Import アノテーションは通常 @Configuration と一緒に使用されます。@Configuration アノテーション クラスは Spring コンテナの初期化プロセス中に解析されます (ソース コードは org.springframework.context にあります) .annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions) プロセスは、アノテーションが付けられたクラスの @Import アノテーションのメタデータを解析し、クラスが関連するインターフェイスを実装しているかどうかに基づいて処理します。ソースコードの場所: org.springframework.context.annotation.ConfigurationClassParser#processImports; キーコードは次のとおりです,

try {
    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                                                           this.environment, this.resourceLoader, this.registry);
            Predicate<String> selectorFilter = selector.getExclusionFilter();
            if (selectorFilter != null) {
                exclusionFilter = exclusionFilter.or(selectorFilter);
            }
            if (selector instanceof DeferredImportSelector) {
                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
            }
            else {
                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
            }
        }
        else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
            // Candidate class is an ImportBeanDefinitionRegistrar ->
            // delegate to it to register additional bean definitions
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                                     this.environment, this.resourceLoader, this.registry);
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            this.importStack.registerImport(
                currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}
ログイン後にコピー

上記のコードから、@ の値クラスを使用するには 3 つのシナリオがあることがわかります。インポート:

1. ImportSelector.class インターフェースの実装シナリオ。インスタンス セレクターの selectImports メソッドを直接呼び出して、インスタンス化する Bean オブジェクトの完全なクラス名のリストを返し、それに基づいてインスタンス オブジェクトを作成します。完全なクラス名の文字列リストを取得し、現在の processImports メソッドを再帰的に呼び出します。このメソッドは最終的にconfigurationClasses コレクションに追加され、configurationClasses コレクション内のオブジェクトは BeanDefinitionRegistry タイプのレジストリ オブジェクトに登録されます。この場合、BeanDefinition の遅延インジェクション機能を実装するための ImportSelector インターフェースが実装され、DeferredImportSelector インターフェースが拡張されます。 DeferredImportSelector インターフェイスは、ImportSelector インターフェイスを拡張し、内部インターフェイス Group を持っています。@Import アノテーションが付けられた値クラスが DeferredImportSelector インターフェイスを実装し、インターフェイスの内部クラス Group インターフェイスも実装する場合、この実装クラスは遅延する必要があるように見えます。遅延処理が必要な場合、ImportSelector インスタンス セレクターは DeferredImportSelectorHolder オブジェクトにアセンブルされ、deferredImportSelectors コレクションに追加されます。処理ロジックのソース コードの場所: org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle; キー コードは次のとおりです。

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
    if (this.deferredImportSelectors == null) {
        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
        handler.register(holder);
        handler.processGroupImports();
    }
    else {
        this.deferredImportSelectors.add(holder);
    }
}
ログイン後にコピー

DeferredImportSelector インターフェースの実装ロジックは org.springframework.context.annotation.ConfigurationClassParser#parse メソッドで呼び出されます。具体的なコードは this.deferredImportSelectorHandler.process() にあります。キー コード以下のとおりであります、 ###
public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            // 具体的执行逻辑
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}
ログイン後にコピー

在processGroupImports()方法中,先通过grouping.getImports()拿到需要自动装配的Group.Entry(封装了全类名)对象集合,然后通过processImports()方法根据Entry类名字符串进行创建SourceClass类(该类可以通过asConfigClass()方法转成ConfigurationClass对象),最终添加到configurationClasses集合中。代码如下,

public void processGroupImports() {
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            try {
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                               Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                               exclusionFilter, false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configurationClass.getMetadata().getClassName() + "]", ex);
            }
        });
    }
}
ログイン後にコピー

grouping.getImports()方法中主要执行具体的实现类的process方法和selectImports()方法(如果是AutoConfigurationImportSelector类,则调用org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process和org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports,两个方法的具体类容请看2.2.2章节的说明),selectImports返回需要自动装配的Group.Entry对象集合,Entry对象中保存了全类名。代码如下:

public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                           deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}
ログイン後にコピー

ImportSelector接口代码代码如下:

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
	@Nullable
	default Predicate<String> getExclusionFilter() {
		return null;
	}
}
ログイン後にコピー

DeferredImportSelector接口的代码如下:

public interface DeferredImportSelector extends ImportSelector {

    @Nullable
    default Class<? extends Group> getImportGroup() {
        return null;
    }
    interface Group {
        void process(AnnotationMetadata metadata, DeferredImportSelector selector);
        Iterable<Entry> selectImports();
        class Entry {
			// 省略
        }
    }
}
ログイン後にコピー

2.实现了 ImportBeanDefinitionRegistrar.class接口的场景;会先创建ImportBeanDefinitionRegistrar 实例类 registrar,再把 registrar 添加到 configClass 的 importBeanDefinitionRegistrars中,接口的registerBeanDefinitions方法的调用是在 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法里的this.reader.loadBeanDefinitions(configClasses)代码中。具体执行语句loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());关键代码如下,

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
                       registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
ログイン後にコピー

ImportBeanDefinitionRegistrar接口代码如下:

public interface ImportBeanDefinitionRegistrar {

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
                                         BeanNameGenerator importBeanNameGenerator) {
        registerBeanDefinitions(importingClassMetadata, registry);
    }

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}
ログイン後にコピー

3.没有实现以上两接口的普通类,会直接调用org.springframework.context.annotation.ConfigurationClassParser#processImports里面的processConfigurationClass方法,把当前configClass添加至 configurationClasses 集合中。configurationClasses集合中的对象最终会被注册到BeanDefinitionRegistry类型的 registry 对象中。

2.@SpringApplication注解分析

@SpringApplication注解主要包括了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。代码如下,

@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) })
ログイン後にコピー
2.1@SpringBootConfiguration

配置注解,包含了@Configuration注解,表明是配置类。

2.2@EnableAutoConfiguration

自动装配注解,主要逻辑是:根据 EnableAutoConfiguration 类型从META-INF/spring.factories 文件加载需要自动装配的类,并注入到Spring容器中。它包括了@AutoConfigurationPackage注解和一个@Import(AutoConfigurationImportSelector.class)注解。代码如下,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
ログイン後にコピー
2.2.1@AutoConfigurationPackage

注册名为 org.springframework.boot.autoconfigure.AutoConfigurationPackages ,BeanClass为BasePackages.class 的GenericBeanDefinition 到 BeanDefinitionRegistry 中,通过@Import 注解实现注入功能,代码如下,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
ログイン後にコピー

AutoConfigurationPackages.Registrar.class实现了ImportBeanDefinitionRegistrar接口 ,所以在Spring容器初始化的过程中会调用它的registerBeanDefinitions方法把PackageImport类注入到Spring容器中去。代码如下,

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImport(metadata).getPackageName());
    }
    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImport(metadata));
    }
}
ログイン後にコピー
2.2.2@Import(AutoConfigurationImportSelector.class)

自动装配关键逻辑,先从META-INF/spring.factories 文件加载类型值为 EnableAutoConfiguration的字符串集合,再通过过滤,生成需要自动装配的类,最后注入到Spring容器中。AutoConfigurationImportSelector实现了DeferredImportSelector接口并且内部也实现了DeferredImportSelector.Group接口,所以在Spring容器初始化的过程中会调用

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法和#selectImports方法,

process()用来生成需要自动装配的类型,方法的代码如下,

Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
             () -> String.format("Only %s implementations are supported, got %s",
                                 AutoConfigurationImportSelector.class.getSimpleName(),
                                 deferredImportSelector.getClass().getName()));
// 1. getAutoConfigurationMetadata()
// 从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,
// 里面保存了加载类是否自动装配的条件 ,
// org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration.ConditionalOnBean
// =javax.jms.ConnectionFactory
// 2. getAutoConfigurationEntry()
// 从 META-INF/spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串类表 并封装成 自动装配类对象
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 循环遍历 自动装配类对象 的自动装配类字符串 ,添加到 this.entries
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    this.entries.putIfAbsent(importClassName, annotationMetadata);
}
ログイン後にコピー

getAutoConfigurationMetadata() 方法主要逻辑是:从META-INF/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,里面保存了自动加载类是否符合自动装配的前置条件,比较熟悉的有ConditionalOnClass和ConditionalOnBean,文件相关内容如下:

org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.security.oauth3.client.servlet.OAuth3ClientAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration.ConditionalOnWebApplication=REACTIVE
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.security.oauth3.resource.servlet.OAuth3ResourceServerAutoConfiguration.ConditionalOnWebApplication=SERVLET
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration.ConditionalOnWebApplication=SERVLET
//省略
ログイン後にコピー

getAutoConfigurationEntry()方法 主要逻辑是:从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表并封装成自动装配类AutoConfigurationEntry对象,代码如下,

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                                                           AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获取注解元数据的属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 从spring.factories 文件中获取key为 EnableAutoConfiguration 的配置类字符串列表
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去掉重复的 自动装配类字符串
    configurations = removeDuplicates(configurations);
    // 根据注解元数据获取 需要排除的类名
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 检查排除的类名
    checkExcludedClasses(configurations, exclusions);
    // 根据排除的类名进行排除
    configurations.removeAll(exclusions);
    // 从spring.factories 文件中获取key为 AutoConfigurationImportFilter 的配置对象进行过滤
    // 过滤规则从 getAutoConfigurationMetadata() 返回类的数据中获取
    configurations = filter(configurations, autoConfigurationMetadata);
    // 执行导入配置类的监听事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回 AutoConfigurationEntry 对象
    return new AutoConfigurationEntry(configurations, exclusions);
}
ログイン後にコピー

getCandidateConfigurations()方法从spring.factories 文件中获取类型为 EnableAutoConfiguration 的配置类字符串列表,代码如下,

// getSpringFactoriesLoaderFactoryClass()方法返回 EnableAutoConfiguration
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文件中EnableAutoConfiguration .class 对应的实现类字符串为

# 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,\
// 省略
ログイン後にコピー

2.selectImports()方法返回排序后的 Entry(需要自动装配的包装实体类) 对象集合,代码如下,

public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    }
    Set<String> allExclusions = this.autoConfigurationEntries.stream()
        .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
        .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
        .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);
    // 返回排序后的 Entry 集合
    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
        .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
        .collect(Collectors.toList());
}
ログイン後にコピー

注意:@EnableAutoConfiguration 注解的分析过程需要结合@Import注解的过程来看。

2.2.3@ComponentScan

コンポーネント スキャン アノテーションは、自動スキャン パッケージ パスを構成するために使用されます。構成パスがない場合は、メイン構成クラスの名前空間にあるすべてのパッケージとクラスがスキャンされます。

以上がSpringBootのスタートアップコードと自動アセンブリのソースコードとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート