SpringBoot-2.1.3自动配置原理

前言

J2EE笨重的开发、繁多的配置、低下的开发效率、复杂的部署流程、第三方技术集成难度大。springboot提供一站式解决方案,默认集成了主流的框架,大量的自动配置,免去了很多的配置文件,大大的简化开发,这大多数人对springboot的最直接的印象,这也是springboot的成功之处。
虽然springboot集成了很多框架并有默认的配置,但是我们去使用技术的时候要知其然知其所以然,要了解自动配置其背后的原理,增强自身对springboot的理解。

springboot版本:2.1.3
大家可以边看文章边看代码,不要光看文章,会看不下去的
这里大致看完(是弄明白大概的流程哦)大约半小时左右

分析过程
1、首先项目启动时会加载主配置项,在@SpringBootApplication注解中有个@EnableAutoConfiguration注解,表示启动自动配置。
2、@EnableAutoConfiguration具体的细节是如何;

2.1、进入@EnableAutoConfiguration注解,里面有个@Import({AutoConfigurationImportSelector.class})注解,表示想容器中导入AutoConfigurationImportSelector这个类,这个类就是一个配置项选择器,可以获取要导入的配置项。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
	...
}

2.2、进入AutoConfigurationImportSelector类,可以看到一个方法selectImports()

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

在这里面会调用一个方法getAutoConfigurationEntry(),该方法返回的就是配置项信息。

2.3、进入getAutoConfigurationEntry(),关注其中的这行代码,返回的是已申请即将导入容器的配置项信息集合

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
			// 省略...
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            // 省略...
        }
    }

2.4、进入getCandidateConfigurations()方法,同样继续深入loadFactoryNames()方法,这里大家留意该方法的第一个参数,后面我们会用到。

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }

PS:看到这里大家可以先回过头看看,捋一捋

2.5、重头戏来了,进入loadFactoryNames()方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		// 获取一个传进来的Class的全路径
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} 

具体细节在loadSpringFactories()方法中,该方法就在loadFactoryNames()的下面

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

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 {
            	// flag1:获取类路径下的资源"META-INF/spring.factories"
                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);
                    // flag2:将URL转换成Properties
                    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);
            }
        }
    }
  • 在loadSpringFactories方法中首先会获取一个类路劲下的资源"META-INF/spring.factories",如图:
    在这里插入图片描述
    文件内容很多,只截取了一部分
    在这里插入图片描述

  • 在外层while中会将文件数据转换成URL,然后将URL装换成Properties,Properties是一个Hashtable,最终配置文件中的数据将以key-value的形式存在Properties中的entry中,如下所示:(具体如何转换的可以在表示flag2的地方继续深入,这里就不涉及了,太多了大家看的头疼)
    在这里插入图片描述

  • 那内层的while循环其实就是对Properties进行遍历,获取key,获取value,并且会将value的值按逗号分隔获取具体的每一个String,然后将这些数据存入result这个Map中,如下所示:

                                                       result(Map)
    
keyValue
org.springframework.context.ApplicationContextInitializerorg.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.context.ApplicationContextInitializerorg.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
org.springframework.context.ApplicationListenerorg.springframework.boot.autoconfigure.BackgroundPreinitializer
  • 最后返回该保存了所有配置项信息的map,回到loadFactoryNames()方法

2.6、上面执行完返回执行getOrDefault(…)

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		// 获取一个传进来的Class的全路径
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} 
default V getOrDefault(Object key, V defaultValue)

这里有个getOrDefault(…)方法,这是map的方法,用来判断当前map中是否包指定的key,有则返回对应的数据,这个key就是loadFactoryNames()第一个参数的某个Class的全路径名,往回loadFactoryNames方法上一层看,
在这里插入图片描述
一看就明白了,传进来的就是EnableAutoConfiguration,那自然这个key就是EnableAutoConfiguration的全路径,也就是会拿到配置文件中EnableAutoConfiguration所对应的数据
在这里插入图片描述
所以到这,就知道springboot启动时自动配置到底配置的那些组件,一看就知道了,总的来说就是:

  • 获取META-INF/spring.factories文件中EnableAutoConfiguration指定的配置项,并将其导入组件,实现自动配置
  • 每个xxxAutoConfiguration即为导入的组件,通过这些配置类实现具体的配置

后面一片以一个xxxAutoConfiguration作为实例进一步分析。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值