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

19 篇文章 1 订阅
6 篇文章 1 订阅

专栏目录

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

实现方法

  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;
    	}
    
  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;
    		}
    
  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);
		}

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运行原理剖析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xianghan收藏册

极简精品作,一分也是一份鼓励哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值