spring源碼(三)——@Configuration類的註冊

前言

上一篇博客中已經總結到了spring在ConfigurationClassPostProcessor類中調用ConfigurationClassParser#parse方法,開始進行配置類的解析操作,這篇博客就簡單總結一些這些解析操作做了什麼

先看一個實例

通常我們在通過註解的方式使用註解的時候,我們都會看到一個這樣的類(至少我在實際工作中是這樣),通常一個@Configuration的註解的類,其中通過@Bean註解返回各個Bean,如下所示

@Configuration
@ComponentScan(value="com.learn.importselector.config")
public class AppConfig {
    @Bean
    public TestBean testBean(){
        return new TestBean();
    }
}

通過如下實例,我們可以正常的執行TestBean中的方法

public class TestOutOfConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext
                = new AnnotationConfigApplicationContext(AppConfig.class);

        TestBean testBean = annotationConfigApplicationContext.getBean(TestBean.class);
        testBean.testQuery();
    }
}

以上是正常操作,但是,如果我們去掉@Configuration之後,會怎樣,是不是就無法執行了?即我們的AppConfig中去掉@Configuration這個註解之後,我們能否正常執行呢

@ComponentScan(value="com.learn.importselector.config")
public class AppConfig {
    @Bean
    public TestBean testBean(){
        return new TestBean();
    }
}

答案是正常的,上面的TestOutOfConfig依舊能正常執行,那麼問題來了@Configuration有個毛用?或者說加@Configuration與不加@Configuration的最大區別是什麼,這個在接下來的兩篇博客源碼旅途中進一步解惑。

parse方法

上次我們源碼走到了BeanFactoryPostProcessor接口中的方法,發現其中最重要的就是processConfigBeanDefinitions方法,發現這個方法中有一段邏輯,關於@Configuration的——如果這個類加上了@Configuration註解,則這個類被標記爲full,如果這個類沒有標記@Configuration,而只是標記了@Component,@ComponentScan,@Import,@ImportResource則這個類被標記爲lite其中Full表示完全的意思,lite表示部分的意思。

上篇博客最後提到過,如果一個類加了@Configuration會被標記爲全註解的類,同時checkConfigurationClassCandidate會返回true,但是如果沒有加@Configuration註解,但是加了@Component,@ComponentScan,@Import,@ImportResource這四個中的任何一個,這個類就會被標記爲lite,同時checkConfigurationClassCandidate依舊會返回true。

同時在上篇博客的源碼中可以看到入下幾行代碼:

//以下代碼是ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中的片段
//TODO:檢查beanDef是否滿足Configuration類的條件,如果滿足,則放入configCandidates集合中
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}

//TODO:實例化2個set,candidates用於將之前加入的configCandidates進行去重
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);

//TODO:解析candidates中的配置類,真正完成掃描 candidates中目前存放的都是標記爲配置類的beanDefinition
parser.parse(candidates);

可以看到,只要加了@Configuration,@Component,@ComponentScan,@Import,@ImportResource這5箇中的其中一個,都會被作爲candidates集合中的元素,傳遞給parse方法。從這裏似乎看出,@Configuration有個鳥用,和@Component,@ComponentScan,@Import,@ImportResource不差不多麼?不急,後面再介紹。

前面的系列博客中已經梳理到了ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法,調用parse就是在這個方法中操作的。這裏我們直接進入到parse方法中,開始我們今天的梳理

//ConfigurationClassParser第163行
//TODO:解析BeanDefintion
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
            //如果beanDefinition是註解類型的,由於我們的實例採用的是註解的方式,因此會走這個分支
			if (bd instanceof AnnotatedBeanDefinition) {
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
            //如果是AbstractBeanDefinition類型的
			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);
		}
	}

	this.deferredImportSelectorHandler.process();
}

進一步解析

//TODO:ConfigurationClassParser第200行
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
	processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

繼續,進入processConfigurationClass

/**
 * TODO:處理configuration
 * @param configClass
 * @throws IOException
 */
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		return;
	}
	
    //TODO:configurationClasses是一個Map集合,存放已經有的Configuration類
	ConfigurationClass existingClass = this.configurationClasses.get(configClass);
	if (existingClass != null) {//TODO:如果這個集合不爲空
		if (configClass.isImported()) {//TODO:判斷是否導入了
			if (existingClass.isImported()) {
				existingClass.mergeImportedBy(configClass);
			}
			// Otherwise ignore new imported config class; existing non-imported class overrides it.
			return;
		}
		else {//TODO:如果沒有導入,更新爲新的
			// Explicit bean definition found, probably replacing an import.
			// Let's remove the old one and go with the new one.
			this.configurationClasses.remove(configClass);
			this.knownSuperclasses.values().removeIf(configClass::equals);
		}
	}

	// Recursively process the configuration class and its superclass hierarchy.
	//TODO:將Configuration類封裝成一個SourceClass
	SourceClass sourceClass = asSourceClass(configClass);
	do {
		//TODO:真正的處理configuration類,名字雖然如此,但是其實處理的幾乎就是我們掃描得到的所有的beanDefinition
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	while (sourceClass != null);

	//TODO:ImportSelector返回的類會先放到configurationClasses中
	this.configurationClasses.put(configClass, configClass);
}

因此可以看出,doProcessConfigurationClass是我們需要重點討論的方法。

doProcessorConfigurationClass方法

/**
 * Apply processing and build a complete {@link ConfigurationClass} by reading the
 * annotations, members and methods from the source class. This method can be called
 * multiple times as relevant sources are discovered.
 * @param configClass the configuration class being build
 * @param sourceClass a source class
 * @return the superclass, or {@code null} if none found or previously processed
 */
//TODO:處理Configuration的配置類
//TODO:傳進來的參數中,就是將sourceClass轉成了configClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	//TODO:如果有@Configuration類本身帶有@Component標籤
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// Recursively process any member (nested) classes first
		//TODO:處理其中的內部類,不太重要
		processMemberClasses(configClass, sourceClass);
	}

	//TODO:處理propertySource標籤,這個標籤常用於導入屬性
	// 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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	//TODO:處理componentScan註解,處理@Component標記的類
	// 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) {
			//TODO:開始處理配置的掃描路徑下的普通類(這裏的普通類是相對於配置類而言的)
			// 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
			//TODO:遍歷所有的beanDefinition,並做檢查,看是否有配置類
			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());
				}
			}
		}
	}

	//TODO:處理@Import註解
	/**
	 * TODO:這裏是處理@Import註解,註解的方式下@Import註解接受三種值:
	 * TODO:1.普通類;2.實現了ImportSelector接口的類,3.實現了ImportBeanDefinitionRegistrar接口的類
	 * TODO:如果是第二種這個方法的底層會調用對應ImportSelector中的selector方法
	 * TODO:這裏的getImports(sourceClass)拿出來的是@Import中指定的類,比如@Import(TestBean.class),這裏拿出來的就是TestBean
	 */
	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	//TODO:處理@ImportResource
	// Process any @ImportResource annotations
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	if (importResource != null) {
		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);
		}
	}

    //TODO:處理帶有@Bean標籤的方法
	// Process individual @Bean methods
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	//TODO:處理接口中默認的方法
	// Process default methods on interfaces
	processInterfaces(configClass, sourceClass);

	// Process superclass, if any
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !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;
}

先貼出所有的代碼,然後我們一一梳理,前面說過,掃描之後,傳入到parser方法的candidates其實包含了帶有@Configuration,@Component,@ComponentScan,@Import,@ImportResource這5個註解的類,說道這裏可能會問,那@Service之類的呢?其實從原碼來看@Service@Component似乎差別不大,畢竟看看@Service的源碼可以知道

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	@AliasFor(annotation = Component.class)//TODO:人家把service當做component的一個別名來處理
	String value() default "";

}

迴歸正題。

doProcessorConfigurationClass這個方法可以分爲幾個部分

@Component和PropertySource的處理

//TODO:如果有@Configuration類本身帶有@Component標籤
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
	// Recursively process any member (nested) classes first
	//TODO:處理其中的內部類,不太重要
	processMemberClasses(configClass, sourceClass);
}

//TODO:處理propertySource標籤,這個標籤常用於導入屬性
// 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.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
				"]. Reason: Environment must implement ConfigurableEnvironment");
	}
}

這個屬於不太重要的部分,如果@Configuration本身加入了@Component註解,則會先處理其內部類,@PropertySource是用於指定配置文件的,這裏可以參考百度即可。

處理@ComponentScan註解

//TODO:處理componentScan註解,處理@Component標記的類
// 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) {
		//TODO:開始處理配置的掃描路徑下的普通類(這裏的普通類是相對於配置類而言的)
		// The config class is annotated with @ComponentScan -> perform the scan immediately
        //TODO:如果配置類中加入了@ComponentScan註解,則這裏直接掃描相關包
		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
		//TODO:遍歷所有的beanDefinition,並做檢查,看是否有配置類
		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());
			}
		}
	}
}

進入到this.componentScanParser.parse方法,如下所示

//TODO:ComponentScanAnnotationParser中的第82行
/**
 * componentScan的處理
 * @param componentScan
 * @param declaringClass
 * @return
 */
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {

	//TODO:這裏纔是真正掃描類的scanner
	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	/**
	 * TODO:獲取beanName的生成器,在官網中說明過,我們可以實現一個BeanName的解析器,不太重要
	 */
	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

	/**
	 * TODO:web中這個參數比較重要,這裏暫時不討論
	 */
	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
		scanner.setScopedProxyMode(scopedProxyMode);
	}
	else {
		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	/**
	 * TODO:處理includeFilters和excludeFilters
	 */
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addIncludeFilter(typeFilter);
		}
	}
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addExcludeFilter(typeFilter);
		}
	}

	/**
	 * TODO:處理懶加載lazyInit,可以看到,這裏是如果設置了lazyInit爲true則纔將其設置爲true,否則默認爲false
	 */
	boolean lazyInit = componentScan.getBoolean("lazyInit");
	if (lazyInit) {
		scanner.getBeanDefinitionDefaults().setLazyInit(true);
	}

	Set<String> basePackages = new LinkedHashSet<>();
	String[] basePackagesArray = componentScan.getStringArray("basePackages");
	for (String pkg : basePackagesArray) {
		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		Collections.addAll(basePackages, tokenized);
	}
	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
		basePackages.add(ClassUtils.getPackageName(clazz));
	}
	if (basePackages.isEmpty()) {
		basePackages.add(ClassUtils.getPackageName(declaringClass));
	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
		@Override
		protected boolean matchClassName(String className) {
			return declaringClass.equals(className);
		}
	});

	/**
	 * TODO:真正的開始掃描
	 */
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}

真正的doScan

/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	for (String basePackage : basePackages) {
		/**
		 * TODO:findCandidateComponents 這裏就是去解析掃描到的類,將掃描之後的結果放到candidates中
		 * TODO:得到的BeanDefinition是ScannedGenericBeanDefinition類型的,
		 * TODO:這個ScannedGenericBeanDefinition繼承至GenericBeanDefinition,實現了AnnotatedBeanDefinition
		 * TODO:同時GenericBeanDefinitio繼承至AbstractBeanDefinition,因此下面兩個instanceof的判斷都會進入
		 */
		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);

			/**
			 * TODO:如果返回的是ScannedGenericBeanDefinition,則者兩個if都會進入
			 */
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			/**
			 * TODO:之後處理帶有註解的bean,就是我們的普通的帶有@Component註解的bean
			 */
			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);
				/**
				 * TODO:掃描得到了bd放入到map中,這個之前看過很多了
				 */
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

findCandidateComponents方法之後,就是註冊掃描得到的bean。

findCandidateComponents乾的事兒

/**
 * TODO:底層調用的是asm的方式去掃描
 * @param basePackage
 * @return
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
	}
	else {
		//TODO:底層調用的是asm的方式去掃描,這個不是很懂
		return scanCandidateComponents(basePackage);
	}
}

繼續

/**
 * TODO:這裏很難,這裏就是利用asm去獲取類
 * @param basePackage
 * @return
 */
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		//TODO:獲取掃描的根路徑
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().getMetadataReader(resource);
					if (isCandidateComponent(metadataReader)) {
						/**
						 * TODO:最終返回的是一個ScannedGenericBeanDefinition,這裏只是根據註解信息返回了一個bd
						 */
						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);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}

這個方法中可以看到,最終返回的是一個ScannedGenericBeanDefinition類型的BeanDefinition,這裏需要說一下ScannedGenericBeanDefinition類的BeanDefinition,其繼承圖如下所示:

在這裏插入圖片描述

因此在doScan方法中,會同時執行postProcessBeanDefinitionAnnotationConfigUtils.processCommonDefinitionAnnotations兩個分支,因此這裏繼續說一下這兩個方法

postProcessBeanDefinition
//ClassPathBeanDefinitionScanner中的第320行
/**
 * Apply further settings to the given bean definition,
 * beyond the contents retrieved from scanning the component class.
 * @param beanDefinition the scanned bean definition
 * @param beanName the generated bean name for the given bean
 */
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
	//TODO:給beanDefinition 設置默認的值
	beanDefinition.applyDefaults(this.beanDefinitionDefaults);
	//TODO:如果bean中有需要Autowired的,設置Autowired的bean
	if (this.autowireCandidatePatterns != null) {
		beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
	}
}

//TODO: applyDefaults方法
//TODO:AbstractBeanDefinition#applyDefaults中的第358行
/**
 * Apply the provided default values to this bean.
 * @param defaults the default settings to apply
 * @since 2.5
 * TODO:BeanDefinitionDefaults定義了BD中的默認值,這裏可以看到lazy的默認值是false
 */
public void applyDefaults(BeanDefinitionDefaults defaults) {
	setLazyInit(defaults.isLazyInit());
	setAutowireMode(defaults.getAutowireMode());
	setDependencyCheck(defaults.getDependencyCheck());
	setInitMethodName(defaults.getInitMethodName());
	setEnforceInitMethod(false);
	setDestroyMethodName(defaults.getDestroyMethodName());
	setEnforceDestroyMethod(false);
}
processCommonDefinitionAnnotations
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
	processCommonDefinitionAnnotations(abd, abd.getMetadata());
}

/**
 * TODO:處理bean中的通用屬性配置,lazy,primary,dependsOn,Role,Description等
 * @param abd
 * @param metadata
 */
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
	if (lazy != null) {
		abd.setLazyInit(lazy.getBoolean("value"));
	}
	else if (abd.getMetadata() != metadata) {
		lazy = attributesFor(abd.getMetadata(), Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
	}

	if (metadata.isAnnotated(Primary.class.getName())) {
		abd.setPrimary(true);
	}
	AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
	if (dependsOn != null) {
		abd.setDependsOn(dependsOn.getStringArray("value"));
	}

	AnnotationAttributes role = attributesFor(metadata, Role.class);
	if (role != null) {
		abd.setRole(role.getNumber("value").intValue());
	}
	AnnotationAttributes description = attributesFor(metadata, Description.class);
	if (description != null) {
		abd.setDescription(description.getString("value"));
	}
}

這裏針對@ComponentScan中掃描出來的BeanDefinition之後,然後直接註冊到IOC容器中了。

之後我們回到doProcessConfigurationClass方法中,開始處理@Import註解了。

處理@Import註解

//TODO:處理@Import註解
/**
 * TODO:這裏是處理@Import註解,註解的方式下@Import註解接受三種值:
 * TODO:1.普通類;2.實現了ImportSelector接口的類,3.實現了ImportBeanDefinitionRegistrar接口的類
 * TODO:如果是第二種這個方法的底層會調用對應ImportSelector中的selector方法
 * TODO:這裏的getImports(sourceClass)拿出來的是@Import中指定的類,比如@Import(TestBean.class),這裏拿出來的就是TestBean
 */
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

之前提到過的,@Import註解中可以接受三種類,一個是實現了ImportSelector的類,一種是實現了ImportBeanDefinitionRegistrar接口的類,另一個就是普通類。

/**
 * TODO:
 * @param configClass
 * @param currentSourceClass
 * @param importCandidates
 * @param checkForCircularImports 是否檢查有循環導入的情況
 */
//TODO:處理@Import
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);
		//TODO:@Import註解的屬性可以接受三種類,1.普通類,2.ImportSelector類型,3.ImportBeanDefinitionRegistrar類型
		try {
			for (SourceClass candidate : importCandidates) {
				//TODO:處理ImportSelector類型
				if (candidate.isAssignable(ImportSelector.class)) {
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass();
					//TODO:獲取出指定的ImportSelector的子類(反射實現一個Selector)
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
					if (selector instanceof DeferredImportSelector) {
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						//TODO:回調importSelector類中的selectorImports方法
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						//TODO:處理importSelector指定方法(selectorImport方法)返回的類名稱(遞歸調用)
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}

				//TODO:ImportBeanDefinitionRegistrar 將IOC中的beanDefinition的Map集合暴露給開發者,開發者可以處理這個容器
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {//TODO:處理ImportBeanDefinitionRegistrar類型
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					Class<?> candidateClass = candidate.loadClass();
					//TODO:通過反射獲取BeanDefinitionRegistrar
					ImportBeanDefinitionRegistrar registrar =
							BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
					ParserStrategyUtils.invokeAwareMethods(
							registrar, this.environment, this.resourceLoader, this.registry);
					//TODO:將ImportBeanDefinitionRegistrar放到一個map集合中
					//TODO: Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
					//TODO:處理普通類,如果@Import中導入的是一個普通類,這個普通類會被當做@Configuration的類來處理
					//TODO:先將這個類加入到importStack中,然後按照@Configuration類來處理這個普通類
					// 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));
				}
			}
		}
		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();
		}
	}
}

可以看到,針對@Import註解的這三種情況,其實spring並沒有立馬將其註冊到IOC容器中,而是分別先放到對應的集合中再進行註冊

1、針對ImportSelector,通過遞歸的調用方式,最終會和處理@Import註解中的普通類一樣,最終會放入ConfigurationClassParser類的configurationClasses容器中

2、針對ImportBeanDefinitionRegistry,會將其先加入到configClass的Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars這個集合中

3、針對普通類,最終會在ConfigurationClassParser類的processConfigurationClass方法中將其放到configurationClasses容器中。

至此doProcessorConfigurationClass方法,針對@Import註解的處理完成。

處理@ImportResource

@ImportResource直接對配置文件進行處理

//TODO:處理@ImportResource
// Process any @ImportResource annotations
AnnotationAttributes importResource =
		AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
	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);
	}
}

這塊比較簡單,直接處理配置文件即可。

處理帶@Bean註解的方法

// Process individual @Bean methods
//TODO:直接將方法解析之後,放入到configClass中的Set<BeanMethod> beanMethods集合中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

處理接口中默認的方法和父類

這一步不太中重要源碼中如下,看看就好:

//TODO:處理接口中默認的方法
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);

// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
	String superclass = sourceClass.getMetadata().getSuperClassName();
	if (superclass != null && !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

至此,doProcessorConfigurationClass方法執行完成,這個方法執行完成,也標誌着parser.parse方法執行的完成

之後,我們會退到BeanFactoryPorcessor接口中真正回調的方法——ConfigurationClassPostProcessor類中的processConfigBeanDefinitions方法(畢竟我們的實例中只有一個ConfigurationClassPostProcessor類實現了BeanFactoryPorcessor接口)

配置類的註冊

//TODO:ConfigurationClassPostProcessor第325行
do {
	//TODO:解析candidates中的配置類,真正完成掃描 candidates中目前存放的都是標記爲配置類的beanDefinition
	parser.parse(candidates);
	//TODO:驗證解析出來的類到底可不可用
	parser.validate();
	//TODO:從parser中取出configurationClasses的集合,這個集合就有import導入的普通類
	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(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());
	}

	//TODO:註冊配置類
	this.reader.loadBeanDefinitions(configClasses);
	alreadyParsed.addAll(configClasses);

	candidates.clear();
	if (registry.getBeanDefinitionCount() > candidateNames.length) {
		String[] newCandidateNames = registry.getBeanDefinitionNames();
		Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
		Set<String> alreadyParsedClasses = new HashSet<>();
		for (ConfigurationClass configurationClass : alreadyParsed) {
			alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
		}
		for (String candidateName : newCandidateNames) {
			if (!oldCandidateNames.contains(candidateName)) {
				BeanDefinition bd = registry.getBeanDefinition(candidateName);
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
						!alreadyParsedClasses.contains(bd.getBeanClassName())) {
					candidates.add(new BeanDefinitionHolder(bd, candidateName));
				}
			}
		}
		candidateNames = newCandidateNames;
	}
}
while (!candidates.isEmpty());

parse方法完成之後,就是對這些配置類進行註冊了

//TODO:從parser中取出configurationClasses的集合,這個集合就有import導入的普通類
//TODO:之前在總結parse方法的時候,發現針對@Import的三種導入的類,會放入到parser中的configurationClasses集合中,這個方法parser.getConfigurationClasses()就是獲取這個結合中的keySet
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());

//TODO:註冊配置類
this.reader.loadBeanDefinitions(configClasses);

loadBeanDefinitions就需要我們進一步總結了

loadBeanDefinitions

/**
 * Read {@code configurationModel}, registering bean definitions
 * with the registry based on its contents.
 */
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

遍歷每一個configClass,然後進行註冊

/**
 * TODO:註冊解析出來的類
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 */
private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	//TODO:如果一個類被@Import引入了,會被加入一個Set的集合中,這個集合名爲importBy,則會在這裏完成註冊
	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}

	//TODO:@Import中引入的是XML的配置會調用這個加載BeanDefinition
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

	//TODO:註冊configClass中的ImportBeanDefinitionRegistrar
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

至此,@Configuration類型的註冊完成。

總結

本篇博客簡單總結了配置類的註冊,但是針對@Configuration註解標記的類和沒有@Configuration標記的類,還是有着根本的不同的,這個不同在下一篇博客中進一步總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章