赞
踩
在Springboot中,有各种各样的starter(启动器)
,比如spring-boot-starter-web等,Maven引入Starter后,只需要启动,无需手动配置,容器便可以自动配置必要的默认配置(其实是已经约定写好的配置),查看各个Starter的依赖,无一例外都会依赖“spring-boot-starter”
,以2.3.4版本为例,这里上部分Maven代码:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
进入spring-boot-starter依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
可以看到,每一个启动器,最终都需要依赖spring-boot-autoconfigure
进行自动配置,这个包下主要还是一堆自动配置类AutoConfiguration,其中还有一个核心配置文件:spring.factories
,以及相关注解@SpringBootApplication等等
@EnableAutoConfiguration
每个javaer学习springboot时,无一例外都是从使用@SpringBootApplication
注解在自己写的XXXApplication启动类上,再在类里面实现一个main方法,方法里用SpringApplication.run()启动springboot应用开始,而EnableAutoConfiguration便是其中核心;
@SpringBootApplication
源码:
package org.springframework.boot.autoconfigure;
@SpringBootConfiguration
@EnableAutoConfiguration //核心在于此,进入
@ComponentScan(....)
public @interface SpringBootApplication {
//省略..........
}
@EnableAutoConfiguration
源码:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class}) //核心在于此,进入
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";//是否开启自动配置,可以在application.yml或properties中配置
...
}
(注意前置知识:Spring将组件注册进容器有哪些方式)
AutoConfigurationImportSelector
源码:
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } } 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; }
在方法selectImports
中,返回的数组里面的类名均会被注册进容器中,依次调用了getAutoConfigurationEntry
->getCandidateConfigurations
,而getCandidateConfigurations
便是最核心的方法,通过SpringFactoriesLoader
把自动配置类加载进容器中;
加载过程见SpringFactoriesLoader
源码:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = (Map)cache.get(classLoader); if (result != null) { return result; } else { Map<String, List<String>> result = new HashMap(); try { Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories"); 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()) { Map.Entry<?, ?> entry = (Map.Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); String[] var10 = factoryImplementationNames; int var11 = factoryImplementationNames.length; for(int var12 = 0; var12 < var11; ++var12) { String factoryImplementationName = var10[var12]; ((List)result.computeIfAbsent(factoryTypeName, (key) -> { return new ArrayList(); })).add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> { return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); }); cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14); } } }
spring.factories(本质是一个properties键值对文件):
此时,SpringFactoriesLoader.loadFactoryNames
方法的第一个参数为EnableConfiguration.class,在方法内,通过类加载器加载classpath下的资源"META-INF/spring.factories",把spring.factories中EnableAutoConfiguration作为Key,对应的xxxAutoConfiguration的类名列表作为value,读进MapMap<String, List>中,经过过滤在AutoConfigurationImportSelector. selectImports返回注入的数组,于是自动配置类便被注入到容器中;
所以,当自己写的jar包想要被扫描到容器中,也可以自己写一个spring.factories,Key选用org.springframework.boot.autoconfigure.EnableAutoConfiguration,也会将Value自动注入到容器中:
再来看看org.springframework.boot.autoconfigure
下的xxxxxAutoConfiguration
这些配置类也不是说加载就加载的,只有@ConditionalOnXXX生效了,符合特定条件,才能被注入到容器中并生效;
Springboot自动配置概括为,核心在启动注解@SpringBootApplication的@EnableAutoConfiguration注解,通过@Import注解引入AutoConfigurationImportSelector类,经过一系列调用,最后调用SpringFactoriesLoader类加载spring.factories文件,并把文件中key为EnableAutoConfiguration的value全部加载进容器中,再根据当前依赖环境过滤后,最后才能被注入进容器,从而完成自动配置。
在最新的springboot中,仍旧支持spring.factories,但后续将被慢慢废弃;
主要优化有:
1.spring.factories中,org.springframework.boot.autoconfigure.EnableAutoConfiguration=\的key全部迁移至 META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
2.新增注解类AutoConfiguration
,并修改了AutoConfigurationImportSelector.getCandidateConfigurations
方法,如下,也就是将自动配置类放在imports
文件里,并加到候选配置列表中;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。