前言
上一篇博客中已經總結到了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方法中,會同時執行postProcessBeanDefinition
和AnnotationConfigUtils.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標記的類,還是有着根本的不同的,這個不同在下一篇博客中進一步總結。