Spring作為Java開發最常用的容器管理框架,使用註解為我們提供很多便捷,下面通過原始碼分析Spring基於註解驅動自動配置的原理
首先介紹兩個關鍵類:
ConfigurationClassPostProcessor
類原始碼註釋: {@link BeanFactoryPostProcessor} used for bootstrapping processing of{@link Configuration @Configuration} classes. 大致說的是用於引導處理 @Configuration類
類結構圖
從結構圖中發現它實現了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