上一节分析invokeBeanFactoryPostProcessors方法时,仅对 parser.parse(candidates)进行深入分析。本节着重分析 this.reader.loadBeanDefinitions(configClasses)方法。
@Import注解导入的类总共分成三种:
上一节只针对doProcessConfigurationClass整体流程进行分析,如下图所示。先分析doProcessConfigurationClass方法中的对@Import注解的处处理。
processImports(configClass, sourceClass, getImports(sourceClass), true);
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
//将当前的sourceClass进入visited 防止无限递归
if (visited.add(sourceClass)) {
//找出当前类上的所有注解,然后遍历注解,直到注解是@Import或者注解为空
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
//比如@Enablexx,需要递归继续调用
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
getImports返回当前类的所有导入类。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
//Import循环校验
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
//将当前类加入栈中
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
/**
* @import导入的类总共有三种:A、普通类、实现ImportSelect、ImportBeanDefinitionRegistrar的类
*
* 1、对于实现ImportSelector接口的导入类,如果并且还为deferredImportSelectorHandler的实现类,则在自动装配时处理。
* 对于仅实现的ImportSelector接口的类先实例化,然后调用selectImport接口得到要导入的类,然后转化成sourceClass然后
* 递归调用processImports;
*
* 2、对于实现ImportBeanDefinitionRegistrar的导入,同样进行实例化,然后加入
* Map importBeanDefinitionRegistrars =new LinkedHashMap<>()
* Key为实例,value为导入该类的类信息(比如当前candidateClass是A,A是Config类导入的,此时value就为config的信息);
*
* 3、导入的普通类,将当前candidate结合configClass(该信息主要用于candidate记录是被谁导入的)生成新的configClass,
* 递归调用processConfigurationClass
*
*
* 总结:
* A、针对导入的ImportSelect实现类以及普通类,最终都会执行processConfigurationClass,作为Configuration放入
* private final Map configurationClasses = new LinkedHashMap<>()
* 但是都还没注册到beanDefinitionMap中,通过后续的 this.reader.loadBeanDefinitions(configClasses)实现注册
* beanDefinitionMap。
*
* B、实现ImportBeanDefinitionRegistrar接口的类,未放入configurationClasses中,将实现类标记在ConfigurationClass中
* importBeanDefinitionRegistrars的属性上,最终通过this.reader.loadBeanDefinitions(configClasses)实现注册
*
*/
if (candidate.isAssignable(ImportSelector.class)) {
// 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);
//因为导入的类可能也会有@Import标记,则继续递归调用
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);
//将导入注册类实例化后放入map中,map中的key为实例bean value为当前sourceClass的注解
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());
//在当前ConfigurationClass中通过importBy 标记是通过哪个ConfigurationClass引进的,便于后续处理
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();
}
}
}
ImportSelector
实例化ImportSelector的实现类,然后转化成SourceClass,递归调用processImports;
ImportBeanDefinitionRegistrar
实例化ImportBeanDefinitionRegistrar的实现类,然后添加到
Map
中,key为实例化对象,value为导入类的AnnotationMetadata;
普通类
将导入的普通类转成ConfigurationClass对象,然后调用processConfigurationClass方法。
ConfigurationClass通过
private final Set importedBy = new LinkedHashSet<>(1)
属性记录是被哪个类引入的;
下面以demo进行说明:
@Configuration
@Import({
ImportBeanDefinitionRegistrarImpl.class, ImportSelectorImpl.class, ImportBean.class})
@ImportResource(value="classpath:spring-config.xml")
public class Config {
/**
* Import总结,总共三种情况:1、引入普通的类;2、引入实现ImportSelector的类;3、引入ImportBeanDefinitionRegistrar实现的类
*/
@Bean
public static Student user(){
return new Student();
}
}
public class ImportBeanDefinitionRegistrarImpl implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
public class ImportSelectorImpl implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"xu.jiang.hua.im.ImportSelectBean"};
}
}
public class ImportBean {
}
以上是 ConfigurationClassParser类属性
private final Map
ImportSelectBean作为ImportSelectort接口实现的导入类,未被注册到BeanDefinitionMap中,此处importBy记录ImportSelectBean被Config导入,如红框1所示;
ImportBean作为@Import注解直接导入的类,未被注册到BeanDefinitionMap中,此处importBy记录ImportBean被Config导入,如红框3所示;
ImportBeanDefinitionRegistrarImpl未生成相应的对象放入configurationClasses 中,但是被记录在Config类中的importBeanDefinitionRegistrars的map中,如红框8所示;
Config类中的@Bean方法被记录在Config类所对应Configuration中的beanMethods,如6所示;
Config类中的@ImportSource注解导入的配置存储在Config类所对应Configuration中的ImporedResource;
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//遍历所有的configurationClass
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
//忽略非核心代码
//如果configClass被Import引入,未注入到beanDefinitionMap中,此处册到beanDefinitionMap中
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
//如果configClass里面的@Bean注解,此处对@Bean标记的bean注册到beanDefinitionMap中
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//当前类导入的ImportResources
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//处理当前类导入的ImportBeanDefinitionRegistrars
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
//主要处理:Primary Role DependOn Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//注册到beanDefinitionMap中
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);
if (logger.isTraceEnabled()) {
logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
}
}
ImportBean和ImportSelectBean所对应的importBy非空,会被上述方法注册到容器;
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
回调ImportBeanDefinitionRegistrars接口的实现。
对于Config类里面有@Bean标记的方法,因此Config class所对应的Configuration里面的beanMethods为非空,如下所示:
则会调用以下方法:
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// Consider name and any aliases
//优先从注解里面取属性name,如果为空 则直接取方法名为beanName
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden
//注册别名
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
if (metadata.isStatic()) {
// static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
beanDef.setFactoryMethodName(methodName);
}
else {
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getString("value"));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
//注册到beanDefinitionMap
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
本节第1步部分分析了@Import、@ImportResouce等注解导入的类如何在所对应类的Configuration中表示;第2部分分析如何将@Import、@Bean、@ImportResource标记的类注册到beanDefintionMap中;截止到目前,已经完成容器如何将spring管理的类注册到beanDefinitionMap的分析(除自动装配外,作为专题单独分析),下一节分析spring如何实例化bean。