1. 一个 Spring 加载类的问题
先抛出个问题:SpringBoot 允许通过注解根据某个类是否存在来决定配置,如
@Bean
@ConditionalOnClass(value = HikariDataSource.class)
public DataSource hikariDataSource() {
return new HikariDataSource();
}
@Bean
@ConditionalOnClass(value = BasicDataSource.class)
public DataSource basicDataSource() {
return new BasicDataSource();
}
我们也知道 ClassLoader
加载一个类时,如果这个类或这个类依赖的类找不到则会抛出 ClassNotFoundException
。
Spring 是如何实现这样的条件加载而不会抛出 ClassNotFoundException
异常?
2. Spring 解析配置类的过程
在第一篇已经介绍过 ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
会注册所有的 Bean 定义。现在来仔细看看这个过程。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
这个方法主要是做了防止重复注册的处理,具体的注册逻辑在 processConfigBeanDefinitions(registry)
。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 首先从 registry 找出已经注册的未处理的配置类的定义作为配置类候选
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 对配置类候选进行排序,以满足依赖配置依赖顺序
// Sort by previously determined @Order value, if applicable
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
// 找出 beanName 的生成器
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// Parse each @Configuration class
// 创建 ConfigurationClassParser 来解析候选配置类定义
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 当前待解析的候选配置类定义
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
// 当前已解析的配置类
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
// 解析当前候选集里的所有配置类定义
parser.parse(candidates);
// 验证解析的结果
parser.validate();
// 从解析获取解析得到的所有配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
// 移除已经处理过的配置类,因为下面要进行注册,防止重复注册
configClasses.removeAll(alreadyParsed);
// 创建 ConfigurationClassBeanDefinitionReader,用于注册配置类
// reader 的创建应该是可以放到循环外的
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 注册配置类
this.reader.loadBeanDefinitions(configClasses);
// 把本轮处理完的配置类加到已处理集合
alreadyParsed.addAll(configClasses);
// 清除已处理的候选集,复用这个集合
candidates.clear();
// 从 registry 找出新加入、未处理过的配置类定义,加入当前候选集
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<String>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) { // 过滤出新加入的
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) { // 找出未处理过的
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
} while (!candidates.isEmpty()); // 如果当前候选集不为空则继续下一轮处理
// 至此,通过 Java 类配置的 bean 定义都加载并注册到容器
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null) {
if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
上述方法并未涉及某个配置类定义如何解析得到配置类、注册配置类定义的细节这一层的逻辑如下:
1. 从 registry
收集配置类定义放到 Set<BeanDefinitionHolder> candidates
作为处理的开始源;
2. 创建 ConfigurationClassParser parser
用于解析新的配置类定义和 ConfigurationClassBeanDefinitionReader reader
用于注册新的配置类定义;
3. 初始化已解析集合 alreadyParsed
;
4. 用 parser
解析候选集 candidates
,进行验证;
5. 得到解析结果 Set<ConfigurationClass> configClasses
,移除已经处理过的 Set<ConfigurationClass> alreadyParsed
;
6. 用 reader
把收集到的配置类集合 configClasses
注册到 registry
;
7. 把 configClasses
加入已解析集合 alreadyParsed
;
8. 清空候选集 candidates
,重新收集候选集 candidates
;
9. 如果收集到的候选集 candidates
不为空,继续跌代步骤 4 。
现在来看如何解析候选集,ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)
:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {// 基于 Java 类配置的都是走这里
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// ....删掉了防止重复处理的逻辑
// SourceClass 是一个简单的包装,允许被注解的源类以统一的方式进行处理,而不管它们是如何加载的
// 递归处理配置类和它的超类
SourceClass sourceClass = asSourceClass(configClass);
do {
// 处理给定的配置类,如果它有非 Object 的父类,则返回其父类,递归处理
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 首先递归处理内部嵌套类
processMemberClasses(configClass, sourceClass);
// 处理 @PropertySource 注解,把资源文件加载到 environment 里
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
} else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 处理 @ComponentScan 注解,如果有,立即执行扫描
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 如果配置类有 @ComponentScan 注解,立即执行扫描
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 检查扫描到的定义里是否有更多的配置类,有则递归 parse
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 处理 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// 处理 @ImportResource 注解
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 处理 @Bean 注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理接口的默认方法
processInterfaces(configClass, sourceClass);
// 如果有,处理父类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// 没有父类,处理完成
return null;
}
解析一个配置类的大致过程:
- 递归处理所有嵌套类;
- 处理源类上
@PropertySource
注解; - 处理
@ComponentScan
注解; - 处理
@Import
注解,通过自定义的ImportSelector/ImportBeanDefinitionRegistrar
接口实现来加载bean定义; - 处理
@ImportResource
注解; - 处理所有有
@Bean
注解的方法,方法的信息封装在MethodMetadata
,与所在的配置类一起封装成BeanMethod
添加到配置类的beanMethods
集合里; - 递归处理所实现的 interfaces 上的默认方法;
- 如果非 Object、非 java 自带类的父类,则返回进行递归处理;
上面的解析过程中有个很重要的角色是 SourceClass
,表示 SpringBoot 自己解析到的源文件信息。
// SourceClass 是一个简单的包装,允许被注解的源类以统一的方式进行处理,而不管它们是如何加载的
// 递归处理配置类和它的超类
SourceClass sourceClass = asSourceClass(configClass);
do {
// 处理给定的配置类,如果它有非 Object 的父类,则返回其父类,递归处理
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
在上述的递归处理过程中,sourceClass 一开始是配置类本身对应的源类的信息,后续是其父类的源类信息。
SourceClass
SourceClass 与 Class 啥区别?为什么需要它?
从上面代码的 SourceClass sourceClass = asSourceClass(configClass);
开始看:
SourceClass asSourceClass(String className) throws IOException {
if (className.startsWith("java")) {
// 对于核心Java类不使用ASM,因为肯定存在的,直接加载
try {
return new SourceClass(this.resourceLoader.getClassLoader().loadClass(className));
} catch (ClassNotFoundException ex) {
throw new NestedIOException("Failed to load class [" + className + "]", ex);
}
}
return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
}
metadataReaderFactory 用完整类名构造一个 Resource,然后这个 Resource 和 ClassLoader 来构造一个 SimpleMetadataReader。SimpleMetadataReader 通过 ClassReader 来读取这个类的字节码文件,再通过 ASM 工具 AnnotationMetadataReadingVisitor 进行解析,得到关于这个类的元数据。这个过程绕过了JDK 的类加载机制,也就避免了一开始那个可能出现类找不到的问题。
有兴趣的可以继续看下 SimpleMetadataReader,MetadataReader
的唯一实现类:
final class SimpleMetadataReader implements MetadataReader {
private final Resource resource;
private final ClassMetadata classMetadata;
private final AnnotationMetadata annotationMetadata;
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader;
try {
classReader = new ClassReader(is);
} catch (IllegalArgumentException ex) {
throw new NestedIOException("ASM ClassReader failed to parse class file - " +
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
} finally {
is.close();
}
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
this.annotationMetadata = visitor;
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
this.classMetadata = visitor;
this.resource = resource;
}
// 省略其他代码
}