一、SpringBoot是什么
SpringBoot 的诞生就是为了简化 Spring 中繁琐的 XML 配置,其本质依然还是Spring框架,使用SpringBoot之后可以不使用任何 XML 配置来启动一个服务,使得我们在使用微服务架构时可以更加快速的建立一个应用。
二、SpringBoot的特点
- 提供了固定的配置来简化配置,即约定大于配置
- 尽可能地自动配置 Spring 和第三方库,即能自动装配
- 内嵌容器,创建独立的 Spring 应用
- 让测试变的简单,内置了JUnit、Spring Boot Test等多种测试框架,方便测试
- 提供可用于生产的特性,如度量、运行状况检查和外部化配置。
- 完全不需要生成代码,也不需要 XML 配置
三、启动类
下面探究SpringBoot的启动原理,关于一些细节就不赘述,我们捉住主线分析即可。
3.1 @SpringBootApplication
一切的来自起源SpringBoot的启动类,我们发现main方法上面有个注解:@SpringBootApplication
@SpringBootApplication
public class SpringbootWorkApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootWorkApplication.class, args);
}
}
@SpringBootApplication 标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的main方法来启动 SpringBoot 应用;它的本质是一个组合注解,我们点进去查看该类的元信息主要包含3个注解:
@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(里面就是@Configuration,标注当前类为配置类,其实只是做了一层封装改了个名字而已)
-
@EnableAutoConfiguration(开启自动配置)
-
@ComponentScan(包扫描)
3.1.1 @SpringBootConfiguration
我们继续点@SpringBootConfiguration进去查看源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@Configuration标注在某个类上,表示这是一个 springboot的配置类。可以向容器中注入组件。
注:@Inherited是一个标识,用来修饰注解,如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解
3.1.2 @ComponentScan
- @ComponentScan:配置用于 Configuration 类的组件扫描指令
- 提供与 Spring XML 的 context:component-scan 元素并行的支持
- 可以 basePackageClasses 或basePackages 来定义要扫描的特定包。 如果没有定义特定的包,将从声明该注解的类的包开始扫描。
3.1.3 @EnableAutoConfiguration
- @EnableAutoConfiguration顾名思义就是:开启自动导入配置
- 这个注解是SpringBoot的重点,我们下面详细讲解
四、@EnableAutoConfiguration
- 我们点进去看看该注解有什么内容
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //自动导包
@Import({AutoConfigurationImportSelector.class}) //自动配置导入选择
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
4.1 @AutoConfigurationPackage
- 自动导入配置包
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
4.1.1 @Import({Registrar.class})
点Registrar.class进去查看源码如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//断点
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
注:Registrar实现了ImportBeanDefinitionRegistrar类,就可以被注解@Import导入到spring容器里。
这个地方打断点
运行可以查看到(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])的值为com.ljw.springbootwork:当前启动类所在的包名。
结论:
[@AutoConfigurationPackage 就是将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描注冊到 spring 容器中。]
4.2 @Import({AutoConfigurationImportSelector.class})
作用:AutoConfigurationImportSelector开启自动配置类的导包的选择器,即是导入哪些类,有选择性的导入。
点AutoConfigurationImportSelector.class进入查看源码,这个类中有两个方法见名知意:
1、selectImports:选择需要导入的组件
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
2、getAutoConfigurationEntry:根据导入的@Configuration类的AnnotationMetadata返回AutoConfigurationImportSelector.AutoConfigurationEntry
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 这打个断点,看看 返回的数据
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 删除重复项
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 检查
this.checkExcludedClasses(configurations, exclusions);
// 删除需要排除的依赖
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
this.getCandidateConfigurations(annotationMetadata, attributes)这里断点查看
configurations数组长度为133,并且文件后缀名都为 **AutoConfiguration
结论:这些都是候选的配置类,经过去重,去除需要的排除的依赖,最终的组件才是这个环境需要的所有组件。有了自动配置,就不需要我们自己手写配置的值了,配置类有默认值的。
我们继续往下看看是如何返回需要配置的组件的
4.2.1 getCandidateConfigurations(annotationMetadata, attributes)
方法如下:
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;
}
这里有句断言: 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.”);
意思是:“在 META-INF/spring.factories 中没有找到自动配置类。如果您使用自定义包装,请确保该文件是正确的。
结论:即是要loadFactoryNames()方法要找到自动的配置类返回才不会报错。
getSpringFactoriesLoaderFactoryClass()
我们点进去发现:this.getSpringFactoriesLoaderFactoryClass()返回的是EnableAutoConfiguration.class这个注解。这个注解和@SpringBootApplication下标识注解是同一个注解
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
结论:获取一个能加载自动配置类的类,即SpringBoot默认自动配置类为EnableAutoConfiguration
4.2.2 SpringFactoriesLoader
SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spring容器中。
这里的key是EnableAutoConfiguration接口,value是需要加载的组件
注:会加载所有jar包下的classpath路径下的META-INF/spring.factories文件,这样文件不止一个
4.2.2.1 loadFactoryNames()
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());
}
断点查看factoryTypeName的值
先是将 EnableAutoConfiguration.class 传给了 factoryType
然后String factoryTypeName = factoryType.getName();
所以factoryTypeName 值为 org.springframework.boot.autoconfigure.EnableAutoConfiguration
4.2.2.2 loadSpringFactories()
接着查看loadSpringFactories方法的作用
该方法作用是加载所有依赖的路径META-INF/spring.factories文件,通过map结构保存,key为文件中定义的一些标识工厂类,value就是能自动配置的一些工厂实现的类,value用list保存并去重
在回看 loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName,Collections.emptyList());
因为 loadFactoryNames 方法携带过来的第一个参数为 EnableAutoConfiguration.class,所以 factoryType 值也为 EnableAutoConfiguration.class,那么 factoryTypeName 值为 EnableAutoConfiguration。拿到的值就是META-INF/spring.factories文件下的key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
getOrDefault 当 Map 集合中有这个 key 时,就使用这个 key值,如果没有就使用默认值空数组。
结论:
- loadSpringFactories()该方法就是从**“META-INF/spring.factories”中加载给定类型的工厂实现的完全限定类名放到map中**。
- loadFactoryNames()是根据SpringBoot的启动生命流程,当需要加载自动配置类时,就会传入org.springframework.boot.autoconfigure.EnableAutoConfiguration参数,从map中查找key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,这些值通过反射加到容器中,之后的作用就是用它们来做自动配置,这就是Springboot自动配置开始的地方。
- 只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动。