• Spring源码(十)configurationClassPostProcessor下的@import注解解析


    Spring源码(十)configurationClassPostProcessor下的@Import注解解析

    @Import

    @import代码形式

    对于import注解,用springBoot的SpringBootapplication的自动配置原理写一下,因为比较方便,有现成的@import。
    在这里插入图片描述

    一般我们回答springboot的自动装配原理一般会将注解,会说@SpringBootapplication里面有三个注解,最重要的是@EnableAutoConfiguration,而@EnableAutoConfiguration里面有@Import(AutoConfigurationImportSelector.class),找到AutoConfigurationImportSelector这个类的getCandidateConfigurations方法就能将Spring.factories下的enableautoConfiguration读进来。但是怎么找到getCandidateConfigurations方法呢?我们换一个角度去看这个问题。从configurationClassPostProcessor下看。

    首先会进入到doProcessConfigurationClass处理的注解类下,有个方法处理@import的,processImports方法。
    在这里插入图片描述
    这里面的参数也比较重要,首先看下getImports参数方法。
    在这里插入图片描述
    这里定义了两个集合,然后调用了collectImports(sourceClass, imports, visited);方法。
    在这里插入图片描述
    这里的最下面的if判断,如果遍历的注释没有@import注解,那么就需要进入递归的逻辑里面去了,因为有可能在@a下的@b下才有@import,而这个时候只能找到@b,不能找到@import。在@springbootApplication里,实际上里面有三个注解,这个三个注解并没有@import,实际上在@enableautoConfiguration里面有@import注解,所以这里需要递归处理不带@import的注解。
    这里实际上在getImports的imports集合返回了2个,在enableAutoConfiguration注解下有个autoConfigurationPackage注解,里面也有个@import,这里我们忽略掉。
    在这里插入图片描述

    然后执行processImports(configClass, sourceClass, getImports(sourceClass), true);方法。

    
    	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 (selector instanceof DeferredImportSelector) {
    							this.deferredImportSelectorHandler.handle(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));
    					}
    				}
    			}
    
    • 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

    在processImports方法中,我们注意到实际上import有不同类型,这里需要区别对待,而在 @EnableAutoConfiguration下的@import的类为DeferredImportSelector,而DeferredImportSelector继承了ImportSelector。所以这里走延迟导入,实例化创建了对象,并加入到deferredImportSelectors集合中去。延迟处理是在@configuration完成之后再处理这个方法。
    继续回到doProcessConfigurationClass方法中来,处理完毕processImports。由于我们采用了延迟处理的方案,所以需要全部走完这个方法,一直返回到parse还没有分annotation、abstractBeanDefinition时。

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
    		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);
    			}
    		}
    
    		this.deferredImportSelectorHandler.process();
    	}
    
    • 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

    看上面方法最后一行this.deferredImportSelectorHandler.process();这里就是对加入到延迟处理集合中的方法进行处理了。看下process方法。

    		public void process() {
    			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    			this.deferredImportSelectors = null;
    			try {
    				if (deferredImports != null) {
    					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
    					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
    					deferredImports.forEach(handler::register);
    					handler.processGroupImports(); 
    				}
    			}
    			finally {
    				this.deferredImportSelectors = new ArrayList<>();
    			}
    		}
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    进入到processGroupImports处理方法下,在进入getImports是核心处理方法。

    		public Iterable<Group.Entry> getImports() {
    			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
    				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
    						deferredImport.getImportSelector());
    			}
    			return this.group.selectImports();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    进入到process方法。

    		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
    					() -> String.format("Only %s implementations are supported, got %s",
    							AutoConfigurationImportSelector.class.getSimpleName(),
    							deferredImportSelector.getClass().getName()));
    			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    			this.autoConfigurationEntries.add(autoConfigurationEntry);
    			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    				this.entries.putIfAbsent(importClassName, annotationMetadata);
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    进入到getAutoConfigurationEntry方法中。

    	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    			AnnotationMetadata annotationMetadata) {
    		if (!isEnabled(annotationMetadata)) {
    			return EMPTY_ENTRY;
    		}
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    		configurations = removeDuplicates(configurations);
    		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    		checkExcludedClasses(configurations, exclusions);
    		configurations.removeAll(exclusions);
    		configurations = filter(configurations, autoConfigurationMetadata);
    		fireAutoConfigurationImportEvents(configurations, exclusions);
    		return new AutoConfigurationEntry(configurations, exclusions);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意,process方法与getAutoConfigurationEntry已经在AutoConfigurationImportSelector类了,在getAutoConfigurationEntry中有一个方法getCandidateConfigurations,这就是我们需要的配置。

    		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    				getBeanClassLoader());
    		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    				+ "are using a custom packaging, make sure that file is correct.");
    		return configurations;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    进入loadFactoryNames方法,

        public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }
    
    • 1
    • 2
    • 3
    • 4

    进入到loadSpringFactories方法

     private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
            if (result != null) {
                return result;
            } else {
                try {
                    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                    LinkedMultiValueMap result = new LinkedMultiValueMap();
    
                    while(urls.hasMoreElements()) {
                        URL url = (URL)urls.nextElement();
                        UrlResource resource = new UrlResource(url);
                        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                        Iterator var6 = properties.entrySet().iterator();
    
                        while(var6.hasNext()) {
                            Entry<?, ?> entry = (Entry)var6.next();
                            String factoryClassName = ((String)entry.getKey()).trim();
                            String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                            int var10 = var9.length;
    
                            for(int var11 = 0; var11 < var10; ++var11) {
                                String factoryName = var9[var11];
                                result.add(factoryClassName, factoryName.trim());
                            }
                        }
                    }
    
                    cache.put(classLoader, result);
                    return result;
                } catch (IOException var13) {
                    throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
                }
            }
        }
    
    • 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

    上面这个方法就能将所有的beanFactories文件都加载回来,包含enableautoConfiguration。然后返回到getOrDefault,这个时候完成对enableautoConfiguration的识别。回到 getAutoConfigurationEntry方法里,去除掉一些不用的,最后包含20多个。

    好了,@import结束。

    @import总结

    在BFPP下的,configurationClassPostProcessor包含了@import注解的解析,从启动类里面一个个挨个查找,最后找到@import的tutoConfigurationImportSelector的类,在这个类进行解析的时候,有个延迟处理的属性,在延迟处理时,有个getImports方法,这个方法有个getAutoConfigurationEntry,entry对象会调用getCandidateConfigurations方法,这个方法就会把enableconfiguration整个都加载回来,完成自动装配的环节。

  • 相关阅读:
    实现Promise的原型方法--前端面试能力提升
    Github上都在疯找的京东内部“架构师进阶手册”终于来了
    浅谈 AOP 什么是 AOP ?
    linux关于ssh免密登录、known_hosts文件
    flink以增量+全量的方式更新广播状态
    pyqt5的组合式部件制作(一)
    [solidity]合约调用合约
    王道操作系统---操作系统运行环境
    SpringCloud 学习笔记总结 (二)
    【C++笔试强训】第二十五天
  • 原文地址:https://blog.csdn.net/qq_40223516/article/details/126331409