从源码中理解Spring Boot自动装配原理

守听  金牌会员 | 2022-9-17 08:38:44 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 885|帖子 885|积分 2655

个人博客:槿苏的知识铺
一、什么是自动装配

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用jar包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部jar包来说,只需要按照SpringBoot定义的标准,就能将自己的功能装配到SpringBoot中。
二、自动装配的实现原理

自动装配的实现,离不开SpringBootApplication这个核心注解。查看这个注解的源码,我们会发现在SpringBootApplication注解上,存在着几个注解,其中SpringBootConfiguration、EnableAutoConfiguration、ComponentScan这三个注解是需要我们注意的。
  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan(excludeFilters = {
  4.                 @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  5.                 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  6. public @interface SpringBootApplication {
  7.         ...
  8. }
复制代码
(1) ComponentScan
扫描被@Component 、@Service注解的bean,该注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如SpringBootApplication注解源码所示,容器中将排除TypeExcludeFilterh和AutoConfigurationExcludeFilter。
(2) EnableAutoConfiguration
启用 SpringBoot 的自动配置机制
(3) Configuration
允许在上下文中注册额外的 bean 或导入其他配置类
2.1 EnableAutoConfiguration详解

@EnableAutoConfiguration是实现自动装配的重要注解,在这个注解上存在以下两个注解:AutoConfigurationPackage、Import。
  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {
  4.         ...
  5. }
复制代码
2.1.1 AutoConfigurationPackage

表示对于标注该注解的类的包,应当使用AutoConfigurationPackages注册。实质上,它负责保存标注相关注解的类的所在包路径。使用一个BasePackage类,保存这个路径。然后使用@Import注解将其注入到ioc容器中。这样,可以在容器中拿到该路径。
  1. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
  2.         @Override
  3.         public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  4.                 register(registry, new PackageImport(metadata).getPackageName());
  5.         }
  6. }
复制代码
查看AutoConfigurationPackages中的Registrar这个类的源码,在Registrar#registerBeanDefinitions方法中有这样一句代码new PackageImport(metadata).getPackageName(),查看PackageImport的构造器后不难发现,这里获取的是StandardAnnotationMetadata这个实例所在的包名。
  1. /**
  2. * metadata: 实际上是 StandardAnnotationMetadata 实例。
  3. * metadata#getClassName(): 获取标注 @AutoConfigurationPackage 注解的类的全限定名。
  4. * ClassUtils.getPackageName(…): 获取其所在包。
  5. */
  6. PackageImport(AnnotationMetadata metadata) {
  7.         this.packageName = ClassUtils.getPackageName(metadata.getClassName());
  8. }
复制代码
此时再回去看Registrar#registerBeanDefinitions中调用的AutoConfigurationPackages#register方法
  1. public static void register(BeanDefinitionRegistry registry, String... packageNames) {
  2.         // BEAN:AutoConfigurationPackages类的全限定名
  3.         // 此时判断BeanDefinitionRegistry中是否存在以BEAN作为beanName的BeanDefinition对象
  4.         // 如果不存在,走else方法,构造了一个BackPackages实例,进行注册
  5.         if (registry.containsBeanDefinition(BEAN)) {
  6.                 BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
  7.                 ConstructorArgumentValues constructorArguments = beanDefinition
  8.                                 .getConstructorArgumentValues();
  9.                 constructorArguments.addIndexedArgumentValue(0,
  10.                                 addBasePackages(constructorArguments, packageNames));
  11.         } else {
  12.                 GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  13.                 beanDefinition.setBeanClass(BasePackages.class);
  14.                 beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
  15.                 beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  16.                 registry.registerBeanDefinition(BEAN, beanDefinition);
  17.         }
  18. }
复制代码
2.1.2 Import(AutoConfigurationImportSelector.class)

它是利用AutoConfigurationImportSelector机制再来给容器中批量导入一些配置东西的,接下来带大家了解究竟导入了哪些内容。
  1. /**
  2. * AutoConfigurationImportSelector类中存在一个叫selectImports的方法,就是我们到底要向容器中导入哪些
  3. * 内容,都会在这里进行扫描并导入。
  4. */
  5. @Override
  6. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  7.     // 判断EnableAutoConfiguration是否开启默认开启true
  8.     if (!isEnabled(annotationMetadata)) {
  9.         return NO_IMPORTS;
  10.     }
  11.     // 1.加载META-INF/spring-autoconfigure-metadata.properties 文件
  12.     // 2.从中获取所有符合条件的支持自动装配的类
  13.     // 自动配置类全名.条件=条件的值
  14.     AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  15.         .loadMetadata(this.beanClassLoader);
  16.     // 获取AutoConfigurationEntry
  17.     AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
  18.     return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  19. }
复制代码
接下来重点看getAutoConfigurationEntry(annotationMetadata)方法,利用这个方法向容器中批量导入一些默认支持自动配置的类,当你理解了这部分内容之后,就基本了解了Spring Boot是如何进行自动装配的,废话不多说,让我们进入正题。
  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  2.     // 判断EnableAutoConfiguration是否开启默认开启true
  3.     if (!isEnabled(annotationMetadata)) {
  4.         return EMPTY_ENTRY;
  5.     }
  6.     // 获取注解属性
  7.     AnnotationAttributes attributes = getAttributes(annotationMetadata);
  8.     // 调用getCandidateConfigurations(annotationMetadata, attributes),利用loadSpringFactories(ClassLoader classLoader)加载当前系统所有的META-INF/spring.factories文件,得到默认支持的自动配置的类的列表
  9.     List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  10.     // 去除重复的 configuration
  11.     configurations = removeDuplicates(configurations);
  12.     // 获取到SpringBootApplication上exclude和excludeName配置的需要排除的类
  13.     Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  14.     // 检查configurations是否含有exclusions中的类
  15.     checkExcludedClasses(configurations, exclusions);
  16.     // 将exclusions中的类从configurations中排除
  17.     configurations.removeAll(exclusions);
  18.     // 对所有候选的自动配置类进行筛选,
  19.     // 比如ConditionalOnProperty  当属性存在时
  20.     // ConditionalOnClass  当class存在
  21.     // ConditionalOnMissingClass  当这个clas不存在时才去配置
  22.     // 过滤器
  23.     configurations = getConfigurationClassFilter().filter(configurations);
  24.     // 将自动配置的类,导入事件监听器,并触发fireAutoConfigurationImportEvents事件
  25.         // 加载META-INF\spring.factories中的AutoConfigurationImportListener
  26.     fireAutoConfigurationImportEvents(configurations, exclusions);
  27.     // 创建AutoConfigurationEntry对象
  28.     return new AutoConfigurationEntry(configurations, exclusions);
  29. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

守听

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表