BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。
下面我们看一下BeanDefinitionLoader的构造方法:
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
// 读取注解
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
// 读取XML配置
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
// 读取classpath
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
从构造方法中我们可以看到,在BeanDefinitionLoader分别代理了AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、GroovyBeanDefinitionReader、ClassPathBeanDefinitionScanner分别用于从类注解、XML文件、Groovy文件和classpath中加载类的定义信息。
另外,在构造BeanDefinitionLoader时要指定一个BeanDefinition注册中心,这个注册中心一般就是当前的ApplicationContext,还需要执行要加载的源。
加载过程
针对不同的源,进行了不同的实现:
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
从类注解加载
在加载类时,BeanDefinitionLoader首先会判断该类是否包含@Component注解,如果包含则执行注册逻辑,否则不注册。
private int load(Class<?> source) {
// 忽略Groovy文件加载逻辑
if (isComponent(source)) {
this.annotatedReader.register(source);
return 1;
}
return 0;
}
从XML文件加载
加载XML文件的过程实际由XmlBeanDefinitionReader完成,首先会将目标XML文件加载到内存中,并用dom方式进行解析,然后将得到的BeanDefinition进行注册,并返回注册成功的数量。
// 省略非核心代码
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
return count;
从包名加载
ClassPathBeanDefinitionScanner首先从classpath:{packageName}目录加载所有的类文件,确定给定的bean定义是否符合候选条件(默认实现检查该类是否不是接口而不依赖于封闭类),然后将符合候选条件的BeanDefinition进行注册。
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
从字符串加载
这里的字符串可以是一个类名,也可以是XML文件路径,也可以是一个包名。
BeanDefinitionLoader在拿到这个字符串后,会先替换字符串中的占位符。首先将字符串认为是类名,尝试按照从类注解中加载的方式进行加载;
如果加载失败,则认为该字符串是文件路径,尝试按照从文件加载的方式进行加载;
否则尝试按照从包名加载的方式进行加载;
如果以上方式都无法加载,则抛出异常信息。
private int load(CharSequence source) {
// 替换字符串中的占位符
String resolvedSource = this.xmlReader.getEnvironment()
.resolvePlaceholders(source.toString());
// 首先将字符串认为是类名
try {
return load(ClassUtils.forName(resolvedSource, null));
}
catch (IllegalArgumentException | ClassNotFoundException ex) {
// swallow exception and continue
}
// 既然不是类名,就认为是文件路径
Resource[] resources = findResources(resolvedSource);
int loadCount = 0;
boolean atLeastOneResourceExists = false;
for (Resource resource : resources) {
if (isLoadCandidate(resource)) {
atLeastOneResourceExists = true;
loadCount += load(resource);
}
}
if (atLeastOneResourceExists) {
return loadCount;
}
// 既不是类名,也不是文件路径,则认为是包名
Package packageResource = findPackage(resolvedSource);
if (packageResource != null) {
// 加载包下的Bean定义信息
return load(packageResource);
}
throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}
网友评论