目录
springboot啥都不难,总所周知spring全家桶系列难就难在理解源码。。。。。。。
今天结合网上资料,自己总结了一下springboot的自动配置原理。
我现在使用的springboot版本为2.3.1.不同版本的springboot在源码上有差别!但大体一致。
管他三七二十一先打个断点再说:
这个注解点进去我们可以看到:
这里面主要关注两个东西:
@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 {}; }
点进去瞅瞅:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage { String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; }
发现这里有导入Regitstrar类:
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)); } }
new PackageImport(metadata).getPackageName(),它其实返回了当前主程序类的 **同级以及子级 ** 的包组件。
什么意思呢?
我们来看这样一个目录:
bean1和我们的springboot启动类位于同一个包下,二bean2不是位于我们启动类的同级目录或者子级目录,那么我们启动的时候bean2是不会被加载到的!所以你项目的一切需要加入容器的类必须放在启动类的同级包下或者它的子级目录中。
AutoConfigurationImportSelector有一个方法为: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()); } }
它首先回去检查是否开启了自动配置类,然后才回去加载注解数据 this.getAutoConfigurationEntry(annotationMetadata);
那么这个annotationMetadata在哪儿?
来看下面一行代码:
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; }
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
再点进去我们会发现这一行代码:
Enumeration
它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。这个外部文件,有很多自动配置的类。如下:
spring.factories文件由一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔。
springboot项目启动时,@SpringBootApplication用在启动类在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
@Configuration( proxyBeanMethods = false ) //表示是一个配置类,可以给容器中添加组件 @EnableConfigurationProperties({ServerProperties.class})// 启用ConfigurationProperties功能 @ConditionalOnWebApplication( type = Type.SERVLET ) @ConditionalOnClass({CharacterEncodingFilter.class}) @ConditionalOnProperty( prefix = "server.servlet.encoding", value = {"enabled"}, matchIfMissing = true )
@EnableConfigurationProperties({ServerProperties.class})// 启用ConfigurationProperties功能
ServerProperties.class:
@ConfigurationProperties( prefix = "server", ignoreUnknownFields = true )
@ConditionalOnWebApplication :spring底层@Conditional注解,根据不同的条件进行判断,如果满足条件整个配置类才会生效。
总结:
1.springboot会自动加载大量的自动配置类。
2.只要我们要用的组件有,我们就不需要再去配置
3.给容器添加组件的时候。会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性。
xxxxxAutoConfiguration:自动配置类
给容器中添加属性:
xxxxProperties:封装配置文件中的相关属性。