SpringBoot 的启动原理
@[toc]
使用Spring Boot
首先打开IDEA 创建一个Spring Boot项目
选中 SpringInitializer 然后next 过程就不累赘了。
然后打开pom文件我们发现Srping Boot 有个配置依赖
启动依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
测试依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
点击启动依赖后我们发现这里面各种依赖
原来启动依赖里包含了spring-boot-starter 启动依赖,mvc依赖 还有tomcat 以及json 转换包 还有validator验证的包等等
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
然后在根目录下还有一个主程序,启动类
@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}
如果主启动类没有被 @SpringBootApplication 标注,启动时会报一个错误:Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory 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 {
....
}
发现有@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan这么三个注解
可以说明 @SpringBootApplication注解是个组合注解
我们也可以在启动类上使用@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan 来启动Spring Boot
先来看@ComponentScan 注解
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
这个注解作用是扫描指定包下的路径,指定了两个过滤条件 TypeExcludeFilter
和 AutoConfigurationExcludeFilter
这两个应该在启动的时候是过滤一些包 或者在启动的时候给容器中加一些组件
TypeExcludeFilter 类源码中 有个 match 方法 作用过滤的判断逻辑
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && this.getClass().equals(TypeExcludeFilter.class)) {
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
Iterator var4 = delegates.iterator();
while(var4.hasNext()) {
TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
if (this.beanFactory instanceof ListableBeanFactory 这句可以看到 BeanFactory 底层Ioc容器, 去执行自定义的过滤方法 TypeExcludeFilter 的作用是做扩展的组件过滤
同理 AutoConfigurationExcludeFilter 里面也有一个 match 方法 可以看到源码中this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader)
判断了是不是配置和自动配置 ,暂且先不用关注。
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
public AutoConfigurationExcludeFilter() {
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
}
return this.autoConfigurations;
}
}
然后看 @EnableAutoConfiguration 注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到 有@AutoConfigurationPackage
和
@Import({EnableAutoConfigurationImportSelector.class})
这两个注解
首先先看 @AutoConfigurationPackage 注解从名字上来看是自动配置包 ,点进去后
@Import({Registrar.class}) public @interface AutoConfigurationPackage { }
发现 给@ 注解导入一个组件 @Import({Registrar.class})
Registrar 组件
@Order(-2147483648)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
debug一下可以看到 这个注解作用是导入我们根目录经下的包名,通过包名导入组件,也就是扫描各种bean 然后注册到Ioc容器中。
然后来研究一下 @Import({EnableAutoConfigurationImportSelector.class})
貌似这个也是导入组件
打开 EnableAutoConfigurationImportSelector
类
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector
再打开 AutoConfigurationImportSelector
会看到如下代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
此类实现了 DeferredImportSelector
接口 你会发现这个类继承了ImportSelector 这个类
public interface DeferredImportSelector extends ImportSelector
作用是DeferredImportSelector 的执行时机比 ImportSelector 更晚,导入组件后会去查找导入组件结果。
然后我们还发现有下面这段代码 selectImports
这个方法按名字来看是查找导入的组件 ,这也证明了AutoConfigurationImportSelector
继承DeferredImportSelector
这个接口的意义。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
可以看到下面这段代码
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
好像是配置了一个配置集合
点进去后发现以下代码
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
this.getSpringFactoriesLoaderFactoryClass()
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
传入的Class就是 @EnableAutoConfiguration ,而这个类中
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
开启自动配置
SpringFactoriesLoader.loadFactoryNames 扫描了类路径下
使用 classLoader 去加载了指定常量路径META-INF/spring.factories
下的资源
我们会发现 多个包下有 spring.factories
文件被加载
然后debug继续往下走发现
AutoConfigurationImportSelector
类中 selectImports
方法里 configurations = this.filter(configurations, autoConfigurationMetadata);
这段代码进行过滤
传入的 参数 autoConfigurationMetadata
是上面代码AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
查询结果
点进去发现加载了 所有的spring-autoconfigure-metadata.properties
文件
然后发现这个文件里 基本都是以org.springframework.boot.autoconfigure
开头的全路径包名 ,瞬间明白了
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
这个类是找到所有Spring Boot支持的自动配置的类
然后过滤加载的自动配置包里是否含有在类路径下导入
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
自动配置导入过滤器
排除类路径下不存在的自动配置 最后是20个默认的自动配置 加入到Ioc容器中
总结
基本上Spring Boot的自动配置原理大致上分为三步:
- 1
过滤器
首先在启动类上过滤或者给容器添加组件 - 2
扫描
根路径下所有的bean - 3 添加类路径
META-INF/spring.factories
和META-INF/spring-autoconfigure-metadata.properties
自动配置的包和过滤自动配置是否在类路径下有包最后注册到Ioc中
关注我
长按二维码
如果你喜欢这篇文章,喜欢,在看,转发。
网友评论