春のブート自動組立原理とソースコード解析

記事の主な内容

  1. 春ブーツ自動組立の原則[あり]シミュレーションケース
  2. スプリングブート自動組立ソースコード解析

原則SpringBootの自動読み込み

箱から出しての機能を備えたSpringBoot、それは静かに多くのことを行うために私たちを助ける[自動的に多数のオブジェクトを作成して組み立てられています]

自動組立の実装

インタフェースは、主に自動アセンブリによって行われるImportSelector

ImportSelector

ImportSelectorインターフェースは、コアスプリングインポート外部構成インターフェース(機能アノテーション)が自動設定SpringBootと@EnableXXXに決定的な役割を果たしています。ImportSelector @importを使用すると、クラスマーク実装クラス@Conフィギュレーションで導入されたとき、それはクラス実装クラス名がBeanとして定義されて返されます。

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

} 复制代码

継承グラフインターフェース

ここに画像を挿入説明

DeferredImportSelector

それImportSelector差が豆のローディングのタイミングであるImportSelectorからDeferredImportSelectorインタフェース継承上のグラフが示すことから、フィギュレーションが完了した全て@Conを待つDeferredImportSelectorニーズ後にロードされるであろう

春のブート自動組立ケース

プロジェクトポータル:自動構成

ケースメジャーコード

  1. 豆の生産の構成クラスを作成するために、私たちはそれを宣言するために@Configurationアノテーションを使用しないでください。

public class MyConfig {
    @Bean(value = "chenfu", name = "chenfu")
    public Map<Object, Object> getMap() {
        HashMap<Object, Object> map = new HashMap<>();
        map.put("code", 200);
        map.put("msg", "success");
        String nowDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        map.put("data", nowDate);
        return map;
    }
} 复制代码
2. ImportSelectorインタフェースを実装し、我々はコンフィギュレーションクラス名の先頭に戻ります

public class MyConfigImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
//        返回配置名称
        return new String[]{MyConfig.class.getName()};
    }
} 复制代码
3.テストを実行します

@SpringBootApplication
@Import(value = MyConfigImportSelector.class)
public class AutoConfigApp {
    public static void main(String[] args) {
        ConfigurableApplicationContext app = SpringApplication.run(AutoConfigApp.class, args);
        Object chenfu = app.getBean("chenfu");
        System.out.println(chenfu);
    }
} 

复制代码

ケース注釈文を作成していないオブジェクトの春ブーツ自動組立後の上記の一般的なコンテンツ、(コントローラ@、サービス@、Repostiroty @)を使用して、春のオブジェクトが、知られているケースを介してプログラム使用して動的にロードされた豆。主スプリング山猿にこれらのオブジェクトを解析するステップで行うクラスConfigurationClassParser processImports方法において

解決プロセスのImportSelector

ConfigurationClassParser

ConfigurationClassParserクラスのソース

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
	if (importCandidates.isEmpty()) {
		return;
	}
	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				//对ImportSelector类型的类进行处理
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
 					//上边我们说过的另外一种Selector类型,可以理解为延迟加载
					if (selector instanceof DeferredImportSelector) {
						//该方法内部会将该Selector保存到一个集合【deferredImportSelectors】中
						this.deferredImportSelectorHandler.handle(
								configClass, (DeferredImportSelector) selector);
					} else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, 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 =
							BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
					ParserStrategyUtils.invokeAwareMethods(
							registrar, 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
					// 直接把上边的官方英文注释硬翻译了,当前类不是ImportSelector或ImportBeanDefinitionRegistrar类型,直接让其走@Configuration类的处理流程
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		} catch (BeanDefinitionStoreException ex) {
			throw ex;
		} catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		} finally {
			this.importStack.pop();
		}
	}
} 

复制代码

要約すると、一般的なプロセスは、インターフェースImportSelectorの戻り値が再帰的に解析され、その後クラスのフルネームはフィギュレーション@Conに従って処理終を解決します。

ImportSelectorの概要

SpringBootはImportSelectorに、主に感謝し、使用する準備ができて箱から出しています。

春のブートソースコード解析

いくつかの拡張を行うことに基づいて春ブーツ春。

  1. それはSpringBootApplication @EnableAutoConfigurationコメントSpringBoot中にノートを宣言します

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication { 复制代码
EnableAutoConfiguration @ 2.インポートによって定義@import({AutoConfigurationImportSelector.class})SpringBootを導入しました

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration { 复制代码

次に、ソースコード解析AutoConfigurationImportSelectorされ始めます

AutoConfigurationImportSelector

AutoConfigurationImportSelectorがselectImports実装クラスである、我々はselectImports方法を見て

selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
} 

复制代码

この方法の主なロジックはgetAutoConfigurationEntryにおける方法であります

getAutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 2.1通过getCandidateConfigurations方法获取所有需要加载的bean
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        // 去重
        configurations = this.removeDuplicates(configurations);
        // 获取不需要加载的bean,我们可以通过spring.autoconfigure.exclude配置
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.filter(configurations, autoConfigurationMetadata);
        // 发送事件,通知所有的AutoConfigurationImportListener进行监听
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
} 复制代码

次に、中と呼ばれるトップソースgetCandidateConfigurations方法で見てみましょう

getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	// 这里的getSpringFactoriesLoaderFactoryClass()最终返回的是EnableAutoConfiguration.class
    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;
} 

复制代码

SpringFactoriesLoader.loadFactoryNames豆の最後のチャネルがSpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClassを()、this.getBeanClassLoader())を得る、上記論理から分かります。

SpringFactoriesLoader

loadFactoryNames

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	// 通过factoryClassName获取相应的bean全称
    String factoryClassName = factoryClass.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} 

复制代码

loadSpringFactories

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
        	// 获取项目中所有META-INF/spring.factories文件,将其组装成Map
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
} 

复制代码

独自のMETA-INF / spring.factoriesを定義することができ、それぞれのjarパッケージは、jarファイルが自動的に読み込まれます豆の内部で定義された同じ時間spring.factoriesにロードされます。私たちは、コンフィギュレーションファイルの内容の春ブーツで[のみ]一部を見ることができます

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

# 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,\ 复制代码

したがって、我々は、ケースの上部を変換することができ、ディレクトリresoucesにファイルを作成し、以下を追加します

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
top.chenfu.auto.config.MyConfig   复制代码

ブートクラス@importまたはカスタム注釈を削除する@EnableXXX

ここに画像を挿入説明

よりエキサイティングな記事は、マイクロチャネル公共数を心配することができる:Javaプログラマは収集します




おすすめ

転載: juejin.im/post/5df76ed46fb9a016526ebbcc