专栏目录
1-SpringBoot架构设计与实现原理-SpringBoot设计与特性
2-SpringBoot架构设计与实现原理-SpringBoot注解原理
3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配
4-SpringBoot架构设计与实现原理-SpringBoot自定义starter
5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析
SpringBoot的自动加载机制与原理
自动装配源码入口
查看@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自定义starter
5-SpringBoot架构设计与实现原理-SpringBootApplication运行原理剖析