目录
前言
spring boot版本2.4.3
为什么说spring boot部署简单,且0代码生成,就是因为spring boot帮我们做了很多事情,其中自动配置类是一个很重要的因素。
1. 先看入口函数
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
ctrl+鼠标左键点进去@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}
)}
)
发现是一个合成注解,接下来一一解释。
元注解:
@Target({ElementType.TYPE}),表示可标注在类,接口或枚举上。
@Retention(RetentionPolicy.RUNTIME),表示注解会在class字节码文件中存在,在运行时可以通过反射获取到。
@Documented,表示该注解将被包含在javadoc中。
@Inherited,说明子类可以继承父类中的该注解。
注解:
@SpringBootConfiguration,表示这是一个配置类
@ComponentScan,包扫描规则,默认是主类所在的包及其子包
@EnableAutoConfiguration,开启自动配置
2. @EnableAutoConfiguration
点开@EnableAutoConfiguration,发现他也是合成注解。
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
2.1 @AutoConfigurationPackage
点进去发现
@Import({Registrar.class})
public @interface AutoConfigurationPackage {}
@Import({Registrar.class}),是导入一个配置类
点进去Registrar
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));
}
}
通过添加断点发现,在启动时只有第一个方法会执行,所实现的功能是将获取到的包名(默认是主类所在的包)注册,实质上是将包名添加到下面这个set中。
private final Set<String> basePackages = new LinkedHashSet();
该set所在类为AutoConfigurationPackages下的BasePackagesBeanDefinition 静态内部类中
2.2 @Import({AutoConfigurationImportSelector.class})
点进去,里面有一个方法。叫获取候选的配置。
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;
}
里面这么多方法,是怎么找这个方法的?笨方法:将所有方法全部打上断点,然后debug运行,如果运行到其他类中,就step out,然后观察,前面都是一些初始化工作,到了上面的方法里面configurations突然就有值了。并且里面都是AutoConfiguration结尾的,那么此处就是找出所有需要自动配置的类。
122行是一个断言,做判断用的,不用管
明显主要逻辑是在121行执行的,打上断点,重新debug运行。
在121行按F7(进去参数方法,然后再出去),最终进入loadFactoryNames()方法
其中两个参数分别为org.springframework.boot.autoconfigure.EnableAutoConfiguration和RestartClassLoader
来到72行进入loadSpringFactories()方法
可以看到如果有缓存就从缓存获取,如果没有缓存就从META-INF/spring.factories中获取。会扫描spring所有的jar包下的META-INF/spring.factories文件,并获取其中的key和value放到map中。
以其中一个为例。
然后调用map的getOrDefault(factoryTypeName, Collections.emptyList())方法。意思是获取第一个参数key所对应的value,如果获取不到则返回第二个参数。
而上面的map里面是有org.springframework.boot.autoconfigure.EnableAutoConfiguration这个key的,所以会返回其对应的value。
注意只是获取候选的配置,并不一定会全部加载。
3. 加载
最终会按需加载,导入哪个包才会加载哪个。