一、简述
代码环境: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的配置文件
- 创建一个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;
}
}
- 创建配置文件
spring-beans.txt
,并增加配置
heart:com.example.springdemo.demo1.Heart
- 在启动类或配置类上加
@ImportResource
// 需要指定配置文件和读取器
@ImportResource(value = "spring-beans.txt", reader = TxtBeanDefinitionReader.class)
@SpringBootApplication
public class SpringDemoApplication {
- 执行看结果
可以从容器中获取到Heart对象,表示自定义的BeanDefinitionReader成功读取并注册了BeanDefinition。