Spring Boot自動組立(基本原理)

この記事は、Spring Boot自動、自動組立の核心部分を直接説明します。

【Spring Boot 自动装配的入口原理】http://www.baidu.com

Spring Boot自動組立(基本原理)

次のメソッドによって返される文字列配列は、自動的にアセンブルする必要がある構成クラスのリストです。構成クラスのリストを取得するコード全体は次のとおりです。

// AutoConfigurationImportSelector.class
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
    if (!isEnabled(annotationMetadata)) {
    
    
        return NO_IMPORTS;
    }
    // <1> 加载所有的jar包中的 `META-INF/spring-autoconfigure-metadata.properties` 文件
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
        .loadMetadata(this.beanClassLoader);
    // <2> 从@EnableAutoConfiguration注解的元数据AnnotationMetadata中获取所有的属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // <3> 加载所有jar包中的 `META-INF/spring.factories` 配置文件,获取key=EnableAutoConfiguration的所有自动配置类
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // <4> 移除重复的配置类
    configurations = removeDuplicates(configurations);
    // <5> 获取需要排除的条件
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // <6> 检查排除的类
    checkExcludedClasses(configurations, exclusions);
    // <7> 移除排除的类
    configurations.removeAll(exclusions);
    // <8> 执行filter过滤
    configurations = filter(configurations, autoConfigurationMetadata);
    // <9> 触发事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
}

まず、アノテーションのメタデータを表すパラメータを導入しますannotationMetadata。これには、アノテーションのすべての完全な情報が含まれます@Import。selectImports メソッドが配置されているクラスをインポート () する人は誰でも、このアノテーションは誰を示します。したがって、ここで annotationMetadata はアノテーションEnableAutoConfiguration

上記の方法を簡単に説明すると次のとおりです。

  • <1>で、jar パッケージ内のすべてのMETA-INF/spring-autoconfigure-metadata.propertiesファイルを、後のフィルタリング サービスのためにキャッシュします。

  • <2>@EnableAutoConfiguration、アノテーションのメタデータからAnnotationMetadataすべての属性を取得します。

  • <3>META-INF/spring.factories構成、key=EnableAutoConfigurationのすべての自動構成クラスを取得します。以下の『getCandidateConfigurations 方法』を参照してください。

  • <4>、重複した構成クラスを削除します

  • <5>、除外する必要がある条件を取得します。以下の「getExclusions 方法」を参照してください。

    含む:

    1.exclude属性

    2.excludeName属性

    3.spring.autoconfigure.exclude環境構築

  • <6>、除外されたクラスを確認します

  • <7>、除外されたクラスを削除します

  • <8>除外する必要がある 3 つの状況を除外した後、フィルタリングを実行する必要があります。以下の「filter 方法」を参照してください。

  • <9>、イベントがトリガーされます

getCandidateConfigurations 方法

キーがMETA-INF/spring.factories構成EnableAutoConfiguration候補となるすべてのアセンブリ コンポーネントを取得します

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
    
    
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
	                                                  AnnotationAttributes attributes) {
    
    
        // <1> 读取所有jar包中的 META-INF/spring.factories 文件中 key 为 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;
	}

	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    
    
		return EnableAutoConfiguration.class;
	}
}

getExclusions メソッド

このメソッドの機能は、除外する必要がある自動アセンブリクラスの状況を取得することであり、以下の 3 種類があります。

  • @EnableAutoConfiguration の exclude 属性
  • @EnableAutoConfiguration の excludeName 属性
  • 環境変数spring.autoconfigure.exclude
public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
    
    

	private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";

	protected Set<String> getExclusions(AnnotationMetadata metadata,
	                                    AnnotationAttributes attributes) {
    
    
		Set<String> excluded = new LinkedHashSet<>();
        // <1> 从注解属性中获取 exclude
		excluded.addAll(asList(attributes, "exclude"));
        // <2> 从注解属性中获取 excludeName
		excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        // <3> 从环境 env 中获取 spring.autoconfigure.exclude
		excluded.addAll(getExcludeAutoConfigurationsProperty());
		return excluded;
	}

    // 从环境中获取 spring.autoconfigure.exclude
	private List<String> getExcludeAutoConfigurationsProperty() {
    
    
		if (getEnvironment() instanceof ConfigurableEnvironment) {
    
    
			Binder binder = Binder.get(getEnvironment());
			return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
					.map(Arrays::asList).orElse(Collections.emptyList());
		}
		String[] excludes = getEnvironment()
				.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
		return (excludes != null ? Arrays.asList(excludes) : Collections.emptyList());
	}
}

フィルタ方式

機能:AutoConfigurationImportFilter設定(つまり、自動設定インポート フィルター)

例:OnClassCondition例として、構成クラスが依存するクラス条件を事前にリストし、依存するクラスがクラスパスに存在するかどうかを確認して、一部の構成クラスを事前にフィルタリングすることで、@ConditionalOnClassアノテーション原則より効率的です。

備考: OnClassCondition クラスは @ConditionalOnClass アノテーションと同じ効果があり、@ConditionalOnClass で置き換えることができます。

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

// AutoConfigurationImportSelector.class

// <1> autoConfigurationMetadata 参数
private List<String> filter(List<String> configurations,
                            AutoConfigurationMetadata autoConfigurationMetadata) {
    
    
    long startTime = System.nanoTime();
    String[] candidates = StringUtils.toStringArray(configurations);
    // <2> 最终需要过滤掉的配置类结果数组,默认是false保留配置类,为true过滤掉配置类
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    // <3> 获取过滤器列表
    for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
    
    
        invokeAwareMethods(filter);
        // <4> 是否跟过滤器匹配【期待:match数组是false,false表示跳过该配置类】
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);
        for (int i = 0; i < match.length; i++) {
    
    
            if (!match[i]) {
    
    
                skip[i] = true;
                skipped = true;
            }
        }
    }
    if (!skipped) {
    
    
        return configurations;
    }
    List<String> result = new ArrayList<>(candidates.length);
    // <5> 最终的自动装配类
    for (int i = 0; i < candidates.length; i++) {
    
    
        if (!skip[i]) {
    
    
            result.add(candidates[i]);
        }
    }
    return new ArrayList<>(result);
}
  • <1>autoConfigurationMetadataパラメータの内容は次から取得されます。META-INF/spring-autoconfigure-metadata.properties

  • <2>、スキップ配列は、スキップする必要がある最終構成クラスの結果を記録します。

  • <3>で、設定されているすべてのフィルタを取得し、SpringFactoriesLoader.loadFactoriesメソッドキーがAutoConfigurationImportFilter次の自動設定クラス インポート フィルタを取得します。

    Spring Boot 2.0.2.RELEASE はデフォルトでのみ構成されておりOnClassCondition、構成は次のとおりです。

    // spring.factories 文件
    # Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnClassCondition
    
  • <4>場所、特定のフィルターのマッチングを実行します。マッチング結果が false の場合は設定クラスをスキップすることを意味し、true の場合は処理を行いません

  • <5>、スキップ結果に従って、条件を満たす構成クラスのみを追加し、構成クラスのリストを返します。

autoConfigurationMetadata のソース

META-INF/spring-autoconfigure-metadata.properties ファイルは、フィルタリング用に準備されたメタデータと考えることができます。これは、一部の構成クラスを事前にフィルタリングして除外し、構成クラスのアセンブリの相対順序を設定し、構成クラスのアセンブリの絶対順序を設定するのに役立ちます。構成クラスのアセンブリなど

final class AutoConfigurationMetadataLoader {
    
    

   protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

   public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    
    
      return loadMetadata(classLoader, PATH);
   }
}

Spring-autoconfigure-metadata.properties ファイルの内容の例:

# AutoConfigureAfter(在某个配置类之后)
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
# AutoConfigureBefore(在某个配置类之前)
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
# ConditionalOnClass(对应的类必须存在)
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration.ConditionalOnClass=org.jooq.DSLContext

OnClassCondition フィルター

Spring Boot 2.0.2.RELEASE バージョンでは、OnClassCondition フィルターは、インポート フィルターを自動的に構成するようにデフォルトで構成された AutoConfigurationImportFilter です。

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

OnClassCondition の英語名からもわかるように、クラス条件に基づいて構成クラスをロードするかどうかを判断する機能であり、クラスパスに依存クラスが存在する場合は構成クラスがロードされ、依存クラスが存在する場合は構成クラスがロードされます。がクラスパスに存在しない場合、構成クラスはロードされません。

具体的には、matchメソッドで返される配列に従い、データ項目がtrueの場合はクラスパスにクラスが存在することを意味し、falseの場合はクラスパスにクラスが存在しないことを意味します。

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

class OnClassCondition extends SpringBootCondition
		implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
    
    

	public boolean[] match(String[] autoConfigurationClasses,
	                       AutoConfigurationMetadata autoConfigurationMetadata) {
    
    
		ConditionEvaluationReport report = getConditionEvaluationReport();
		// <1> 计算匹配情况
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
				autoConfigurationMetadata);
		boolean[] match = new boolean[outcomes.length];
		for (int i = 0; i < outcomes.length; i++) {
    
    
			// <2> 转换匹配情况【期待整体是false,即不等于null 且 match = false,则跳过该配置类】
			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
            // 这里是记录不匹配的情况,把不匹配的原因详情设置到报告器report中
            if (!match[i] && outcomes[i] != null) {
    
    
				logOutcome(autoConfigurationClasses[i], outcomes[i]);
				if (report != null) {
    
    
					report.recordConditionEvaluation(autoConfigurationClasses[i], this,
							outcomes[i]);
				}
			}
		}
		return match;
	}

    // <3> 计算匹配结果(分2半处理)
	private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
    
    

		int split = autoConfigurationClasses.length / 2;
		// 第一个解析器:ThreadedOutcomesResolver
		OutcomesResolver firstHalfResolver = createOutcomesResolver(
				autoConfigurationClasses, 0, split, autoConfigurationMetadata);
		// 第二个解析器:StandardOutcomesResolver
		OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
				autoConfigurationClasses, split, autoConfigurationClasses.length,
				autoConfigurationMetadata, this.beanClassLoader);
		// 第 2 个解析
		ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
		// 第 1 个解析
		ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
		ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
		System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
		System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
		return outcomes;
	}
}
  • <1>、候補構成クラスのマッチングを計算します。

  • <2>、一致するケースを一致する配列に変換します

    述べる:

    1. 配列項目を false にしたい場合は、両方の条件が false でなければなりません

    2. 配列項目を true にするには、2 つの条件のうち 1 つだけを true にします。

  • <3>その場所では、二分法が使用され、2 つのパーサーが候補構成クラスを解析するために使用され、1 つは構成クラスの半分を解析します。

StandardOutcomesResolver リゾルバー

StandardOutcomesResolver パーサーは、構成クラスの ConditionalOnClass 構成がクラスパスに存在するかどうかを計算します。

private final class StandardOutcomesResolver implements OutcomesResolver {
    
    

    // 解析
    @Override
    public ConditionOutcome[] resolveOutcomes() {
    
    
        return getOutcomes(this.autoConfigurationClasses, this.start, this.end,
                this.autoConfigurationMetadata);
    }

    private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
            int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
    
    
        ConditionOutcome[] outcomes = new ConditionOutcome[end - start];

        // <1> 迭代候选配置类
        for (int i = start; i < end; i++) {
    
    
            String autoConfigurationClass = autoConfigurationClasses[i];
            // <2> 获取配置类的 ConditionalOnClass 条件
            Set<String> candidates = autoConfigurationMetadata
                    .getSet(autoConfigurationClass, "ConditionalOnClass");
            if (candidates != null) {
    
    
                // <3> 计算candidates是否满足
                outcomes[i - start] = getOutcome(candidates);
            }
        }
        return outcomes;
    }
}
  • <1>で、反復二分法における候補構成クラス
  • <2>、メタデータから特定の構成クラスのConditionalOnClass条件セットを抽出します。
  • <3>、特定の計算候補が満たされるかどうか

引き続き getOutcome メソッドを見てみましょう

private ConditionOutcome getOutcome(Set<String> candidates) {
    
    
    try {
    
    
        // <1> 计算 candidates 和 MISSING 的匹配情况
        List<String> missing = getMatches(candidates, MatchType.MISSING,
                                          this.beanClassLoader);
        if (!missing.isEmpty()) {
    
    
            // <2> 此时 match 为 false
            return ConditionOutcome.noMatch(
                ConditionMessage.forCondition(ConditionalOnClass.class)
                .didNotFind("required class", "required classes")
                .items(Style.QUOTE, missing));
        }
    }
    catch (Exception ex) {
    
    
        // We'll get another chance later
    }
    return null;
}
  • <1>、候補と MISSING の間の一致を計算します。これは MISSING のタイプであるため、返された Missing は欠落セットを意味します。セットが空でない限り、欠落があることを意味します。つまり、検出が失敗したことを意味します。構成クラスはアセンブルしないでください

次に、コレクションが空ではない状況を引き続き確認します。コードは次のとおりです。

private List<String> getMatches(Collection<String> candidates, MatchType matchType,
                                ClassLoader classLoader) {
    
    
    List<String> matches = new ArrayList<>(candidates.size());
    for (String candidate : candidates) {
    
    
        // 要求 match
        if (matchType.matches(candidate, classLoader)) {
    
    
            matches.add(candidate);
        }
    }
    return matches;
}

引き続き、MISSING タイプの一致メソッドを確認します。

private enum MatchType {
    
    

	MISSING {
    
    
		@Override
		public boolean matches(String className, ClassLoader classLoader) {
    
    
            // className 是否存在于 classpath 中
			return !isPresent(className, classLoader);
		}
	};
}

className がクラスパスに存在しない場合、matches メソッドは true を返し、getMatches メソッドは空ではない欠落コレクションを返します。

自動配線イベントをトリガーする

fireAutoConfigurationImportEvents(configurations, exclusions)メソッドの実装について引き続き議論する

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered {
    
    

	private void fireAutoConfigurationImportEvents(List<String> configurations,
	                                               Set<String> exclusions) {
    
    
		// 获取配置的监听器
		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
		if (!listeners.isEmpty()) {
    
    
			// 创建事件
			AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
					configurations, exclusions);
			for (AutoConfigurationImportListener listener : listeners) {
    
    
				invokeAwareMethods(listener);
				// 执行事件
				listener.onAutoConfigurationImportEvent(event);
			}
		}
	}
}

Spring Boot は、開発者にフレームワーク レベルで拡張する方法を提供し、自動構成クラスの読み込みを評価できるようにします。Spring Boot 2.0.2.RELEASE を例にとると、AutoConfigurationImportListener 実装 ConditionEvaluationReportAutoConfigurationImportListener がデフォルトで構成されます。自動配線される条件の評価の詳細をドキュメントするために使用されます。構成は、次のように META-INF/spring.factories ファイルにあります。

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

ポータル: nanny Spring5 ソース コード分析

著者とテクノロジーや仕事生活について交流することを歓迎します

著者に連絡する

おすすめ

転載: blog.csdn.net/yuchangyuan5237/article/details/130613323