在 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: