Springboot启动流程源码分析
Springboot启动流程源码分析(一)(自动配置)
我们首先从pom.xml文件开始分析
- 其中它主要是依赖一个父项目,主要是管理控制项目的资源过滤及插件依赖!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 在父启动类里,发现还有一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
- 这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
- 以后我们导入依赖默认是不需要写版本
- 但是如果导入的包没有在依赖中管理着就需要手动配置版本了
启动器 spring-boot-starter介绍
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- springboot-boot-starter-xxx:就是spring-boot的场景启动器
- spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件
- SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来
- 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;
主启动类SpringbootApplication---->一系列注解原理
1、SpringbootApplication主启动类main方法
- 默认的主启动类,一个简单的启动类并不简单!下面我们来分析一下 @SpringBootApplication 注解都干了什么
@SpringBootApplication // 来标注一个主程序类 说明这是一个Spring Boot应用
public class SpringbootApplication {
public static void main(String[] args) {
//以为是启动了一个方法,没想到启动了一个服务
SpringApplication.run(SpringbootApplication.class, args);
}
}
2、@SpringBootApplication注解
-
来标注一个主程序类 说明这是一个Spring Boot应用
-
SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
-
在这个注解中还有许多其他的注解,如下代码所示
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ...
}
3、@ComponentScan注解(@SpringBootApplication子注解)
-
该注解在Spring中很重要 ,它对应XML配置中的元素。
-
作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
4、@SpringBootConfiguration注解(@SpringBootApplication子注解)
- 作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类
- 在该注解下含有两个注解:@Configuration、@Component
@Configuration
public @interface SpringBootConfiguration {}
// @Configuration点进去得到下面的 @Component
@Component
public @interface Configuration {}
-
这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件
-
这里的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
我们回到 SpringBootApplication 注解中继续看。
5、@EnableAutoConfiguration(@SpringBootApplication的注解)
-
注解作用:开启自动配置功能
- 以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置
- @EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效
-
点进EnableAutoConfiguration注解继续查看:
-
@AutoConfigurationPackage :自动配置包
@Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
-
@import :Spring底层注解@import , 给容器中导入一个组件
- Registrar.class 的作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
-
-
退到上一步,继续看@EnableAutoConfiguration的注解
-
**@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 **
- AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
- getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 这里的getSpringFactoriesLoaderFactoryClass()方法,返回的就是注解类Class:EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.
loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
return configurations;
}
// 返回自动配置类EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
- 这个方法又调用了 SpringFactoriesLoader 类的静态方法loadFactoryNames() 方法
// @Nullable ClassLoader classLoader 参数为该类自己SpringFactoriesLoader
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 就是传过来的EnableAutoConfiguration.class;
String factoryTypeName = factoryType.getName();
// 这里又调用了loadSpringFactories方法,Map有键为EnableAutoConfiguration就返回,没有取空集合
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 我们继续追踪查看 loadSpringFactories 方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 获得classLoader,我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
MultiValueMap<String, String> result = cache.get(classLoader);
// 缓存中若有,则直接返回
if (result != null) return result;
try {
// 去加载文件资源 "META-INF/spring.factories"
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 将读取到的资源遍历,封装成为一个Properties
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
}
spring.factories文件
- 我们可以全局搜索并打开spring.factories , 看到了很多自动配置的信息
AopAutoConfiguration实例
- 随便找一个自动配置类打开看看,比如 :AopAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration{ }
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration{ }
- 自动配置的实现是加载classpath下所有的META-INF/spring.factories配置文件
- 将其中对应的 org.springframework.boot.autoconfigure 包下的配置项
- 通过反射实例化将标注了 @Configuration的JavaConfig形式的聚集加载到IOC容器完成配置