spring5 源碼深度解析—– 被面試官給虐懵了,竟然是因為我不懂@Configuration配置類及@Bean的原理
- 2019 年 10 月 17 日
- 筆記
@Configuration註解提供了全新的bean創建方式。最初spring通過xml配置文件初始化bean並完成依賴注入工作。從spring3.0開始,在spring framework模組中提供了這個註解,搭配@Bean等註解,可以完全不依賴xml配置,在運行時完成bean的創建和初始化工作。例如:
public interface IBean { } public class AppBean implements IBean{ } //@Configuration申明了AppConfig是一個配置類 @Configuration public class AppConfig { //@Bean註解申明了一個bean,bean名稱默認為方法名appBean @Bean IBean appBean(){ return new AppBean(); } }
默認情況下bean的名稱和方法名稱相同,你也可以使用name屬性來指定,如
@Configuration註解使用
我們先來看看@Configuration 這個註解的定義
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component //@Component元註解 public @interface Configuration { String value() default ""; }
我們看到源碼裡面,@Configuration 標記了@Component元註解,因此可以被@ComponentScan掃描並處理,在Spring容器初始化時Configuration類 會被註冊到Bean容器中,最後還會實例化。
使用@Autowired/@Inject
因為@Configuration本身也是一個@Component,因此配置類本身也會被註冊到應用上下文,並且也可以使用IOC的@Autowired/@Inject等註解來注入所需bean。我們來修改配置類如下:
@Configuration public class AppConfig { @Autowired public Environment env; @Bean IBean appBean(){ return new AppBean(); } }
使用@CompomentScan
配置類也可以自己添加註解@CompomentScan,來顯式掃描需使用組件。
@Configuration 使用@Component 進行原註解,因此@Configuration 類也可以被組件掃描到(特別是使用XML元素)。
@Configuration @ComponentScan("abc.xxx") public class AppConfig { @Bean IBean appBean(){ return new AppBean(); } }
在這裡認識幾個註解: @Controller, @Service, @Repository, @Component
-
@Controller: 表明一個註解的類是一個”Controller”,也就是控制器,可以把它理解為MVC 模式的Controller 這個角色。這個註解是一個特殊的@Component,允許實現類通過類路徑的掃描掃描到。它通常與@RequestMapping 註解一起使用。
-
@Service: 表明這個帶註解的類是一個”Service”,也就是服務層,可以把它理解為MVC 模式中的Service層這個角色,這個註解也是一個特殊的@Component,允許實現類通過類路徑的掃描掃描到
-
@Repository: 表明這個註解的類是一個”Repository”,團隊實現了JavaEE 模式中像是作為”Data Access Object” 可能作為DAO來使用,當與 PersistenceExceptionTranslationPostProcessor 結合使用時,這樣注釋的類有資格獲得Spring轉換的目的。這個註解也是@Component 的一個特殊實現,允許實現類能夠被自動掃描到
-
@Component: 表明這個注釋的類是一個組件,當使用基於注釋的配置和類路徑掃描時,這些類被視為自動檢測的候選者。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { @AliasFor(annotation = Component.class) String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor(annotation = Component.class) String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Repository { @AliasFor(annotation = Component.class) String value() default ""; }
我們可以看到@Controller, @Service, @Repository這三個註解上都有@Component這個註解
也就是說,上面四個註解標記的類都能夠通過@ComponentScan 掃描到,上面四個註解最大的區別就是使用的場景和語義不一樣,比如你定義一個Service類想要被Spring進行管理,你應該把它定義為@Service 而不是@Controller因為我們從語義上講,@Service更像是一個服務的類,而不是一個控制器的類,@Component通常被稱作組件,它可以標註任何你沒有嚴格予以說明的類,比如說是一個配置類,它不屬於MVC模式的任何一層,這個時候你更習慣於把它定義為 @Component。@Controller,@Service,@Repository 的註解上都有@Component,所以這三個註解都可以用@Component進行替換。
同@Import註解組合使用
新建一個配置類,例如資料庫配置類:
@Configuration public class DatabaseConfig { @Bean public DataSource dataSource(){ return new DataSource(){ ... }; } }
然後在AppConfig中用@Import來導入配置類
@Configuration @Import(DatabaseConfig.class) public class AppConfig { @Autowired public DataSource dataSource; //注入的bean在DatabaseConfig.class中定義 @Bean IBean appBean(){ return new AppBean(); } }
最後執行:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); DatabaseConfig dataSourceConfig = context.getBean(DatabaseConfig.class);
可以看到只註冊了AppConfig.class,容器自動會把@Import指向的配置類初始化。
同@Profile註解組合使用
在配置類中可以申明@Profile註解,僅當滿足profile條件時,才會處理配置類,也可以將@Profile註解載入配置類中的每一個@Bean來實現更細粒度的條件控制。
@Configuration @Profile("develop") public class DatabaseConfig { @Bean public DataSource dataSource(){ return new DataSource(){...}; } }
嵌套使用@Configuration
在配置類中可以創建靜態內部類,並添加@Configuration註解,這樣上下文只需要註冊最外面的配置類,內部的配置類會自動被載入。這樣做省略了@Import,因為本身就在配置類內部,無需再特別指定了。
@Configuration public class AppConfig { @Autowired public DataSource dataSource; //注入的bean在內部定義 @Configuration public static class DatabaseConfig{ @Bean DataSource dataSource(){ return new DataSource() {...}; } } @Bean IBean appBean(){ return new AppBean(); } }
注意:任何嵌套的@Configuration 都必須是static 的。
@Lazy初始化
默認情況下,配置類中的Bean都隨著應用上下文被初始化,可以在配置類中添加@Lazy註解來延遲初始化,當然也可以在每個@Bean註解上添加,來實現更細粒度的控制。
@Configuration @Lazy//延時載入 public class AppConfig { @Bean IBean appBean(){ return new AppBean(); } }
配置類約束
- 配置類必須為顯式申明的類,而不能通過工廠類方法返回實例。允許運行時類增強。
- 配置類不允許標記final。
- 配置類必須全局可見(不允許定義在方法本地內部類中)
- 嵌套配置類必須申明為static 內部類
- @Bean方法不可以再創建新的配置類(所有實例都當做bean處理,不解析相關配置註解)
@Configuration源碼
ApplicationContext的refresh方法
在我之前的一篇文章spring5 源碼深度解析—–ApplicationContext容器refresh過程中寫過,Spring容器啟動時,即ApplicationContext介面實現類的對象實例執行refresh方法時,在Bean初始化完成之前,有一個擴展點,用來操作BeanFactory,來擴展對應的功能,比喻往BeanFactory中註冊BeanDefintion,我們回顧一下ApplicationContext的refresh函數:
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 //準備刷新的上下文 環境 4 prepareRefresh(); 5 //初始化BeanFactory,並進行XML文件讀取 6 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 7 //對beanFactory進行各種功能填充 8 prepareBeanFactory(beanFactory); 9 try { 10 postProcessBeanFactory(beanFactory); 11 //激活各種beanFactory處理器 12 invokeBeanFactoryPostProcessors(beanFactory); 13 //註冊攔截Bean創建的Bean處理器,這裡只是註冊,真正的調用實在getBean時候 14 registerBeanPostProcessors(beanFactory); 15 //為上下文初始化Message源,即不同語言的消息體,國際化處理 16 initMessageSource(); 17 //初始化應用消息廣播器,並放入“applicationEventMulticaster”bean中 18 initApplicationEventMulticaster(); 19 //留給子類來初始化其它的Bean 20 onRefresh(); 21 //在所有註冊的bean中查找Listener bean,註冊到消息廣播器中 22 registerListeners(); 23 //初始化剩下的單實例(非惰性的) 24 finishBeanFactoryInitialization(beanFactory); 25 //完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程,同時發出ContextRefreshEvent通知別人 26 finishRefresh(); 27 } 28 catch (BeansException ex) { 29 if (logger.isWarnEnabled()) { 30 logger.warn("Exception encountered during context initialization - " + 31 "cancelling refresh attempt: " + ex); 32 } 33 destroyBeans(); 34 cancelRefresh(ex); 35 throw ex; 36 } 37 finally { 38 resetCommonCaches(); 39 } 40 } 41 }
看到第12行,在初始化BeanFactory後,會激活各種beanFactory處理器,我們來看看invokeBeanFactoryPostProcessors方法
1 public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { 2 3 // Invoke BeanDefinitionRegistryPostProcessors first, if any. 4 // 1、首先調用BeanDefinitionRegistryPostProcessors 5 Set<String> processedBeans = new HashSet<>(); 6 7 // beanFactory是BeanDefinitionRegistry類型 8 if (beanFactory instanceof BeanDefinitionRegistry) { 9 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 10 // 定義BeanFactoryPostProcessor 11 List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); 12 // 定義BeanDefinitionRegistryPostProcessor集合 13 List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); 14 15 // 循環手動註冊的beanFactoryPostProcessors 16 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { 17 // 如果是BeanDefinitionRegistryPostProcessor的實例話,則調用其postProcessBeanDefinitionRegistry方法,對bean進行註冊操作 18 if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { 19 // 如果是BeanDefinitionRegistryPostProcessor類型,則直接調用其postProcessBeanDefinitionRegistry 20 BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; 21 registryProcessor.postProcessBeanDefinitionRegistry(registry); 22 registryProcessors.add(registryProcessor); 23 } 24 // 否則則將其當做普通的BeanFactoryPostProcessor處理,直接加入regularPostProcessors集合,以備後續處理 25 else { 26 regularPostProcessors.add(postProcessor); 27 } 28 } 29 //略.... 30 } 31 32 // 2、如果不是BeanDefinitionRegistry的實例,那麼直接調用其回調函數即可-->postProcessBeanFactory 33 else { 34 // Invoke factory processors registered with the context instance. 35 invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); 36 } 37 //略.... 38 }
我們看看第21行,看看其實現類,如下截圖,發現其中有一個ConfigurationClassPostProcessor,這個類就是本章的重點
ConfigurationClassPostProcessor這個BeanFactoryPostProcessor,來開啟整個@Configuration註解的系列類的載入的,即開啟基於@Configuration的類配置代替beans標籤的容器配置的相關bean的載入。
ConfigurationClassPostProcessor
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); //解析Java類配置bean processConfigBeanDefinitions(registry); }
processConfigBeanDefinitions(registry)處理邏輯:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); //所有已經註冊的bean String[] candidateNames = registry.getBeanDefinitionNames(); //遍歷bean定義資訊 for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } //1.如果當前的bean是Javabean配置類(含有@Configuration註解的類),則加入到集合configCandidates中, else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found // 沒有@Configuration註解的類,直接退出 if (configCandidates.isEmpty()) { return; } // 多個Java配置類,按@Ordered註解排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class //初始化一個ConfigurationClassParser解析器,可以解析@Congiguration配置類 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { //2.解析Java配置類 parser.parse(candidates); //主要校驗配置類不能使用final修飾符(CGLIB代理是生成一個子類,因此原先的類不能使用final修飾) parser.validate(); //排除已處理過的配置類 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()); } //3.載入bean定義資訊,主要實現將@bean @Configuration @Import @ImportResource @ImportRegistrar註冊為bean this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); //清空已處理的配置類 candidates.clear(); //再次獲取容器中bean定義數量 如果大於 之前獲取的bean定義數量,則說明有新的bean註冊到容器中,需要再次解析 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); //新註冊的bean如果也是@Configuration配置類,則添加到數據,等待解析 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }
processConfigBeanDefinitions整個方法可以大體劃分為三個階段:
- 從容器中獲取和Configuration有關係的BeanDefinition
- 以該BeanDefinition為起點,進行解析操作,得到解析結果集
- 將解析到的結果集載入到容器中,即構造成一個BeanDefinition放到容器中待初始化
1、判斷類是否與@Configuration有關
在上面第1步中,有@Configuration註解的會加入到集合當中,這個判斷是在ConfigurationClassUtils#checkConfigurationClassCandidate
當中實現
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { String className = beanDef.getBeanClassName(); if (className == null || beanDef.getFactoryMethodName() != null) { return false; } //獲取註解元數據資訊 AnnotationMetadata metadata; if (beanDef instanceof AnnotatedBeanDefinition && className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); } else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); metadata = new StandardAnnotationMetadata(beanClass, true); } else { try { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); metadata = metadataReader.getAnnotationMetadata(); } catch (IOException ex) { return false; } } // 查找當前註解是否是與@Configuration相關 // 該方法還會判斷該註解上的註解是否有@Configuration,一直往上尋找 // 因為有的註解為複合註解 if (isFullConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); } // 查找當前註解上是否有ComponentScan、Component、Import、ImportResource註解 //如果沒有則查找Bean註解,同上,一直往上查找 else if (isLiteConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); } else { return false; } return true; }
我們看看isFullConfigurationCandidate和isLiteConfigurationCandidate
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) { return metadata.isAnnotated(Configuration.class.getName()); }
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) { // Do not consider an interface or an annotation... if (metadata.isInterface()) { return false; } // Any of the typical annotations found? for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true; } } // Finally, let's look for @Bean methods... try { return metadata.hasAnnotatedMethods(Bean.class.getName()); } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex); } return false; } } private static final Set<String> candidateIndicators = new HashSet<>(8); static { candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName()); }
2、解析Java配置類parser.parse(candidates)
parser.parse(candidates)方法最終調用processConfigurationClass方法來處理@Configuration配置類,ConfigurationClassParser. processConfigurationClass()方法實現程式碼如下:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { //判斷是否需要解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } //判斷同一個配置類是否重複載入過,如果重複載入過,則合併,否則從集合中移除舊的配置類,後續邏輯將處理新的配置類 ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // 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. SourceClass sourceClass = asSourceClass(configClass); do { //【真正解析配置類】 sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); //再次添加到到集合中 this.configurationClasses.put(configClass, configClass); }
doProcessConfigurationClass方法主要實現從配置類中解析所有bean,包括處理內部類,父類以及各種註解
ConfigurationClassParser. doProcessConfigurationClass()解析配置類邏輯如下:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { //遞歸處理任何成員(嵌套)類 processMemberClasses(configClass, sourceClass); // 處理@PropertySource註解 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"); } } // 處理@ComponentScan //獲取@ComponentScan註解資訊 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) { // 按@CmponentScan註解掃描bean Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 遍歷掃描出的bean定義是否是配置類bean for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } //若果掃描出的bean定義是配置類(含有@COnfiguration),則繼續調用parse方法,內部再次調用doProcessConfigurationClas(),遞歸解析 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } //處理@Import註解 processImports(configClass, sourceClass, getImports(sourceClass), true); //處理@ImportResource註解 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註解 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { //將解析出的所有@Bean註解方法添加到configClass配置類資訊中 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } //處理介面中所有添加@Bean註解的方法,內部通過遍歷所有介面,解析得到@Bean註解方法,並添加到configClass配置類資訊中 processInterfaces(configClass, sourceClass); // 如果有父類,則返回父類,遞歸執行doProcessConfigurationClass()解析父類 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; }
下面看兩個很重要的註解@Bean和@ComponentScan的實現過程
-
@ComponentScan註解解析過程
Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
@ComponentScan註解解析,從上面的程式碼可以看出@ComponentScan註解解析通過調用ComponentScanAnnotationParser的parse方法完成,而parse()方法內部處理了一些scanner屬性(過濾器設置)和basePackages包名處理,最終通過調用ClassPathBeanDefinitionScanner.doScan方法實現掃面工作
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); 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")); 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); } } 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); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); }
doScan掃描basePackages下所有bean
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) { //根據basePackage載入包下所有java文件,並掃描出所有bean組件 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //遍歷beandefition for (BeanDefinition candidate : candidates) { //解析作用域Scope 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); } //通用註解解析到candidate結構中,主要是處理Lazy, primary DependsOn, Role ,Description這五個註解 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //檢查當前bean是否已經註冊,不存在則註冊 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 註冊到ioc容器中,主要是一些@Component組件,@Bean註解方法並沒有在此處註冊,beanname和beandefinition 鍵值對 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
ClassPathBeanDefinitionScanner.scanCandidateComponents實現bean定義資訊掃描
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // @ComponentScan("com.sl.springlearning.extension")包路徑處理:packageSearchPath = classpath*:com/sl/springlearning/extension/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //獲取當前包下所有的class文件 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); //按照scanner過濾器過濾,比如配置類本身將被過濾掉,沒有@Component等組件註解的類將過濾掉 //包含@Component註解的組件將創建BeanDefinition 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); } 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; }
- @Bean註解解析過程
retrieveBeanMethodMetadata方法實現了@Bean方法的解析,但並未將實現bean實例的創建。
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) { AnnotationMetadata original = sourceClass.getMetadata(); //獲取所有@Bean註解的方法 Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName()); // 如果配置類中有多個@Bean註解的方法,則排序 if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) { // Try reading the class file via ASM for deterministic declaration order... // Unfortunately, the JVM's standard reflection returns methods in arbitrary // order, even between different runs of the same application on the same JVM. try { AnnotationMetadata asm = this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata(); Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName()); if (asmMethods.size() >= beanMethods.size()) { Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size()); for (MethodMetadata asmMethod : asmMethods) { for (MethodMetadata beanMethod : beanMethods) { if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) { selectedMethods.add(beanMethod); break; } } } if (selectedMethods.size() == beanMethods.size()) { // All reflection-detected methods found in ASM method set -> proceed beanMethods = selectedMethods; } } } catch (IOException ex) { logger.debug("Failed to read class file via ASM for determining @Bean method order", ex); // No worries, let's continue with the reflection metadata we started with... } } return beanMethods; }
3.載入bean定義資訊 this.reader.loadBeanDefinitions(configClasses)
回到ConfigurationClassPostProcessor#processConfigBeanDefinitions
方法,當調用完parse方法之後,能得到一批ConfigurationClass集合,但是這時候只是獲取到,而容器中還沒有對應的註冊資訊,那麼接下來就是對這批集合進行註冊處理
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法的功能就是將之前解析出的configClasses配置類資訊中所有配置相關的資訊添加到spring的bean定義,主要是配置類中的@Bean註解方法,配置類@ImportResource和@Import(實現ImportBeanDefinitionRegistrar介面方式)的bean註冊
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法 實現邏輯如下:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } }
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; } //與@Import註解相關 if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //將@Bean方法註冊為bean for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //將configClass中中ImportResource指定的資源註冊為bean loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); //將configClass中ImportedRegistrar註冊為bean loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
主要看下loadBeanDefinitionsForBeanMethod方法
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { ConfigurationClass configClass = beanMethod.getConfigurationClass(); MethodMetadata metadata = beanMethod.getMetadata(); //獲取方法名 String methodName = metadata.getMethodName(); // Do we need to mark the bean as skipped by its condition? if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { configClass.skippedBeanMethods.add(methodName); return; } if (configClass.skippedBeanMethods.contains(methodName)) { return; } //獲取@Bean註解的元數據資訊 AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class); Assert.state(bean != null, "No @Bean annotation attributes"); // Consider name and any aliases //獲取@Bean註解是否有name屬性,如@Bean(name = "myBean") List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name"))); //默認bean的名稱和方法名稱相同,但是如果設置了name,就取name作為beanName String beanName = (!names.isEmpty() ? names.remove(0) : methodName); //創建一個BeanMethod的BeanDefinition ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata); beanDef.setResource(configClass.getResource()); beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource())); //設置工廠方法 //後期Bean的實例化,getBean的時候,會判斷BeanMethod是否存在FactoryMethod,如果存在,就使用反射調用工廠方法,返回工廠方法中的對象 if (metadata.isStatic()) { // static @Bean method beanDef.setBeanClassName(configClass.getMetadata().getClassName()); beanDef.setFactoryMethodName(methodName); } else { // instance @Bean method beanDef.setFactoryBeanName(configClass.getBeanName()); beanDef.setUniqueFactoryMethodName(methodName); } //.... this.registry.registerBeanDefinition(beanName, beanDefToRegister); }
上面只列出了核心程式碼,主要是構造了BeanDefinition,然後註冊進容器,而BeanDefinition的一些屬性則是由註解中獲取,這部分程式碼省略。
另外,可以看到@Bean的方式構造的BeanDefinition的時候,與普通的不同,這種方式是會設置工廠方法去初始化,也就是說,AppConfig 類下的appBean方法被Spring當成一個工廠方法,也就是說這種方式與下列的初始化方式原理類似:
<bean id="appConfig" class="com.example.springboot.springbootdemo.bean.AppConfig"/> <bean id="appBean" factory-bean="appConfig" factory-method="appBean"></bean>
總結
處理邏輯理了一遍後,看一下ConfigurationClassPostProcessor處理器解析@configuration配置類主要過程:
1. Spring容器初始化時註冊默認後置處理器ConfigurationClassPostProcessor
2. Spring容器初始化執行refresh()方法中調用ConfigurationClassPostProcessor
3. ConfigurationClassPostProcessor處理器藉助ConfigurationClassParser完成配置類解析
4. ConfigurationClassParser配置內解析過程中完成嵌套的MemberClass、@PropertySource註解、@ComponentScan註解(掃描package下的所有Class並進行迭代解析,主要是@Component組件解析及註冊)、@ImportResource、@Bean等處理
5. 完成@Bean註冊, @ImportResource指定bean的註冊以及@Import(實現ImportBeanDefinitionRegistrar介面方式)的bean註冊
6.有@Bean註解的方法在解析的時候作為ConfigurationClass的一個屬性,最後還是會轉換成BeanDefinition進行處理, 而實例化的時候會作為一個工廠方法進行Bean的創建