spring高質量系列-IOC (二)
bean放入到ioc容器 需要先解析bean的xml或者相關注解獲取到beanDefinition然後根據beanDefinition生產對應的bean例項並放入容器中。
容器中都是存放的都是單例bean
BeanDefinition相關介面和類的介紹
- BeanDefinition繼承了AttributeAccessor(存放和取一些key-value)和BeanMetadataElement(可以配置bean的後設資料資源,即獲取beanDefinition的資料來源,比如xml的標籤和註解)
- 2.一級實現類或者子介面:AbstractBeanDefinition(完成了大部分的方法)和AnnotatedBeanDefinition(子介面,增加了獲取註解後設資料和方法的後設資料)
- 3.載入beanDefinition:BeanDefinitionReader介面讀取資源loadBeanDefinition,AbstractBeanDefinitionReader(實現了其大部分方法),XmlBeanDefinitionReader(對xml進行解析獲取bean)。AnnotatedBeanDefinitionReader是一個單獨的類和BeanDefinitionReader沒有關係,但是其提供的方法和其相似
- 註冊:AliasRegistry(將beanName和別名進行對映),BeanDefinitionRegistry(提供了對beanName和BeanDefinition的贈刪改查的操作)
因為IOC容器想要最終獲取beanName和BeanDefinition需要三步驟
- 1.BeanDefinition的Resource定位
- 2.Resource的載入
- 3.註冊進入IOC容器
筆者對註解和xml 兩個解析細節統一進行詳細闡述.
此處我們講的beanDefinition是我們開發人員通過xml配置 或者@Component等註解注入的beanDefinition,這裡不包含spring在啟動時自動注入的一系列beanDefinition
筆者終於找到了spring.xml和我們自定義註解bean是什麼時候處理
BeanDefinition的Resource定位
- AbstractApplicationContext--->refresh()---> invokeBeanFactoryPostProcessors(beanFactory)-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors-->
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry)-->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry-->processConfigBeanDefinitions-->ConfigurationClassParser parser () --->ConfigurationClassParser processConfigurationClass--->ConfigurationClassParser doProcessConfigurationClass
- AbstractApplicationContext--->refresh()---> invokeBeanFactoryPostProcessors(beanFactory)-->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors-->
- 在ConfigurationClassParser類的 方法 processConfigurationClass,將我們的@Configuration類包裝成source
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
SourceClass asSourceClass(@Nullable Class<?> classType) throws IOException {
if (classType == null) {
return new SourceClass(Object.class);
}
try {
// Sanity test that we can reflectively read annotations,
// including Class attributes; if not -> fall back to ASM
for (Annotation ann : classType.getAnnotations()) {
AnnotationUtils.validateAnnotation(ann);
}
return new SourceClass(classType);
}
catch (Throwable ex) {
// Enforce ASM via class name resolution
return asSourceClass(classType.getName());
}
}
上述程式碼就是獲取@Configuration註解類的class同時校驗該class上面的註解是否配置正確即註解的屬性是否合理。並把他包裝成source然後交給doProcessConfigurationClass進行處理
Resource和beanDefinition的載入
概述
- 1 ConfigurationClassParser doProcessConfigurationClass(),即對獲取到的@Configuration 註解進行處理,而我們的@ImportResource註解則是放在啟動類上,我們匯入spring.xml是依靠@ImportResource,即此時在把相關soucre載入
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
首先遞迴處理任何成員(巢狀)類,即從sourceClass獲取內部類看是否屬於@configuration,屬於就遞迴呼叫doProcessConfigurationClass處理子類
processMemberClasses(configClass, sourceClass);
處理@PropertySource 註解,將properties檔案屬性注入到具體bean中,但是前提是environment必須是ConfigurableEnvironment 否則忽略
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 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());
}
}
}
}
處理 any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
處理 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);
}
}
處理 individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 處理實現介面的方法
processInterfaces(configClass, sourceClass);
// 處理 父類, 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;
}
其中@PropertySources和@PropertySource 以及@ComponentScans和@ComponentScan 分別是集合和單個元素的關係
處理類中類
- 首先遞迴處理任何成員(巢狀)類,即從sourceClass獲取內部類看是否屬於@configuration,屬於就遞迴呼叫doProcessConfigurationClass處理子類
處理@PropertySource 註解
- 1.將properties檔案屬性注入到具體bean中,但是前提是environment必須是ConfigurableEnvironment 否則忽略
- 2.最終把PropertySource放入propertySourceList
- 3.更多的細節請看第三節
處理@ComponentScan
- 1.首先從@ComponentScans和@ComponentScan獲取,而我們的啟動類上有註解@SpringBootApplication,其內部定義了預設的scanBasePackages()和scanBasePackageClasses(),我們可以從中獲取到@ComponetScan
- 2.
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
的主要邏輯是設定ClassPathBeanDefinitionScanner的屬性:registry,resourceLoader,beanNameGenerator,scopedProxyMode,resolverClass,resourcePattern,includeFilters(必須是@Component和@ManagedBean註解才可以),excludeFilters(componentScan註解所在的類不需要掃描),lazyInit,basePackages。 - 其中獲取basePackages的邏輯是:從basePackages和basePackageClasses獲取他們的包名字放入到集合中,如果集合還是為空就把我們註解類的包名放入進去。
-
scanner.doScan(StringUtils.toStringArray(basePackages));
這是最終開始掃描的我們包的操作
-
-
ClassPathScanningCandidateComponentProvider
類的Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath)
是獲取到了我們注入的bean資源
-
- 6.獲取到資源後(也就是basePackages下面的class檔案),通過檔案流載入class然後獲取class和註解的相關屬性。然後獲取註解是@Component和@ManagedBean的類
- 將上述獲取的候選類包裝成ScannedGenericBeanDefinition,繼續判斷該類是否不依賴其他bean且該類要麼是具體的實現類 要麼是抽象類的同時其方法上面還有@Lookup的註解
- 8.通過scopeMetadataResolver獲取ScopeMetadata 即判斷是否是單例 同時是否需要包裝成proxy預設是不需要。
- 9 .依次判斷候選BeanDefinition是否是AbstractBeanDefinition和AnnotatedBeanDefinition 然後分別呼叫
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
和AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
其中前者將beanDefinitionDefaults設定到候選BeanDefinition,後者是將註解上面的一些屬性設定到候選BeanDefinition - 10.將beanName和候選BeanDefinition包裝成definitionHolder,同時看是否需要代理然後將該候選bean註冊到beanFactory,但是目前看@bean 和@component 都沒有提供ScopeMetadata 的屬性配置
- 11.
ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass)
就是建立bean的proxy的地方。 - 12.建立動態代理的方式是把原先的BeanDefinition和targetBeanName(在原來的名字上加個字首)註冊到beanFactory中,且該BeanDefinition的AutowireCandidate和Primary屬性被置為false
- 13.建立一個proxyDefinition 其beanClass是ScopedProxyFactoryBean,然後將原先的BeanDefinition和proxyDefinition 進行繫結 最終返回一個definitionHolder是proxyDefinition 和originalBeanName組成的
- 14 若返回的bean是屬於@Component ,@ComponentScan ,@Import,@ImportResource.繼續呼叫ConfigurationClassParser的parse方法進行重新處理
- 15我們通過上述可知@ComponentScan 有ScopedProxyMode 可以指定類是否啟用代理。
處理 @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);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 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();
}
}
}
- 0.也會堅持是否有迴圈匯入,尋找的@Import,ImportSelector介面,ImportBeanDefinitionRegistrar介面 都是作為@Import一樣處理
- 1.可以匯入@Configuration,ImportSelector介面,ImportBeanDefinitionRegistrar介面
或者常規的 regular component classes 。 - 2.@ImportSelector可以返回一些列的需要匯入的類
- 3.@ImportBeanDefinitionRegistrar 可以直接向beanFactory註冊beanDefinition
- 4.經過測試@Configuration類都會被Spring用Cglib增強,但是具體在哪增加筆者尚未知曉@Configuration類下面的@Bean 不會被增強,且Component類下面的@Bean註解不會注入SpringIOC容器
- 下面是@Import ImportSelector 類 importingClassMetadata是我們@Import註解所在的類,最終可以匯入我們想匯入的類
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
System.out.println(importingClassMetadata.toString());
String[] importString = new String[]{"testImport.ImportBean"};
return importString;
}
}
- 下面是ImportBeanDefinitionRegistrar 可以在bean例項化前bean進行增刪改查
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition =new RootBeanDefinition();
((RootBeanDefinition) beanDefinition).setBeanClass(ImportBean.class);
registry.registerBeanDefinition("xujieBean",beanDefinition);
}
}
處理@ImportResource
- 主要作用就是把xml的地址和readerClass放入importedResources集合,留作後期解析使用
處理@Bean
- 1.獲取當前正在處理的class的方法上有bean的,獲取到MethodMetadata,然後將MethodMetadata和當前class 包裝成BeanMethod 加入到當前class的beanMethods
processInterfaces(configClass, sourceClass)
- 看樣子是處理當前configClass實現的介面中的子類且加@Bean的方法(該註解放在父類或者子類方法都可以),但是這個方法至今筆者沒太看懂他的意義
Process superclass,
-1 .即處理當前configclass 是否還有父類且非介面,如果有繼續按照之前的流程進行處理。
此篇文章還留有疑問,後期筆者會在研讀過程中進行解說
1.缺少spring.xml在哪一步載入
2.@configuration在哪一步開始進行了動態代理
- 何時處理beanMethods集合中的@Bean方法
4 .如何讓propertySource生效
總結:doProcessConfigurationClass處理了類中類,@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean,父類和子類,
其中只有@ComponentScan這一塊遇到@Component註解才會註冊bean,其他都存入其內部集合中留待真正的註冊beanDefinition,詳情請關注下篇文章。
相關文章
- Spring Ioc原始碼分析系列--Bean例項化過程(二)Spring原始碼Bean
- Spring 高階原始碼核心思想:Spring IoCSpring原始碼
- Spring原始碼分析之IoC(二)Spring原始碼
- Spring Cloud 系列(二)Eureka 高可用註冊中心SpringCloud
- Spring框架系列(7) - Spring IOC實現原理詳解之IOC初始化流程Spring框架
- iOS 編寫高質量Objective-C程式碼(二)iOSObjectC程式
- iOS編寫高質量Objective-C程式碼(二)iOSObjectC程式
- Spring框架系列(6) - Spring IOC實現原理詳解之IOC體系結構設計Spring框架
- Spring原始碼學習之路---IOC初探(二)Spring原始碼
- react 高效高質量搭建後臺系統 系列 —— 登入React
- Spring系列:基於XML的方式構建IOCSpringXML
- Spring IOC原始碼研究筆記(2)——ApplicationContext系列Spring原始碼筆記APPContext
- Spring - IOCSpring
- Spring框架系列(3) - 深入淺出Spring核心之控制反轉(IOC)Spring框架
- Spring系列之IOC的原理及手動實現Spring
- 高質量前端資源前端
- Spring系列第二講 控制反轉(IoC)與依賴注入(DI),晦澀難懂麼?Spring依賴注入
- Spring AOP IOCSpring
- Spring-IOCSpring
- react 高效高質量搭建後臺系統 系列 —— 腳手架搭建React
- react 高效高質量搭建後臺系統 系列 —— 表格的封裝React封裝
- react 高效高質量搭建後臺系統 系列 —— 請求資料React
- react 高效高質量搭建後臺系統 系列 —— 系統佈局React
- react 高效高質量搭建後臺系統 系列 —— 前端許可權React前端
- Spring 系列(二):Spring MVC的父子容器SpringMVC
- 頂級質量高仿包
- 360首提IOC質量評估標準 提升威脅情報IOC應用水平
- 圈複雜度那些事兒-前端程式碼質量系列文章(二)複雜度前端
- 理解Spring(一):Spring 與 IoCSpring
- 致同:以黨建工作的高質量引領業務發展高質量
- 從原始碼看Spring中IOC容器的實現(二):IOC容器的初始化原始碼Spring
- spring-IOC容器原始碼分析(二)BeanDefinition註冊流程Spring原始碼Bean
- Spring使用之IOCSpring
- Spring(IOC&DI)Spring
- spring ioc和aopSpring
- Spring框架IOC容器Spring框架
- Spring的IOC容器Spring
- 【Spring】IOC&DISpring