1-SpringBoot架构设计与实现原理-SpringBoot设计与特性2-SpringBoot架构设计与实现原理-SpringBoot注解原理3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配4-SpringBoot架构设计与实现原理-SpringBoot自定义starter5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析
查看@SpringBootApplication源码找到@EnableAutoConfiguration
点击@EnableAutoConfiguration源码
@EnableAutoConfiguration --> @Import --> AutoConfigurationImportSelector
@EnableAutoConfiguration --> @AutoConfigurationPackage --> @Import({Registrar.class}) org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
根据上下文做动态加载bean,或者批量加载bean
新建xh-auto-configuration项目
创建两个bean对象
public class AccountService {
}
public class UserService {
}
创建XHDefinitionRegistrar
public class XHDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Class beanClass=AccountService.class;
RootBeanDefinition beanDefinition=new RootBeanDefinition(beanClass);
String beanName=StringUtils.uncapitalize(beanClass.getSimpleName());
beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);
}
}
创建XHImportSelector
public class NXImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
Map<String,Object> attributes= annotationMetadata.getAnnotationAttributes(EnableNXAutoConfiguration.class.getName());
//动态注入bean :判断逻辑实现动态配置
//返回的是一个固定的UserService
return new String[]{UserService.class.getName()};
}
}
自定义注解EnableXHAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({NXImportSelector.class,NXDefinitionRegistrar.class}) //
public @interface EnableNXAutoConfiguration {
//配置一些方法
Class<?>[] exclude() default {};
}
运行验证
@SpringBootApplication
@EnableNXAutoConfiguration
public class NxAutoConfigurationApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ca=SpringApplication.run(NxAutoConfigurationApplication.class,args);
System.out.println(ca.getBean(UserService.class));
System.out.println(ca.getBean(AccountService.class));
}
}
在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的。其中使用了
Spring 模式注解装配 Spring @Enable 模块装配 Spring 条件装配装配 Spring 工厂加载机制
实现类: SpringFactoriesLoader
配置资源: META-INF/spring.factories
自动装配举例
参考 META-INF/spring.factories
实现方法
- 激活自动装配 - @EnableAutoConfiguration
- 实现自动装配 - XXXAutoConfiguration
- 配置自动装配实现 - META-INF/spring.factories
自定义自动装配
XHAutoConfiguration 条件判断: user.name == “xh” 模式注解: @Configuration @Enable 模块: @EnableXH -> XHImportSelector -> XHConfiguration - > XH
自动装载源码剖析
- 打开注解SpringBootApplication,上面有ComponentScan注解用来扫描bean和排除bean
- 看看上面的EnableAutoConfiguration注解,上面引用了@Import(AutoConfigurationImportSelector.class)
- 我们看看AutoConfigurationImportSelector.class先看看类关系AutoConfigurationImportSelector implements DeferredImportSelector implements extends ImportSelector#selectImports()那么说明我们需要从selectImports方法入手去看
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//自动装配入口 重点看getAutoConfigurationEntry方法
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//重点 getCandidateConfigurations
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);
//激活bean中的事件监听
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回自动配置的数据对象
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//SpringFactoriesLoader SPI 机制
//去加载spring.factories文件
//打开spring-boot-autoconfigure-2.4.0-sources.jar!/META-INF/spring.factories 看下EnableAutoConfiguration
//去演示NXImportSelector 实际EnableAutoConfiguration的values就是需要自动装配加载的bean的类路径
List<String> configurations =
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;
}
- 重点看看spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring-autoconfigure-metadata.properties
搜索 Configuration、ConditionalOnClass、ConditionalOnBean发现bean加载过程的依赖条件
- AutoConfigurationImportSelector中处理自动注入原数据的逻辑
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
if (this.autoConfigurationMetadata == null) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
}
return this.autoConfigurationMetadata;
}
- 跟踪源码查看哪里调用了AutoConfigurationImportSelector#selectImports一直向上查找直到Spring初始化的源头AbstractApplicationContext#refresh().invokeBeanFactoryPostProcessors(beanFactory);
- EnableAutoConfiguration注解的上面有个@AutoConfigurationPackage注解点进去,@Import(AutoConfigurationPackages.Registrar.class)
- 将扫描的包注册到IOC容器中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//将扫描的包注册到IOC容器中 接下来看看new PackageImports部分代码
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
}
// 获取带有AutoConfigurationPackage注解的类
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
SpringBoot SPI
SPI的全称是Service Provider Interface, 直译过来就是"服务提供接口"
涉及到的知识点
- SPI机制
- FactoryBean
- JDK动态代理
具体实现看META-INF下的文件
打开spring-boot-autoconfigure-2.4.0-sources.jar!/META-INF/spring.factories 看下EnableAutoConfiguration
key=values[] key=value[] key=values[]
spi的扩展 满足目录结构一致 文件名一致 key要存在并且符合当前的加载
专栏目录
1-SpringBoot架构设计与实现原理-SpringBoot设计与特性2-SpringBoot架构设计与实现原理-SpringBoot注解原理3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配4-SpringBoot架构设计与实现原理-SpringBoot自定义starter5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析