• 3-SpringBoot架构设计与实现原理-自动装配底层原理和手写自动装配


    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 {
    }
    
    • 1
    • 2
    • 3
    • 4

    创建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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建XHImportSelector

    public class NXImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            Map attributes=                annotationMetadata.getAnnotationAttributes(EnableNXAutoConfiguration.class.getName());
            //动态注入bean :判断逻辑实现动态配置
    
            //返回的是一个固定的UserService
            return new String[]{UserService.class.getName()};
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    自定义注解EnableXHAutoConfiguration

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({NXImportSelector.class,NXDefinitionRegistrar.class}) //
    public @interface EnableNXAutoConfiguration {
    
        //配置一些方法
        Class<?>[] exclude() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行验证

    @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));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    源码分析自动装配

    在 Spring Boot 场景下,基于约定大于配置的原则,实现 Spring 组件自动装配的目的。其中使用了

    底层装配技术

    Spring 模式注解装配
    Spring @Enable 模块装配
    Spring 条件装配装配
    Spring 工厂加载机制

    • 实现类: SpringFactoriesLoader
    • 配置资源: META-INF/spring.factories

    自动装配举例

    参考 META-INF/spring.factories

    实现方法

    1. 激活自动装配 - @EnableAutoConfiguration
    2. 实现自动装配 - XXXAutoConfiguration
    3. 配置自动装配实现 - META-INF/spring.factories

    自定义自动装配

    XHAutoConfiguration
    条件判断: user.name == “xh”
    模式注解: @Configuration
    @Enable 模块: @EnableXH -> XHImportSelector -> XHConfiguration - > XH

    自动装载源码剖析

    1. 打开注解SpringBootApplication,上面有ComponentScan注解用来扫描bean和排除bean

    2. 看看上面的EnableAutoConfiguration注解,上面引用了@Import(AutoConfigurationImportSelector.class)

    3. 我们看看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;
      	}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
    4. 重点看看spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring-autoconfigure-metadata.properties

      搜索 Configuration、ConditionalOnClass、ConditionalOnBean发现bean加载过程的依赖条件

    5. AutoConfigurationImportSelector中处理自动注入原数据的逻辑

      		private AutoConfigurationMetadata getAutoConfigurationMetadata() {
      			if (this.autoConfigurationMetadata == null) {
      				this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
      			}
      			return this.autoConfigurationMetadata;
      		}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    6. 跟踪源码

      查看哪里调用了AutoConfigurationImportSelector#selectImports

      一直向上查找直到Spring初始化的源头

      AbstractApplicationContext#refresh().invokeBeanFactoryPostProcessors(beanFactory);

    7. EnableAutoConfiguration注解的上面有个@AutoConfigurationPackage注解点进去,@Import(AutoConfigurationPackages.Registrar.class)

    8. 将扫描的包注册到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);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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要存在并且符合当前的加载

  • 相关阅读:
    java 通过行为参数化传递代码,来解决不断增长的需求
    【JavaScript】解决 JavaScript 语言报错:Uncaught TypeError: XYZ is not a function
    结构体的简单介绍(3)——结构体的内存对齐
    培训心得怎么写?CHAT帮你解决问题
    前(jsencrypt)后(node-rsa/crypto)端 RSA 加密与解密
    第一章: HTML
    C语言—程序环境和预处理
    前端 diy 功能模块原来如此强大和简单,我这小白也轻松学会了【带附件】
    PyCharm 2022最新版详细图文安装教程(安装+运行测试+汉化+背景图设置)
    快速入门Web开发(中)后端开发(有重点)
  • 原文地址:https://blog.csdn.net/xianghanscce/article/details/125900545