SpringBoot 运行原理
SpringBoot 运行原理
pom.xml
- 发现项目有个父依赖
spring-boot-starter-parent
,此依赖主要是管理项目的资源过滤及插件。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 在刚刚的父依赖中,发现还有个父依赖
spring-boot-dependencies
,此依赖管理了SpringBoot应用的所有依赖版本,是SpringBoot的版本控制中心,所以以后我们导入依赖默认是不需要写版本,但是如果导入的包没有在依赖中管理着就需要手动配置版本了。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
- springboot-boot-starter-xxx:spring-boot的场景启动器
例如:spring-boot-starter-web
,它帮我们导入了web模块正常运行所依赖的组件。
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ,我们未来也可以自己自定义 starter。
主启动类
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
SpringApplication.run()
方法的作用:
该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行。
@SpringBootApplication注解
作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用。
点进这个注解,我们发现还有许多其他注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {...}
@ComponentScan注解
作用:自动扫描主程序所在包已经所在子包下的所有注解,将符合的Bean注册到Spring IoC容器中。
@SpringBootConfiguration注解
作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类。
我们继续进去这个注解查看:
@Configuration
public @interface SpringBootConfiguration {...}
@Configuration
:说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件,相当于<Beans></Beans>。再点进这个注解查看:
@Component
public @interface Configuration {...}
@Component
:这就说明,启动类本身也是也被Spring 的 Ioc 容器管理。
@EnableAutoConfiguration注解
作用:开启自动配置功能。以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 。
点入这个注解:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {...}
1. @AutoConfigurationPackage
:自动配置包。点进去发现:
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@Import({Registrar.class})
:将Registrar类导入到当前容器中。Registrar.class 的作用:将自动配置的包注册到Spring容器。
2. @Import({AutoConfigurationImportSelector.class})
: 自动配置导入选择器,那么它会选择哪些组件导入呢?让我们点击去这个类看源码:
2.1 有个方法getCandidateConfigurations()
:选择哪些组件导入
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;
}
此方法中有一个方法:loadFactoryNames()
:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
此方法中有一个方法: loadSpringFactories()
:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
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()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
最后发现会到classpath下的读取"META-INF/spring.factories"文件的配置。
spring.factories
让我们打开 spring-boot-autoconfigure-2.2.5.RELEASE.jar 下的 META-INF/spring.factories
我们看到了很多自动配置的文件XXXAutoConfiguration
,这就是自动配置根源所在!
我们随意打开一个XXXAutoConfiguration
,例如:HttpEncodingAutoConfiguration
可以看到XXXAutoConfiguration
一个个的都是JavaConfig配置类,注入了一些Bean。
存在XXXProperty
可以为XXXAutoConfiguration
设置我们在application.properties中配置的属性。
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories
配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure
包下的配置项,通过反射实例化为对应标注了 @Configuration
的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
@Conditional
XXXAutoConfiguration
中存在大量@Conditional
类似的注解,这些注解表示必须在一定的条件下才能生效。
例如:
1.class条件注解
@ConditionalOnClass
:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnMissingClass
:某个class类路径上不存在的时候,才会实例化一个Bean。
2.Bean条件注解
@ConditionalOnBean
:当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean
:当容器里没有指定Bean的条件下进行实例化。
3.属性条件注解
@ConditionalOnProperty
:当指定的属性有指定的值时进行实例化。
4.Resource条件注解
@ConditionalOnResource
:当类路径下有指定的资源时触发实例化。
5.web条件注解
@ConditionalOnNotWebApplication
:不是web应用,才会实例化一个Bean。
@ConditionalOnWebApplication
:当项目是一个Web项目时进行实例化。
6.表达式条件注解
@ConditionalOnExpression
:基于SpEL表达式的条件判断,当表达式为true的时候,才会实例化一个Bean。
在springBoot的配置文件application.properties
中配置debug=true
可以查看哪些自动配置类生效,哪些不生效。
Positive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)
总结
- SpringBoot在启动的时候会扫描主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器。
- SpringBoot在启动的时候会从类路径下的
META-INF/spring.factories
中获取一堆XXXAutoConfiguration
,并按照@Conditional
进行过滤,条件成立的才会导入Spring IoC容器中,根据XXXProperty
类中的配置来自动配置这些组件,我们可以通过application.properties
自定义XXXProperty
类中的配置。 - 有了自动配置类 , 我们免去了手动编写配置功能组件等工作。