springBoot 自定义starter解析原理

130 阅读4分钟

看这篇文章之前,一定要先看下SpringBoot Bean的注册与解析,并且还要记得解析Bean中关键的几个方法,文章地址:juejin.cn/post/689118…

在上一篇文章中我们自己写了一个starter,并且将它配置到springBoot项目中生效,那么现在问题来了,springBoot是怎么知道我们写了配置类的呢??又是什么时候被解析呢??

1:在Java中一切皆对象,那么配置类也是个对象,既然要解析我们的配置类,那么肯定是在解析Bean的时候扫描到我们的配置类,想来想去好像只有这一步能够去扫描我们的配置类并且解析了。那么我们现在回到解析Bean的源码中

//这个方法是在ConfigurationClassParse类中,具体解析流程可以看我的另一篇文章,看我主页
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
       
        
        // 这个方法是处理@Import注解的
        processImports(configClass, sourceClass, getImports(sourceClass), true);    
        return null;
    }
    
 在SpringBoot启动类的的@SpringBootApplication注解里面有一个@EnableAutoConfiguration注解,点进这个注解发现有一个
 @Import(AutoConfigurationImportSelector.class)的注解
 
 所以 processImports(configClass, sourceClass, getImports(sourceClass), true); 这个方法就会实例化
 AutoConfigurationImportSelector.class这个类,凡是使用@Import注解实现的类都会被加入到ConfigurationClassParse类的
 this.deferredImportSelectors的一个集合当中去,具体可以进入processImports方法中去有一个方法
 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);进去可以看到有一行
 this.deferredImportSelectors.add(holder);
 

2:既然找到了这个类,那么肯定就有处理它的地方,我们会带ConfigurationClassParse 的 parse()方法

//这个方法就是在解析Bean的时候会执行的一个方法
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);
            }
        }
        //解析完Bean还有@Import注解之后,会对使用@Import注解注入的bean进一步处理,这个时候不妨debug一下,看下是不是@SpringBootApplication
        //注解中AutoConfigurationImportSelector.class这个类
        this.deferredImportSelectorHandler.process();
    }
    

3:进步一步处理

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<>();
            }
        }
        
        
        public void processGroupImports() {
		for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            //debug进这个getImports()这个方法
			grouping.getImports().forEach(entry -> {
				ConfigurationClass configurationClass = this.configurationClasses.get(
						entry.getMetadata());
				try {
               
					processImports(configurationClass, asSourceClass(configurationClass),
							asSourceClasses(entry.getImportClassName()), false);
				}
				catch (BeanDefinitionStoreException ex) {
					throw ex;
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to process import candidates for configuration class [" +
									configurationClass.getMetadata().getClassName() + "]", ex);
				}
			});
		}
	}
    
    
    public Iterable<Group.Entry> getImports() {
		for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
            //进入这个方法
			this.group.process(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getImportSelector());
		}
		return this.group.selectImports();
	}
    
    
    @Override
	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()));
        // 这个类如果是AutoConfigurationImportSelector类型的,那么就会去执行这个类的
        // getAutoConfigurationEntry这个方法,所以现在我们可以到开始说的
        // AutoConfigurationImportSelector这个类中去查看这个方法
		AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
						annotationMetadata);
		this.autoConfigurationEntries.add(autoConfigurationEntry);
		for (String importClassName : autoConfigurationEntry.getConfigurations()) {
			this.entries.putIfAbsent(importClassName, annotationMetadata);
		}
	}

4:进入AutoConfigurationImportSelector类

protected AutoConfigurationEntry getAutoConfigurationEntry(
            AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 这个方法就是去加载我们自定义starter的配置类的
        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);
    }
    

5:进入getCandidateConfigurations()方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        // 去加载我们的配置类
        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;
    }
    

6: 一直跟进 loadSpringFactories()这个方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            // public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
            // 这步是去指定文件去加载我们的配置类,现在知道为什么我们在自定义starter的时候最后一步就是要去创建这个文件
            // 并且把我们的配置类的全路径配置进去,就是为了这一步,这样就可以通过反射拿到我们的配置类的实例了
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    

7:配置类拿到了,但是还需要进行过滤,因为我们在配置类上配置了@Condition相关的注解,@Condition注解就是必须满足某些条件才会被解析,下一篇文章我们可以自己实现@Conditon的用法。

8:结束语:我们自定义starter的用法以及SpringBoot如何解析我们的配置并让我们的配置生效已经结束了,建议从头到尾debug跟踪,这样才不会感觉迷茫