Spring Boot 基於註解驅動原始碼分析--自動配置

怪咖_OOP發表於2018-08-28

Spring作為Java開發最常用的容器管理框架,使用註解為我們提供很多便捷,下面通過原始碼分析Spring基於註解驅動自動配置的原理

首先介紹兩個關鍵類:

ConfigurationClassPostProcessor

類原始碼註釋: {@link BeanFactoryPostProcessor} used for bootstrapping processing of{@link Configuration @Configuration} classes. 大致說的是用於引導處理 @Configuration類

類結構圖

image.png
從結構圖中發現它實現了BeanDefinitionRegistryPostProcessor而BeanDefinitionRegistryPostProcessor繼承了BeanFactoryPostProcessor介面

BeanFactoryPostProcessor介面是spring底層的一個介面可以在元件初始化之前,修改BeanDefinition並重新註冊bean

BeanDefinitionRegistryPostProcessor它是標準SPI擴充套件介面,允許在已有的BeanDefinition中進一步註冊bean定義

BeanDefinitionRegistryPostProcessor介面實現方法

從配置類進一步獲取bean定義並註冊

/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);
        //處理配置bean定義
		processConfigBeanDefinitions(registry);
	}
複製程式碼
processConfigBeanDefinitions(registry);
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		
		......省略檢查部分程式碼
		
		// 逐個解析@Configuration 配置類
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
		do {
		  //類配置解析器進行解析
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			//載入bean定義
			this.reader.loadBeanDefinitions(configClasses);
			
			......
	}
複製程式碼

在該方法中例項化ConfigurationClassParser(類配置解析器)

解析完之後通過ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法載入bean定義

關於bean載入完成後怎樣進行例項化將在後面作為單獨一篇文章分享給大家

ConfigurationClassParser

解析@Configuration類定義,解析單個配置類時可以有任意數量的配置類物件,因為當前配置類可能使用@Import註解包含其他配置類

在上面方法程式碼中parser.parse(candidates);

parse(Set configCandidates)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		processDeferredImportSelectors();
	}
複製程式碼

在parse方法中呼叫了processConfigurationClass(ConfigurationClass configClass)方法

processConfigurationClass(ConfigurationClass configClass)
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {

        ......
        
		// 遞迴處理配置類和超類(父類)
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}
複製程式碼

doProcessConfigurationClass(configClass, sourceClass);

該方法處理了配置類上的@PropertySources、@PropertySource、@ComponentScans、@ComponentScan、@ImportResource註解

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(
							holder.getBeanDefinition(), this.metadataReaderFactory)) {
						parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
			AnnotationAttributes importResource =
					AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}
複製程式碼

關於方法中如何處理各個註解並進行bean定義,感興趣的朋友可以點選對應方法,自行閱讀!

以上屬於原創文章,轉載請註明作者@怪咖

QQ:208275451

相關文章