在 ConfigurationClassPostProcessor
有对多个 注解进行分析,本文主要围绕 @Import
和 @PropertySource
进行详细分析:
Import
@Import
注解解析在 ConfigurationClassPostProcessor
解析中进行,具体位置在 ConfigurationClassParse
的 processImports
中:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// 判断是否有import类
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 判断import中类型
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
基于 @Import
引入类类型不同,Spring 容器分为三种进行处理:
- 普通类,则会将其作为一个
configuration
加入到待解析容器中,进行下次迭代解析。 ImportBeanDefinitionRegistrar
类,则会放入ConfigClass
的importBeanDefinitionRegistrars
中,因此父类解析完最后,再解析ImportSelector
类,则分为两种情况。如果是DeferredImportSelector
,则将放到最后进行解析,如果是普通的ImportSelector
则会立即调用
其selectImports
获取候选类进行解析。
Import 普通类
对于普通类的处理,Spring的处理方式是进行递归调用 当前 processConfigurationClass
方法:
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
ImportSelector 类
ImportSelector
类分为两种情况,其实差别挺大的。
DeferredImportSelector类
先看 DeferredImportSelector
接口结构:
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
// 处理类
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
// 获取import操作
Iterable<Entry> selectImports();
class Entry {}
}
上面接口有以下几个用途:
getImportGroup
返回一个Group对象Group
中process
用于处理类,而selectImports
则主要用于返回Entry
类型的集合。
下面从这几个方法调用来看看具体用途。
ConfigurationClassParser
中:
- 在
processImports
中,如果是DeferredImportSelector
类型,则会加入到DeferredImportSelectorHandler
中,用于后续处理:
DeferredImportSelectorHandler
是ConfigurationClassParser
内部类,主要用于缓存DeferredImportSelector
,用于最后执行。
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
- 在
parse
方法执行之后,最后一行则会执行this.deferredImportSelectorHandler.process();
public void process() {
// 获取所有的DeferredImportSelectorHolder
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 对 DeferredImportSelectorHolder 进行排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 调用每一个 DeferredImportSelector 的register 方法
deferredImports.forEach(handler::register);
// 对所有从 Group 中对象,执行其 selectImports 方法。
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
下面看 DeferredImportSelectorGroupingHandler
的 register
和 processGroupImports
:
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
// 如果是 DeferredImportSelector 接口,但是 其返回import接口为null,那么就去执行 `selectImports` 方法
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
// 加入到 configurationClasses 中
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 调用所有的group调用其getImports,而后对所有返回对象 执行 `processImports` 方法
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
普通 ImportSelector 类
Spring 对其解析是直接执行 selector
的 selectImports
方法,对返回的集合对象,在进行递归调用 processImports
:
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
ImportBeanDefinitionRegistrar 类
addImportBeanDefinitionRegistrar
类型则会在 parse 方法执行结束后,由Spring 统一解析并执行其 registerBeanDefinitions
,Spring 允许其通过该方法注册相关bean信息。
处理 ImportBeanDefinitionRegistrar
类
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
最终在 parse
执行之后,在 ConfigurationClassBeanDefinitionReader
的 loadBeanDefinitionsFromRegistrars
进行:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
即执行所有 ImportBeanDefinitionRegistrar
类型的 registerBeanDefinitions
方法。
PropertySource
@PropertySource
主要将 properties
下配置文件放入到 Enviroment
中:
@PropertySource
解析在 ConfigurationClassPostProcessor
中会被解析。在 扫描并解析 Configuration
和 Conponent
标签时,遇到 PropertySource
会有如下解析:
在 ConfigurationClassParser
中 doProcessConfigurationClass
中:
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
在 processPropertySource
中:
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
// 获取name
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
// 获取字节编码
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
// 获取值
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
// 获取解析器PropertySourceFactory
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
// 将Resource 加载出来,然后调用 addPropertySource 放到environment 的 propertySources 中
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
- 上面方法主要是将 解析出来
Resource
,放入到SpringContext
中enviroment
的propertySources
了。
也就是说,如果你一旦使用 @PropertySource
,properties
中定义,在全局中都可以使用,也就是容器中 类A上加注解引入了,你可以在容器类B中使用。
总结
对于 @Import
难免有点抽象,可以使用几个例子进行分析,https://github.com/anLA7856/springLearn/
觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring: