"约定大于配置"(Convention Over Configuration)是一种理念,旨在通过默认约定和规则来减少开发人员需要做的配置工作。在Spring Boot框架中,这一原则得到了充分应用,帮助开发者更快地构建高效的应用程序
在springboot项目中,我们会使用@SpringBootApplication注解在启动类上,它提供了相当多的便利性,帮助我们完成了当前启动类目录下的组件扫描和自动配置,其源码如下
可以看到它是一个组合注解,相当于声明了@Configuration、@EnableAutoConfiguration和@ComponentScan,而自动装配实现的秘密就在@EnableAutoConfiguration注解上
查看@EnableAutoConfiguration源码,发现它注入了一个AutoConfigurationImportSelector类型的bean,继续跟进
我们查看一下AutoConfigurationImportSelector类关系图
AutoConfigurationImportSelector是做什么的呢?该类上的javadoc有这么一句话,“DeferredImportSelector to handle auto-configuration”,deferred的意思是推迟的,意为处理自动装配的推迟的ImportSelector
DeferredImportSelector 是 ImportSelector的子类,它在所有@Configuration bean都处理完毕后运行。而AutoConfigurationImportSelector实现了DeferredImportSelector接口,并实现了该接口的selectImports方法
查看selectImports方法发现,内部的getAutoConfigurationEntry方法就返回了要装配的@Configuration类
其中最关键的是getCandidateConfigurations方法,源码如下
这里调用了SpringFactoriesLoader.loadFactoryNames返回所有的自动装配类,这个方法的第一个入参就是EnableAutoConfiguration.class,第二个入参是获取的类加载器
继续跟进源码,查看最底层的loadSpringFactories方法
- private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
- ...
- try {
- Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
- while (urls.hasMoreElements()) {
- URL url = urls.nextElement();
- UrlResource resource = new UrlResource(url);
- Properties properties = PropertiesLoaderUtils.loadProperties(resource);
- for (Map.Entry<?, ?> entry : properties.entrySet()) {
- String factoryTypeName = ((String) entry.getKey()).trim();
- String[] factoryImplementationNames =
- StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
- for (String factoryImplementationName : factoryImplementationNames) {
- result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
- .add(factoryImplementationName.trim());
- }
- }
- }
- }
- ...
- }
可以看到,loadSpringFactories方法通过类加载器获取META-INF/spring.factories资源,我们项目中的类路径下的(当然包括三方jar包)所有META-INF/spring.factories都会被读取,并返回被自动装配的类
spring-boot-autoconfigure模块是springboot的自动装配模块
查看该模块下的spring.factories文件
可以看到,该文件下已经帮我们配置了相当多的Configuration类,这也是springboot实现约定大于配置的关键,至此谜团解开