Spring源码之BeanDefinitionReader解析与自定义

发布于:2022-11-08 ⋅ 阅读:(717) ⋅ 点赞:(0)

一、简述

相对于ClassPathBeanDefinitionScanner,BeanDefinitionReader读取是定义静态配置文件中bean。

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

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

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

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

二、源码分析

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

顺带一提,beanDefinition的加载,是在Ioc加载过程中的invokeBeanFactoryPostProcessors阶段,也就是执行所有的BeanFactoryPostProcesser的时候,其中就有一个步骤,invokeBeanDefinitionRegistryPostProcessors执行所有的BeanDefinition注册器,默认就一个ConfigurationClassPostProcessor,这里面就会获取所有的配置类,循环遍历这些配置类,去执行接下来要说的核心代码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。
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看