spring-context注解源码系列七——@ImportResource
注解说明
Indicates one or more resources containing bean definitions to import.
指定导入一个或者多个包含了 BeanDefinition 的文件。
ImportResource和Import区别:
Import(详细说明点击查看)支持导入Class文件;ImportResource则可以导入任何类型的配置文件,默认是支持导入xml和groovy文件,如果要导入其他格式的文件,需要传入自定义的BeanDefinitionReader。
spring自带3种BeanDefinitionReader,它们分别是:XmlBeanDefinitionReader、PropertiesBeanDefinitionReader、GroovyBeanDefinitionReader。
属性说明
/**
* 同locations
*/
@AliasFor("locations")
String[] value() default {};
/**
* 需要被导入的资源的路径
*/
@AliasFor("value")
String[] locations() default {};
/**
* 自定义资源解析器
*/
Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
使用示例
@SpringBootApplication
@ImportResource(locations={"classpath:/spring/*.xml"})
public class BackApplication {
public static void main(String[] args) {
SpringApplication.run(BackApplication.class, args);
}
}
相关源码
ConfigurationClassParser
/**
* Apply processing and build a complete {@link ConfigurationClass} by reading the
* annotations, members and methods from the source class. This method can be called
* multiple times as relevant sources are discovered.
* @param configClass the configuration class being build
* @param sourceClass a source class
* @return the superclass, or {@code null} if none found or previously processed
*
* 读取配置类中的注解、内部类、方法来完成配置类的解析
*/
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
......
// Process any @ImportResource annotations
// 处理ImportResource注解,将locations的内容和reader对应的资源解析器放在importedResources存起来
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
// 取出配置的资源路径
String[] resources = importResource.getStringArray("locations");
// 取出解析器的类型
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
// 根据配置生成真正的资源路径
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 把信息放到配置类的importedResources中
configClass.addImportedResource(resolvedResource, readerClass);
}
}
......
// No superclass -> processing is complete
return null;
}
ConfigurationClassBeanDefinitionReader
/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*
* 读取并注册单个配置类中的beanDefinition
*/
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
......
// 解析@ImportResource导入的资源文件(配置类的importedResources)并转化为BeanDefinition
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
......
}
/**
* 解析@ImportResource导入的资源文件并转化为BeanDefinition
*/
private void loadBeanDefinitionsFromImportedResources(
Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
importedResources.forEach((resource, readerClass) -> {
// Default reader selection necessary?
// 如果没有设置解析器,则根据文件的后缀名选择对应的解析器
if (BeanDefinitionReader.class == readerClass) {
// 如果后缀名是 groovy,则使用 GroovyBeanDefinitionReader,否则使用 XmlBeanDefinitionReader
if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
// When clearly asking for Groovy, that's what they'll get...
readerClass = GroovyBeanDefinitionReader.class;
}
else {
// Primarily ".xml" files but for any other extension as well
readerClass = XmlBeanDefinitionReader.class;
}
}
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
if (reader == null) {
try {
// Instantiate the specified BeanDefinitionReader
// 如果之前没有创建过解析器,则先创建解析器并缓存起来
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
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() + "]");
}
}
// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
// 使用对应的解析器解析配置文件
reader.loadBeanDefinitions(resource);
});
}