1、自动配置原理
1、我们编写的SpringBoot启动类上有一个@SpringBootApplication
注解,表示当前类是springboot的启动类(入口类)。
package com.baidou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication //表示当前类是springboot的启动类
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
点击@SpringBootApplication查看源码:
// 前四个是元注解
@Target(ElementType.TYPE) // 说明这个注解作用在类或接口上
@Retention(RetentionPolicy.RUNTIME) // 控制注解的生命周期,RUNTIME表示一直存活(源码阶段、字节码文件阶段、运行阶段)
@Documented // 是否可以生成文档
@Inherited // 是否可以继承
// 核心注解:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
@SpringBootApplication它是一个组合注解:
-
@SpringBootConfiguration:声明当前类是一个springboot的配置类。
-
@EnableAutoConfiguration:支持自动配置
。 -
@ComponetScan:组件扫描,扫描主类所在的包以及子包里的bean。
2、查看@SpringBootConfiguration注解源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 表示这个注解它也是spring的配置类
public @interface SpringBootConfiguration {
...
}
3、@ComponetScan组件扫描,扫描并加载符合条件的Bean到容器中
@ComponetScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
4、查看@EnableAutoConfiguration注解源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //指定需要扫描配置的包
@Import(AutoConfigurationImportSelector.class)//导入AutoConfigurationImportSelector这个配置类(加载自动配置的类)
public @interface EnableAutoConfiguration {
4.1、点击@AutoConfigurationPackage注解,发现导入这么一个静态内部类AutoConfigurationPackages.Registrar
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) //导入Registrar中注册的组件
// @AutoConfigurationPackage注解的主要作用就是将启动类所在包及所有子包下的组件到扫描到spring容器中。
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
接着点击Registrar类查看源码:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// 给容器中导入某个组件类
// 根据传入的元注解信息获取所在的包, 将包中组件类封装为数组进行注册
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
使用debug查看它扫描哪个包下的组件:
这里我们要注意,在定义项目包结构的时候,要定义的非常规范,我们写的代码要放到启动类所在包或子包下,这样才能保证定义的类能够被组件扫描器扫描到。
4.2、@Import(AutoConfigurationImportSelector.class)注解:
导入了一个自动配置类AutoConfigurationImportSelector,表示向spring容器中导入一些组件。
// 实现DeferredImportSelector接口,需要重写一个selectImports方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
// 此方法的返回值都会加载到spring容器中(bean的全限定名数组)
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断SpringBoot是否开启自动配置
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 获取需要自动配置的bean信息
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 判断是否开启自动配置
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration注解的属性,也就是exclude和excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选的配置
// 获取到所有需要导入到容器中的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);、
// 去除重复的配置类
configurations = removeDuplicates(configurations);
// 获取注解中exclude或excludeName排除的类集合
Set<String> exclusions = getExclusions(annotationMetadata, attributes
// 检查被排除类是否可以实例化,是否被自动配置所使用,否则抛出异常
checkExcludedClasses(configurations, exclusions);
// 去除被排除的类
configurations.removeAll(exclusions);
// 使用spring.factories文件中配置的过滤器对自动配置类进行过滤
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations =
// 扫描所有jar包类路径下 "META-INF/spring.factories文件
// 在spring-boot-autoconfigure中
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
}
加载当前项目中所有jar包的META-INF/spring.factories
下key为:org.springframework.boot.autoconfigure.EnableAutoConfiguration的value值,他的value值就是这130个自动配置类。(第三方stater整合springboot也要提供spring.factories,stater机制)
每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都会加入到容器中;用他们来做自动配置!!!
虽然我们130个自动配置类默认是全部加载,最终它会按照@Conditional条件装配。(生效的配置类就会给容器中装配很多组件)
例如:RedisAutoConfiguration
@Configuration(proxyBeanMethods = false) //表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@ConditionalOnClass(RedisOperations.class) //条件 当项目导入starter-data-redis依赖时才会下限执行
@EnableConfigurationProperties(RedisProperties.class) //让RedisProperties对象读取配置文件中的信息
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) //
public class RedisAutoConfiguration {
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//判断容器有没有这个组件,springioc容器中没有则创建
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisProperties:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private ClientType clientType;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
...
}
扫描到这些自动配置类后,要不要创建呢?
这个要结合每个自动配置类上的条件,若条件满足就会创建,一旦创建好自动配置类之后,配置类中所有具有@Bean注解的方法就有可能执行,这些方法返回的就是自动配置的核心对象。
小结:
1、SpringBoot启动会加载大量的自动配置类。
2、看看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
3、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
- xxxxAutoConfigurartion:自动配置类,给容器中添加组件。
- xxxxProperties:属性类,封装配置文件中相关属性;
【ctrl + n 搜索 *AutoConfiguration
查看默认的写好的所有配置类】