• 【Spring-3.4】解析配置类,标注import注解


    		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    
    • 1

    先看下org.springframework.context.annotation.ConfigurationClassParser#getImports方法。

    	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    		Set<SourceClass> imports = new LinkedHashSet<>();
    		Set<SourceClass> visited = new LinkedHashSet<>();
    		collectImports(sourceClass, imports, visited);
    		return imports;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
    			throws IOException {
    
    		if (visited.add(sourceClass)) {
    			// 遍历配置类的注解,如果没有Import注解,进深入注解中,再次找。
    			for (SourceClass annotation : sourceClass.getAnnotations()) {
    				String annName = annotation.getMetadata().getClassName();
    				// 如果该注解不是Import,在进入到该注解中,继续找。
    				if (!annName.equals(Import.class.getName())) {
    					collectImports(annotation, imports, visited);
    				}
    			}
    			// 最后将import注解中的信息,封装为SourceClass,返回;如果没有Import,返回一个空集合。返回的是import注解中的类对应的sourceClass对象
    			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    递归遍历得到配置类中的import注解的信息。将这些信息封装为SourceClass。所以根据这个方法,可以找到嵌套在注解中的import注解。


    进入org.springframework.context.annotation.ConfigurationClassParser#processImports方法。

    	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
    			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
    			boolean checkForCircularImports) {
    		// 如果没有import注解信息,直接返回,不用任何操作。
    		if (importCandidates.isEmpty()) {
    			return;
    		}
    		// 循环导入,逻辑不太清楚了,
    		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    		}
    		else {
    			this.importStack.push(configClass);
    			try {
    				// 遍历所有的import注解的信息。
    				for (SourceClass candidate : importCandidates) {
    					// 如果类是ImportSelector类型的
    					if (candidate.isAssignable(ImportSelector.class)) {
    						// 得到import注解中的类的元信息
    						Class<?> candidateClass = candidate.loadClass();
    						// 直接通过元信息,进行实例化了。
    						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
    								this.environment, this.resourceLoader, this.registry);
    						Predicate<String> selectorFilter = selector.getExclusionFilter();
    						if (selectorFilter != null) {
    							exclusionFilter = exclusionFilter.or(selectorFilter);
    						}
    						// 如果是DeferredImportSelector类型的,先放在一个类似的集合中,处理完配置完配置类,统一处理。
    						if (selector instanceof DeferredImportSelector) {
    							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    						}
    						else {
    						// 如果不是就调用selectImports方法,发返回的是需要注入到容器的类的全限定类名
    							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
    							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
    							// 继续import的逻辑,这块有一个递归调用,这里的作用是找到真正要注入的类,如果importClassNames中有一个ImportSelector类型的类,要注入的是该类中selectImport方法返回的类,而给该类,所有得有一个递归操作。
    							processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    						}
    					}
    					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    						// 如果是ImportBeanDefinitionRegistrar类型,同样是加入到集合中,统一处理。
    						
    						Class<?> candidateClass = candidate.loadClass();
    						ImportBeanDefinitionRegistrar registrar =
    								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
    										this.environment, this.resourceLoader, this.registry);
    						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
    					}
    					else {
    						// 如果什么也不是,就作为配置类处理,进行解析配置的逻辑。
    						// process it as an @Configuration class
    						this.importStack.registerImport(
    								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    						processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
    					}
    				}
    			}
    			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();
    			}
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    主要有三大块的逻辑,import注解中类的类型,
    ImportSelector:如果是子类型DeferredImportSelector放入集合中,统一处理;不是的话,调用selectImports方法,返回的类名称再次走import的逻辑,为了找到真正要注入的类(如果selectImports方法返回的类中有类型是ImportSelector,A,要注入A的selectImports放回类而非该类A)。
    ImportBeanDefinitionRegistrar:放入集合中,统一处理,此时传入的注解信息是配置类的,即用@configuration注解标注的类的信息
    都不是的话;就作为配置类,走解析配置类的逻辑。

    如果是ImportSelector类型的,返回的全限定类名是要注入到容器中的,作为配置类处理。
    如果是ImportBeanDefinitionRegistrar,可以自己操作注册的bd。
    如果是不容的类型,即就是一个简单的类名,会直接当配置类处理。
    以当前解析的逻辑就是把该配置类放到配置类的缓存中。

    看完源码在看下import注解的使用规则,是不是一目了然呢?

    import注解的使用

  • 相关阅读:
    DOM树的遍历-----修改样式,选择元素,创建和删除节点
    splay平衡树
    RoCE、IB和TCP等网络的基本知识及差异对比
    Linux主机间的相互免秘钥
    Python Opencv实践 - 轮廓特征(最小外接圆,椭圆拟合)
    安装JDK(不同环境下都有)
    【大数据架构(3)】Lambda vs. Kappa Architecture-选择你需要的架构
    国际标准ISO/IEC 30144: 2020在智能变电站辅助监测中的应用研究
    C语言重点突破(2)指针(二)
    如何构建 Protocol Buffers(protobuf)并解决常见问题
  • 原文地址:https://blog.csdn.net/qq_34501351/article/details/118397356