SpringBoot源码之BeanDefinitionReader解析与自定义

一、简述

代码环境:springboot、Spring5.2.4;

相对于ClassPathBeanDefinitionScanner处理被@Component标注的bean,BeanDefinitionReader读取静态配置文件中bean。

处理的时机在ClassPathBeanDefinitionScanner之后,感兴趣的同学可以去看我另一篇文章Spring源码之基于@ComponentScan生成BeanDefinition

继承关系如下:
在这里插入图片描述

BeanDefinitionReader:定义了读取BeanDefinition的通用方法,例如获取BeanDefinition的注册器、加载BeanDefinition等等。

AbstractBeanDefinitionReader:是BeanDefinitionReader子类抽象,实现了一些公共的功能,比如根据资源加载器的不同,来处理资源路径。

最下面三个是具体的实现类,分别读取不同格式的Bean的定义信息,根据配置文件来选择不同的实现类读取。

二、源码分析

Springboot不需要配置文件,要想用BeanDefinitionReader,需要手动指定一个配置文件,例如:@ImportResource(“spring-beans.xml”)

Spring启动的主方法是refresh(),这个方法中包含了12个主要方法,其中的一个主要方法invokeBeanFactoryPostProcessors(beanFactory);负责执行所有的BeanFactoryPostProcessor。而派生类ConfigurationClassPostProcessor负责处理所有的@Configuration配置类。在处理@Component标注的bean之后,会处理配置类上的@ImportResource注解。

核心代码loadBeanDefinitionsFromImportedResources

代码如下:

/**
* @param importedResources @ImportResource的注解信息,Map的key为配置文件名,
*                          value为BeanDefinition读取器,默认是BeanDefinitionReader.class
*/
private void loadBeanDefinitionsFromImportedResources(
			Map<String, Class<? extends BeanDefinitionReader>> importedResources) {

		Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();

        // 循环遍历配置文件
		importedResources.forEach((resource, readerClass) -> {
			// 如果是BeanDefinitionReader,表示没有配置自定义的BeanDefinitionReader,使用框架自带的读取器
			if (BeanDefinitionReader.class == readerClass) {
			    /*
			    * 根据配置文件名的后缀,选择对应的读取器
			    */
				if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
					readerClass = GroovyBeanDefinitionReader.class;
				}
				else if (shouldIgnoreXml) {
					throw new UnsupportedOperationException("XML support disabled");
				}
				else {
					readerClass = XmlBeanDefinitionReader.class;
				}
			}
            
            // 在读取器实例的缓存中查询有没有已创建的实例
			BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
			// 等于null,表示readerClass对应的读取器是自定义的,需要新创建
			if (reader == null) {
				try {
					// 利用反射创建自定义的读取器实例
					reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
					// 当前读取器如果是AbstractBeanDefinitionReader的子类,进行一些赋值操作
					if (reader instanceof AbstractBeanDefinitionReader) {
						AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
						abdr.setResourceLoader(this.resourceLoader);
						abdr.setEnvironment(this.environment);
					}
					// 将读取器加入缓存
					readerInstanceCache.put(readerClass, reader);
				}
				catch (Throwable ex) {
					throw new IllegalStateException(
							"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
				}
			}

			// 用对应的读取器加载配置文件中的BeanDefinition
			// 如果是自定义的读取器,这个方法是需要覆盖重写的
			reader.loadBeanDefinitions(resource);
		});
	}

三、自定义BeanDefinitionReader

背景:需要用txt文件作为Bean的配置文件

  1. 创建一个TxtBeanDefinitionReader继承AbstractBeanDefinitionReader,并实现父类抽象方法。
public class TxtBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public TxtBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        
        // 表示注册的BeanDefinition个数
        int count = 0;
        
        try (
            InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
        ){
            
            String line;
            String beanName;
            String beanClass;
            
            while ((line = bufferedReader.readLine()) != null) {
                
                // 配置格式(beanName:bean的全称)
                String[] info = line.split(":");
                beanName = info[0];
                beanClass = info[1];

                // 创建一个beanDefinition
                RootBeanDefinition beanDefinition = new RootBeanDefinition();
                beanDefinition.setBeanClass(Class.forName(beanClass));
                
                // 注册beanDefinition
                this.getRegistry().registerBeanDefinition(beanName, beanDefinition);

                // 量+1
                count++;
            }
            
        } catch (Exception e) {
            
            e.printStackTrace();
        }
        
        return count;
    }
}
  1. 创建配置文件spring-beans.txt,并增加配置
heart:com.example.springdemo.demo1.Heart
  1. 在启动类或配置类上加@ImportResource
// 需要指定配置文件和读取器
@ImportResource(value = "spring-beans.txt", reader = TxtBeanDefinitionReader.class)
@SpringBootApplication
public class SpringDemoApplication {
  1. 执行看结果

可以从容器中获取到Heart对象,表示自定义的BeanDefinitionReader成功读取并注册了BeanDefinition。
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吖土豆

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值