springboot自动配置与运行原理
一、springboot自动配置
1.启动器
springboot将各种开发功能的环境抽取出来做成了单独的starter(启动器),开发时只需要在pom.xml中引入启动器,就可以将相关的依赖环境导入进来,所以在使用时做什么功能就引入对应的启动器就可以了。
启动器: spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
springboot将依赖的版本用父项目进行管理,在导入依赖时默认不需要指明版本,除非使用的依赖没有在父项目中。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
但是在这里我们并没有看见版本的管理,这里主要是对项目资源过滤以及插件进行管理,在spring-boot-starter-parent点进去可以发现:
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
还有这样一个父依赖,这里才是真正进行版本管理的基地所在。
2.启动类
启动类是程序启动的入口,包括了tomcat等服务的启动:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
看上去简简单单,普普通通,只有几行,但是其中的流程是比较复杂的。
①、@SpringBootApplication:标注了这个类是springboot的主配置类,并且说明这是一个springboot应用,通过运行它标注的main方法来启动应用,如果没有它程序无法启动。
源码分析:进去之后可以发现在它上面还有注解
//四个元注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//springboot配置类
@SpringBootConfiguration
//自动配置
@EnableAutoConfiguration
//自动扫描,当前包及其子包,
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
......
}
@SpringBootConfiguration:标注当前类为springboot配置类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//配置类
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
进去之后发现有@Configuration,说明这是一个配置类相当于之前的java配置代替xml配置。
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
继续进去查看,@Component一个非常熟悉的注解,说明这个启动类本身也是被spring容器所管理,是其中的一个负责启动的组件。
②、@EnableAutoConfiguration:在启动类中这个注解,负责开启自动配置,解放双手。
在之前需要我们在xml或者java中配置的相关组件,springboot可以实现自动配置。
继续深入挖掘:
//自动配置包
@AutoConfigurationPackage
//给容器导入组件
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage:实现自动配置包
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
@Import({Registrar.class}):在spring容器中导入组件。
Registrar.class:把启动类所在包及其子包的所有组件进行扫描,并导入。
回到上一步:
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector:自动配置导入选择器
源码追溯:
第一:getCandidateConfigurations()方法,获取候选的配置组件。
并且getSpringFactoriesLoaderFactoryClass() 返回了之前看的启动自动导入配置文件的注解类;EnableAutoConfiguration
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
第二:可以看到调用了SpringFactoriesLoader.loadFactoryNames()方法,点进去看到调用了loadSpringFactories()方法,
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
第三:进去loadSpringFactories()方法,注意三个地方:
1.cache.get(classLoader):获得classLoader 这里返回EnableAutoConfiguration标注的类本身。
2.classLoader.getResources(FACTORIES_RESOURCE_LOCATION):这里的FACTORIES_RESOURCE_LOCATION进去后是获取一个资源对象: META-INF/spring.factories 。
3.while (urls.hasMoreElements()) {}: 资源遍历并封装成为一个Properties
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
// 获得classLoader 这里返回EnableAutoConfiguration标注的类本身
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 这里的FACTORIES_RESOURCE_LOCATION进去后是获取一个资源对象: *META-INF/spring.factories* 。
// 资源遍历并封装成为一个Properties
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());
}
}
}
第四:查看一下刚刚看到的那个资源文件对象META-INF/spring.factories,
可以发现有很多的配置文件
随便找一个配置OnWebApplicationCondition
自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律