SpringBoot自动装配原理
SpringBoot 自动装配原理
@SpringBootApplication 启动注解
@SpringBootApplication
public class SpringBootBasicLearnApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootBasicLearnApplication.class, args);
}
}
/**
* to store the base package from the importing configuration
* 也就是获取到我们启动类的包路径
*/
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 这里注入容器,将我们的包路径信息封装成一个Bean
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
// BeanClassLoaderAware,ResourceLoaderAware 这些表示可以获取Aware前的信息
// Ordered 表示Bean注入排序
// 关键是这个: DeferredImportSelector 延迟导入选择器 extends
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
public interface DeferredImportSelector extends ImportSelector {
// 这里是接口的定义方法,具体实现看AutoConfigurationImportSelector
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
}
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
// 这里是核心,获取自动配置的实例,通过元数据 !!!
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取EnableAutoConfiguration注解中定义的exclude排除的Bean类
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 这里获取候选配置,是关键!!!
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 删除注解声明的不用的类信息
configurations.removeAll(exclusions);
// 关键!!!根据过滤规则,过滤不用的类信息
configurations = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
// 这里是关键,会找原先定义的所有Bean配置!!!
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
return configurations;
}
// 获取注解类的全类名集合
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
// LOCATION: "META-INF/spring/%s.imports"; 通过这个文件来找到所有自动配置类的信息
// annotation.getName() = org.springframework.boot.autoconfigure.AutoConfiguration
// 最终加载:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
// 这里直接下载源码,找Maven:org.springframework.boot:spring-boot-autoconfigure:2.7.5,一共有144个
// 里面的就是默认启动的一个项,比如:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,可以追一下源码
String location = String.format(LOCATION, annotation.getName());
// 这里就读取到了所有的全类名集合
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> autoConfigurations = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
autoConfigurations.addAll(readAutoConfigurations(url));
}
// 封装被返回
return new ImportCandidates(autoConfigurations);
}
// 这里就是过滤一部分不符合的类名
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
// 通过过滤器 原来加载配置文件有144个,如果是基础SpringBoot项目,就剩13个了
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
// 这里我们以META-INF中Redis自动装配类来演示
1. 查找 org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
2. 查看源码
@AutoConfiguration
// 这里就是判断你容器内有没有这个类数据
@ConditionalOnClass(RedisOperations.class)
// 加载配置类,也就是我们在resource文件下写的内容,加载进来
@EnableConfigurationProperties(RedisProperties.class)
// 导如 Lettuce Jedis 这都是我们熟悉的工具
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
// redisTemplate 工具
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
// StringRedisTemplate 工具
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
// 核心点就是我们需要导入使用组件的核心位,比如我们使用Redis就要导入依赖
org.springframework.data.redis.core
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 玲辰书斋!