spring boot自动装载

spring boot自动装载

  • spring.factories
  • @Import和@Bean注解
  • ImportSelector接口
  • ImportBeanDefinitionRegistrar接口
  • spring boot自动装载原理

spring.factories

spring boot开箱即用的特点,靠的就是强大的自动装载功能,spring boot会扫描classpath下所有依赖的spring.factories文件,将EnableAutoConfiguration配置下的所有类加载到spring容器中。

@Import和@Bean注解

@Import注解和@Bean一样,都是将对象添加到容器中,但是不同的是@Bean是加在方法上,要自己在方法中new对象,且都和@Configuration注解配合使用。
而@Import更加灵活,可以直接导入普通类,和实现ImportSelector或ImportBeanDefinitionRegistrar接口的类搭配使用,通过代码更灵活的往spring容器中注入对象。也可以搭配@Configuration注解使用。

ImportSelector接口

ImportSelector接口的主要方法是String[] selectImports(AnnotationMetadata importingClassMetadata)。是一种灵活的注册对象到spring 容器中的方法。怎么灵活呢?通过AnnotationMetadata这个类,可以获取到指定注解上字段的值,例如spring cloud中常用的@EnableXXX这类注解,搭配ImportSelector接口,可以控制组件关键对象的注册,以达到控制组件是否开启的目的。

ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar接口的方法为registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry),通过接口上的BeanDefinitionRegistry向spring容器添加对象,通过Import引入时,spring会自动调用registerBeanDefinitions方法。
和ImportSelector的区别?ImportSelector还是返回类名让spring去加载配置类,而ImportBeanDefinitionRegistrar是直接向spring容器中添加对象,在已知需要哪些Bean的时候,用ImportSelector更加方便。如果逻辑相对复杂,需要代码去判断实现,比如多数据源的情况下,你不知道有几个数据源,需要代码判断动态添加,则只能用ImportBeanDefinitionRegistrar。

spring boot自动装载原理

上面讲了那么多spring boot常用的自动装载的方法,又是META-INF/spring.factories文件,又是@Import注解,那么spring boot是如何实现这些东西的呢,今天来探究一下spring boot的源码,这里我用的是spring boot 2.7.2版本,各个版本略有不同,接下来一起探究一下。

spring boot的入口十分简单,大家应该都知道,就是一个静态的run方法,让我们从这个run方法开始debug,org.springframework.boot.SpringApplication#run(java.lang.String…)。
spring boot自动装载_第1张图片
通过方法名我们就可以看出来,这里进行了一些简单的处理,包括banner的打印,环境变量的准备等等,注意蓝色这行的方法,F5进去追踪一下。
spring boot自动装载_第2张图片
refreshContext方法包含了一个shutdownHook的注册,这个没什么好看的,直接进入refresh方法,下面还有一个对refresh方法的调用,跳过,直接进入refresh方法的主体org.springframework.context.support.AbstractApplicationContext#refresh。
spring boot自动装载_第3张图片
打标的前面几行,都是一些类的创建,字段的初始化,到了this.invokeBeanFactoryPostProcessors(beanFactory);里面做了大量的处理,我们进入这个方法探究一下做了哪些处理。
spring boot自动装载_第4张图片
还没进入到方法的主体,继续F5进入方法,找到执行的主体,org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List)

spring boot自动装载_第5张图片
前面进行了一些加载器的处理,直接进入到蓝色的这个方法org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
spring boot自动装载_第6张图片
继续debug进箭头指的这个方法,org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
spring boot自动装载_第7张图片
看箭头所指的这个方法,看方法名,应该是加载配置类的,debug进这个方法org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        ...............

        if (!configCandidates.isEmpty()) {
            ..................

            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
                StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
				// 主要看这个方法
                parser.parse(candidates);
                parser.validate();
                Set configClasses = new LinkedHashSet(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
                if (this.reader == null) {
                    this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
                }

                this.reader.loadBeanDefinitions(configClasses);
                alreadyParsed.addAll(configClasses);
                processConfig.tag("classCount", () -> {
                    return String.valueOf(configClasses.size());
                }).end();
                candidates.clear();
                if (registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
                    Set alreadyParsedClasses = new HashSet();
                    Iterator var13 = alreadyParsed.iterator();

                    while(var13.hasNext()) {
                        ConfigurationClass configurationClass = (ConfigurationClass)var13.next();
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }

                    String[] var24 = newCandidateNames;
                    int var25 = newCandidateNames.length;

                    for(int var15 = 0; var15 < var25; ++var15) {
                        String candidateName = var24[var15];
                        if (!oldCandidateNames.contains(candidateName)) {
                            BeanDefinition bd = registry.getBeanDefinition(candidateName);
                            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                                candidates.add(new BeanDefinitionHolder(bd, candidateName));
                            }
                        }
                    }

                    candidateNames = newCandidateNames;
                }
            } while(!candidates.isEmpty());

            if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }

            if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
                ((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();
            }

        }
    }

debug到org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)这个方法,我们看到对processConfigurationClass方法的调用,这是配置类加载的最重要的方法。
在这里插入图片描述
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
spring boot自动装载_第8张图片
进入org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass这个方法
spring boot自动装载_第9张图片
这里用ComponentScanAnnotationParser扫描了工程下面的spring容器要管理的类。然后关注下面这个蓝色行的方法
spring boot自动装载_第10张图片
org.springframework.context.annotation.ConfigurationClassParser#getImports
spring boot自动装载_第11张图片
spring boot自动装载_第12张图片
可以看到,org.springframework.context.annotation.ConfigurationClassParser#collectImports
这个方法,在递归扫描所有类上的注解,把所有的@Import注解的value收集起来放到一个Set里面。
然后看一下这个@Import收集起来的集合,用在了org.springframework.context.annotation.ConfigurationClassParser#processImports
spring boot自动装载_第13张图片
可以看到这里对文章上面讲的一些接口进行了扫描,并按照类型,调用接口方法,将所有普通的类收集起来,ImportSelector类会直接调用selectImports方法, deferredImportSelector会收集起来,等待后面processor方法里调用,ImportBeanDefinitionRegistrar类注册到configclass的register中。还在里面看到了各种aware接口的调用,org.springframework.context.annotation.ParserStrategyUtils#invokeAwareMethods。

spring boot自动装载_第14张图片
parse方法运行完,进入process方法。org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
spring boot自动装载_第15张图片
process方法,将spring.factories中配置的类,加载到了importstack中。如何读取spring.factories配置文件的呢,process方法中单独对deferredImports类进行了一次调用。这里看一下Sprint boot最重要的注解,@SpringBootApplication。这里面有三个重要的注解@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
这里只看@EnableAutoConfiguration注解,这里Import了一个类@Import({AutoConfigurationImportSelector.class})。AutoConfigurationImportSelector.class就是一个deferredImport。在org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry中的getCandidateConfigurations方法里,SpringFactoriesLoader.loadFactoryNames加载了classpath下所有的spring.factories文件
spring boot自动装载_第16张图片
parse方法整个执行完,进入这个loadBeanDefinitions方法。
spring boot自动装载_第17张图片
spring boot自动装载_第18张图片
spring boot自动装载_第19张图片
这里会调用上面整理的ImportBeanDefinitionRegistrar的接口,注册bean到spring 容器中。

你可能感兴趣的:(java,spring,spring,boot,spring,java)