【Spring】Spring原理源码解析(三)-- Spring在解析配置类时,怎么识别并处理@Import类的

系列文章主旨

从如何把自己的Bean注册到Spring容器开始,从该点出发,每篇只关注一个核心流程的原理、源码,再由该篇带出引申出来的其他Spring知识点,继续剖析,最终达到理解Spring核心原理的目的;

上篇内容

分析了spring如何通过内置的,bean工厂后置处理器,解析配置类,识别配置类注解@ComponentScan,然后去扫描对应的包路径,最后生成bd,注册到spring容器中;
详情:【Spring】Spring原理源码解析(二)-- Spring怎么识别@ComponentScan类并完成扫描生成BD注册到beanDefinitionMap中的

本篇内容

从上一篇分析的spring内部实现的ConfigurationClassPostProcessor继续入手,分析spring在解析配置类的时候,又是怎么识别@Import类的原理源码;
文章图片均出自:https://www.processon.com/view/link/62f4d0aa1e0853714d4689bf

核心原理

代码示例

指定配置类初始化spring容器,方法内部会把配置类注册到spring容器中

// import示例
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyImportConfig.class);

A a = context.getBean(A.class);
System.out.println("a == " + a);
// import示例

MyImportConfig指定了几个需要被import的类

@Import(value = {
   
        // 引入的普通配置类
        ScanConfig.class,
        // 引入了实现ImportSelector的类,该类可以引入其他配置类...
        MyImportSelector.class,
        // 引入了实现ImportBeanDefinitionRegistrar的类,该类可以注册bean到spring容器中
        MyImportRegistor.class
})
public class MyImportConfig {
   
}

ScanConfig,指定扫描的路径,属于普通配置类

@ComponentScan(value = {
   "com.zsh.demo.spring.learn.register.bean.importAnno",
                        "com.zsh.demo.spring.learn.register.bean.entity"})
public class ScanConfig {
   
}

MyImportSelector实现了ImportSelector,使用接口的方式来再次引入一个配置类

public class MyImportSelector implements ImportSelector {
   

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
   
        // 指定被import的类
        return new String[]{
   "com.zsh.demo.spring.learn.register.bean.importAnno.importSelector.ImportSelectorImportClz"};
    }
}

被引入的ImportSelectorImportClz打印一句话,证明被实例化

public class ImportSelectorImportClz {
   

    public ImportSelectorImportClz() {
   
        System.out.println("ImportSelectorImportClz被实例化");
    }

}

MyImportRegistor则是实现了ImportBeanDefinitionRegistrar,注册A类到spring容器

public class MyImportRegistor implements ImportBeanDefinitionRegistrar {
   

    /**
     * 这里可以注册bean到spring容器
     * */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
   
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(A.class);
        registry.registerBeanDefinition("a", builder.getBeanDefinition());
    }
}

图解

在这里插入图片描述

核心处理逻辑

Spring容器进行初始化时,会默认生成自己的bean工厂后置处理器ConfigurationClassPostProcessor,并注册到spring容器中,然后在执行所有的bean工厂后置处理时,就会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法;

在执行该方法时,会解析我们注册到Spring的配置类,本篇解析的是@Import注解的元数据信息的处理;

首先会尝试递归获取当前配置类(包括其注解)所有打上@Import的类、注解等,然后遍历一个个解析打上@Import类/注解,获取其value值,该值会指向其需要导入的配置类;

此时被导入的配置类按照三种情况进行处理:
1、该配置类实现了ImportSelector接口,即其又指定了所需要引入并解析的配置类,则实例化ImportSelector接口实现类后,调用接口方法获取import的类,再去递归解析这一个个被import的类;
2、该配置类实现了ImportBeanDefinitionRegistrar接口,则实例化实现类,后放入spring容器,后续就会调用该类的接口方法,完成类注册;
3、其它情况,则认为该类是个配置类,再次递归去解析该配置类;

进阶源码解析

spring调用内置工厂,处理配置类,定位到解析@import的入口

内部的工厂后置处理器ConfigurationClassPostProcessor,默认会在spring初始化时被调用,判断被引入的类属于配置类后(@component、@Import、@Bean、@ComponentScan、@ImportResource),就会对该类及其父类进行递归解析,源码位置是在:org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

该部分内容,详情可以参考【Spring】Spring原理源码解析(二)-- Spring怎么识别@ComponentScan类并完成扫描生成BD注册到beanDefinitionMap中的

从processConfigurationClass跟进到doProcessConfigurationClass()方法,就可以看到解析@Import注解的入口

@Nullable
protected final SourceClass doProcessConfigurationClass(
		ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
		throws IOException {
   

	// 忽略非相关...

	// 解析@Import注解
	processImports(configClass, sourceClass, 
				    // 获得当前class的所有@Import的value集	
				    getImports(sourceClass), 
				    filter, true);

	// 忽略非相关...
}
解析当前配置类,拿到类及注解上所有@Import类的value值集
图解

在这里插入图片描述

源码解析

构造imports集合,准备存储解析后的结果, org.springframework.context.annotation.ConfigurationClassParser#getImports

/**
* org.springframework.context.annotation.ConfigurationClassParser#getImports
*/
private Set<SourceClass
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值