1简介
Spring Boot是基于Spring框架的,它的原理也是基于Spring框架的。
Spring框架是一个非常强大的框架,它提供了一系列的模块来帮助开发人员构建企业级的应用程序。Spring框架的核心是控制反转(Inversion of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)。
Spring Boot是在Spring框架的基础上,提供了自动配置、快速开发和更好的性能等功能,主要原理包括:
- 自动配置:Spring Boot基于约定优于配置的原则,提供了大量的自动配置,避免了繁琐的XML配置。
- 内嵌式容器:Spring Boot提供了内嵌式的Tomcat、Jetty、Undertow等容器,简化了应用程序的部署和启动。
- Starter依赖:Spring Boot提供了一系列的Starter依赖,开发人员可以只添加必要的Starter依赖,而不必担心版本冲突和依赖问题。
- 自动装配:Spring Boot的自动装配机制可以根据classpath中的依赖自动配置应用程序。
- Actuator:Spring Boot提供了Actuator模块,用于监控应用程序的健康状态、性能指标等。
- 外部化配置:Spring Boot可以将配置文件外部化,方便在不同的环境中部署应用程序。
总之,Spring Boot的原理是基于Spring框架的,它提供了一系列的功能来简化应用程序的开发、部署和运行,从而提高开发效率和应用程序的性能。
我们学习springboot步骤:
- 学习springboot的主要底层原理
- 自动装配
- 条件注解Conditional
- starter机制
- 手写实现一个简单springboot框架
下面优先讲解下springboot的核心功能:自动装配。
2 springboot 启动
2.1 引入依赖
springboot在项目中依赖引入方式:
-
方式一:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-parent</artifactId> <version>2.7.2</version> </parent> <dependencies> <dependy> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-xxx</artifactId> </dependy> </dependencies>
-
方式二
<properties> <spring-boot.version>2.7.10</spring-boot.version> </properties> <dependencyManagement> <!-- spring boot 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-xxx</artifactId> </dependency> </dependencies>
2.2 @SpringBootApplication
引入依赖之后,在启动类上加上@SpringBootApplication注解,它怎么就可以完成那么多jar包的Bean自动配置呢?
我们来看下@SpringBootApplication注解源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 省略...
}
@Target(ElementType.TYPE),@Retention(RetentionPolicy.RUNTIME),@Documented,@Inherited这几个元注解我们不在详述,下面我们重点看下其他3个注解。
2.3 @EnableAutoConfiguration
@SpringBootConfiguration是Spring Boot中的一个注解,它是@Configuration注解的特化版本,用于标识一个类是Spring Boot应用程序的配置类。
在Spring Boot中,配置类通常用于定义应用程序中的各种组件和配置信息,例如数据源、Web服务器、消息队列等。通过@Configuration注解和相关注解,可以将这些组件注入到Spring容器中,从而让它们在应用程序中起作用。
与@Configuration注解不同的是,@SpringBootConfiguration注解还会触发Spring Boot的自动配置机制。它会扫描应用程序中的类路径和类库,找到符合条件的组件并进行自动配置。因此,使用@SpringBootConfiguration注解可以简化应用程序的配置和部署,提高开发效率和可维护性。
2.4 @SpringBootConfiguration
@ComponentScan是Spring Framework中的一个注解,它用于指定要扫描的组件包。在Spring Boot中,@ComponentScan注解通常用于扫描应用程序中的所有组件,并将它们注入到Spring容器中。
通过@ComponentScan注解,可以指定要扫描的包和类,也可以通过excludeFilters和includeFilters属性来排除或包含特定的组件。例如,可以使用excludeFilters属性来排除某些组件,例如某些自动配置类或不需要注入到容器中的类。同时,也可以使用includeFilters属性来只包含特定的组件,例如只包含某些接口的实现类。
在Spring Boot中,@ComponentScan注解通常被应用于启动类或配置类中,用于扫描应用程序中的所有组件。如果没有指定@ComponentScan注解,则默认会扫描启动类所在的包及其子包中的所有组件。
2.5 @EnableAutoConfiguration
@EnableAutoConfiguration是Spring Boot中的一个注解,它用于开启自动配置机制。通过@EnableAutoConfiguration注解,可以让Spring Boot自动根据应用程序中的依赖和配置来完成自动配置,从而简化应用程序的开发和部署
看下@EnableAutoConfigutaiton注解源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 省略...
}
2.6 @Import
@Import是Spring Framework中的一个注解,它用于将一个或多个类导入到当前配置类中。通过@Import注解,可以将其他配置类、普通类、甚至是第三方库中的类导入到当前配置类中,从而让它们在Spring容器中起作用。
在Spring Boot中,@Import注解通常用于导入一些自定义的配置类或第三方库中的类。例如,可以使用@Import注解导入一个自定义的配置类,从而将其中定义的Bean注入到Spring容器中。也可以使用@Import注解导入一个第三方库中的类,从而在应用程序中使用该类提供的功能。
自动装配的核心功能,通过AutoConfigurationImportSelector完成。
3 自动配置类的导入流程
下面我们就从启动类的run()方法,通过debugger来追踪下自动装配的流程,我们测试是开源项目pig的gateway模块即网关模块。
第一步:在ApplicationContext#refresh()方法invokeBeanFactoryPostProcessors处设置断点,该方法用于向容器中注册BeanFactory。代码3-1如下所示:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 省略。。。
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 省略。。。
}
断点截图3-1如下所示:
第二步:程序继续执行,忽略中间步骤,我们去看关于AutoConfigurationImportSelector开启自动配置类的导包的选择器
中相关代码执行,即执行getAutoConfigurationEntry()方法,代码如下:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除重复类
configurations = removeDuplicates(configurations);
// 根据过滤器过滤不符合条件的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
继续追踪下getCandidateConfigurations()获取候选配置类方法,代码如下:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 加载jar包下META-INFO/spring.factories下的配置类,兼容springboot2.7.0以前版本
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
// 加载jar包下META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的类,为springboot2.7.0之后自动配置类存放路径
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
// 省略空校验
return configurations;
}
- getSpringFactoriesLoaderFactoryClass()返回:
EnableAutoConfiguration.class
追踪下loadFactoryNames()加载jar包下META-INFO/spring.factories下的配置类方法,代码如下:
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());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 缓存获取给定加载器key对应的值
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 加载META-INFO/spring.factories中的配置
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());
}
}
}
// 去重且map值list设置为不可修改
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
// 省略异常处理
return result;
}
- FACTORIES_RESOURCE_LOCATION:META-INF/spring.factories
- 加载jar包下META-INF/spring.factories中配置
- 其中EnableAutoConfiguration实现类,有的jar包放在spring.factories中,有的单独迁移放置在其他文件中,下面会将。
- result为HashMap结构,key存放=前的key,值为ArrayList结构,里面存放实现类。
- loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());获取key为EnableAutoConfiguration(完整包路径)对应的配置类。
我当前springboot版本2.7.10,我们看下spring-boot-autoconfigure-2.7.10.jar包下META-INF/spring.factories中内容
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Environment Post Processors
# 省略
# Auto Configuration Import Listeners
# 省略
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Failure analyzers
# 省略
# Template availability providers
# 省略
# DataSource initializer detectors
# 省略
# Depends on database initialization detectors
# 省略
- properties格式配置文件,key为父类或者父接口,值为,分隔的实现类
下面我们看下ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);执行
- ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())加载META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置类
- forEach(configurations::add),把这些配置类和之前加载的配置类合并。
ImportCandidates#load()方法:
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// location:META-INF/spring/%s.imports
String location = String.format(LOCATION, annotation.getName());
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
展示部分org/springframework/boot/spring-boot-autoconfigure/2.7.10/spring-boot-autoconfigure-2.7.10.jar!/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的配置类:
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
# 省略
到此springboot导入自动配置类流程部分介绍完成,剩下过滤部分,我们会放在下一篇@Conditionalxxx中讲解。
4 小结
-
loadSpringFactories()该方法就是从“META-INF/spring.factories”中加载给定类型的工厂实现的完全限定类名放到map中
-
loadFactoryNames()是根据SpringBoot的启动生命流程,当需要加载自动配置类时,就会传入org.springframework.boot.autoconfigure.EnableAutoConfiguration参数,从map中查找key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,这些值通过反射加到容器中,之后的作用就是用它们来做自动配置,这就是Springboot自动配置开始的地方
-
load():方法从“META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports”中加载AutoConfiguration实现的完全限定类名与放入自动配置类map中
-
只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动
-
当需要其他的配置时如监听相关配置:listenter,就传不同的参数,获取相关的listenter配置
关于
结语
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/springboot-custom
参考:
[1]Springboot视频教程[CP/OL].P13.
[2]一文搞懂🔥SpringBoot自动配置原理[CP/OL].P13.
[3]ChatGPT
[4]spring boot 自动装配的实现原理和骚操作,不同版本实现细节,debug 到裂开…[CP/OL].P13.
[5]SpringBoot 自动装配原理[CP/OL].P13.