Spring 源碼(四)解析配置類

咱們要找出全部符合條件的業務Bean,首先咱們須要知道在Spring中什麼樣的Bean是符合條件的,是須要容器來管理的:java

  1. 使用組件標註註解的Bean(@Controller、@Service、@Repository、@Component),通常項目裏面使用。
  2. 使用@Bean註解標準的方法,通常導入第三方組件的時候使用。
  3. 使用@Import註解導入的Bean,通常快速導入一批組件時使用。

doProcessConfigurationClass 配置類的解析過程

調用鏈路:spring

doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:app

// 解析配置類將業務Bean的定義BeanDefinition註冊到容器
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) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.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;
}

從源碼咱們能夠看到解析配置類的完整過程:ide

  1. 使用遞歸的方式處理嵌套類
  2. 處理@PropertySource註解,解析配置文件,將配置項加載到環境變量裏面
  3. 處理@ComponentScan註解,將全部符合條件Bean的BeanDefinition註冊到容器
  4. 處理@Import註解
  5. 處理@ImportResource註解
  6. 處理@Bean方法
  7. 處理接口的默認方法
  8. 處理父類

處理@ComponentScan註解

咱們這裏主要介紹下@ComponentScan註解的處理過程,最後主要的處理方法是ClassPathBeanDefinitionScanner#doScan()方法。post

調用鏈路:ui

doScan:270, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
parse:135, ComponentScanAnnotationParser (org.springframework.context.annotation)
doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:this

//  @ComponentScan 執行包掃描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
	for (String basePackage : basePackages) {
		// 根據包路徑找到須要加載到容器的業務Bean
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				// 將符合條件的Bean 定義註冊到容器
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

// 根據包路徑找到須要加載到容器的業務Bean
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
	try {
		// 根據包路徑找到須要加載到容器class文件
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
				
		// 根據資源加載器加載全部在對應包路徑下的class文件到內存
		Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			// 判斷是不是可讀
			if (resource.isReadable()) {
				try {
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					// 判斷是不是@Component組件
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						...
	return candidates;
}

// 判斷是不是@Component組件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	// 執行excludeFilters
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return false;
		}
	}
	// 執行includeFilters
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

findCandidateComponents方法最終會調用AnnotationMetadataReadingVisitor類的hasAnnotation和hasMetaAnnotation方法來判斷是不是@Component組件。 調用鏈路:lua

hasMetaAnnotation:112, AnnotationMetadataReadingVisitor (org.springframework.core.type.classreading)
matchSelf:86, AnnotationTypeFilter (org.springframework.core.type.filter)
match:61, AbstractTypeHierarchyTraversingFilter (org.springframework.core.type.filter)
isCandidateComponent:354, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
findCandidateComponents:288, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
doScan:272, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
parse:135, ComponentScanAnnotationParser (org.springframework.context.annotation)
doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:ssr

// 判斷是不是@Component組件
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

	...
	// 判斷是不是業務Bean
	@Override
	public boolean hasAnnotation(String annotationName) {
		return this.annotationSet.contains(annotationName);
	}
	
	// 判斷是不是業務Bean
	@Override
	public boolean hasMetaAnnotation(String metaAnnotationType) {
		// 若是業務Bean是加的@Service註解,那麼在metaAnnotationMap中將會以 
		// org.springframework.stereotype.Service=org.springframework.stereotype.Component的形式存儲,
		// 將@Service註解映射成@Component註解。其餘繼承自@Component註解的處理方式也同樣
		Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
		for (Set<String> metaTypes : allMetaTypes) {
			if (metaTypes.contains(metaAnnotationType)) {
				return true;
			}
		}
		return false;
	}
	...
}

執行流程:debug

  1. 根據@ComponentScan註解配置的包範圍找到全部符合條件的class文件
  2. 根據資源加載器,將全部的class文件加載到內存
  3. 循環遍歷class找出符合條件的@Component組件
  4. 執行@ComponentScan的excludeFilters
  5. 執行@ComponentScan的includeFilters
  6. 獲取當前class的metaAnnotationMap,判斷是不是@Component組件
  7. 將符合條件的class的BeanDefinition註冊到容器

若是業務Bean是加的@Service註解,那麼在metaAnnotationMap中將會以 org.springframework.stereotype.Service=org.springframework.stereotype.Component的形式存儲, 將@Service註解映射成@Component註解。其餘繼承自@Component註解的處理方式也同樣

相關文章
相關標籤/搜索