ConfigurationClassParser类会被ConfigurationClassPostProcessor用来处理配置类,可以说该类完成了registry后置逻辑的大部分工作。本文分析该类的主要工作流程和实现原理。
该类ConfigurationClassParser和ConfigurationClassBeanDefinitionReader类共同组成了ConfigurationClassPostProcessor的registry的后置操作中的核心实现。
配置类判断
在介绍解析配置类之前,先看一下Spring对配置类的判断标准是怎样的。
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
// 存在factory-method的情况不算是配置类
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
// 获取目标类的元数据
AnnotationMetadata metadata;
// 可以直接获取元数据的情况
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
// 直接获取元数据
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
// 如果类属于下面几种,则不属于是配置类
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
// 根据Class来构建元数据
metadata = AnnotationMetadata.introspect(beanClass);
}
else {
try {
// 获取元数据读取器
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
// 读取元数据
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
}
return false;
}
}
// 如果存在@Configuration注解,那么获取注解内的proxyBeanMethods属性,并据此设置配置的模式(full还是lite)
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果存在@Configuration注解,且proxyBeanMethods属性为true,那么是FULL模式
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 如果没有@Configuration注解,但是类满足几点要求也算是配置类(具体是什么要求点进去看),不过是LITE模式
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
// 获取配置类的顺序,也就是类上@Order注解的value值
Integer order = getOrder(metadata);
if (order != null) {
// 设置顺序
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
private static final Set<String> candidateIndicators = new HashSet<>(8);
// 如果类上面有下面四种注解,那么会认为该类是配置类
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
// 不能是接口
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
/*
* 只要被下面任意几个注解修饰,都算:
* @Component
* @ComponentScan
* @Import
* @ImportResource
*/
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
/*
* 另外,就算只有@Bean修饰的方法,那么也算
* 所以,并不一定必须在类上标注@Configuration注解
* 但最好放在@Configuration类中,因为可以使用full模式的配置
* 因为在full模式的情况下,方法会被代理,调用方法返回的是容器中的对象;
* 而在lite模式的情况下,方法不会被代理,每次调用返回的都是新对象
*/
return hasBeanMethods(metadata);
}
可以看到,只要类被@Component、@ComponentScan、@Import、@ImportSource注解修饰,都算是配置类。其实@Configuration也被@Component修饰,只不过可以配置是用full还是lite模式,full模式会对类使用CGLIB增强。
在ConfigurationClassPostProcessor中会调用ConfigurationClassParser类的parse方法,下面以该方法为切入口,来分析一下该类的工作原理。
parse方法主要流程
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 遍历配置类的bean定义
for (BeanDefinitionHolder holder : configCandidates) {
// 从holder对象中获取bean定义
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
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);
}
}
// 执行推迟了的ImportSelector
this.deferredImportSelectorHandler.process();
}
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
/*
* 在ConfigurationClassPostProcessor中传递进来的metadataReaderFactory是CachingMetadataReaderFactory类型的.
* 这里的reader默认是SimpleMetadataReader类型的实例。
*/
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 检查一下条件注解
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判断配置类是否已经处理过了
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) { // 如果当前类已经导入过了
if (existingClass.isImported()) { // 已存在的类也已经导入过了
existingClass.mergeImportedBy(configClass); // 合并两个配置类,用当前类替换已存在的类的配置
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else { // 如果当前类没有导入过
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
// 将配置类封装为sc对象,方便获取元数据
SourceClass sourceClass = asSourceClass(configClass, filter);
// 只要doProcessConfigurationClass方法不返回null,就一直递归。
do {
// 解析配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 添加配置类,这里的key-value都一样,为什么不用set?
this.configurationClasses.put(configClass, configClass);
}
在下面3个重载的parse方法中,会创建ConfigurationClass对象,下面的处理过程中有些步骤会把解析结果保存到该对象的属性中,后续在ConfigurationClassBeanDefinitionReader中会处理。
该类的多个方法中大量使用了SourceClass这个类型,这个类是对配置类的元数据的封装。提供了很多getXXX方法来获取类的元数据,比如继承的父类、实现的接口、类上的注解、内部类等信息。知道该类可以做这些事情就可以了,暂时没必要直到是怎么获取的。
主要的逻辑在doProcessConfigurationClass方法中。
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 如果类上有@Component注解,其他衍生注解也算
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 递归处理内部类,也就是判断内部类是不是配置类
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
// 处理@PropertySources注解,遍历该注解中的@PropertySource注解数组
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
// 处理@PropertySource注解
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 处理@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
// 判断当前配置类能否跳过,处理@Condition等条件注解
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 遍历@ComponenetScan注解
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 扫描类路径下的bean,内部是会进行bean注册的,这里的返回值的作用是为了下面进行的递归解析。
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
// 遍历扫描结果
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 获取bean定义
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 解析到的bean属于配置类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 进行递归解析
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
// 处理@ImportResource注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
// 获取locations属性的值
String[] resources = importResource.getStringArray("locations");
// 获取reader属性的值
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
// 遍历资源路径位置
for (String resource : resources) {
// 解析占位符
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 添加导入源到配置类(ConfigurationClassPostProcessor)对象中
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual @Bean methods
// 处理类中的@Bean方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 添加bean方法对象到配置类对象中
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 处理接口中的bean方法,也会将bean方法对象添加到配置类对象中
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 处理父类
if (sourceClass.getMetadata().hasSuperClass()) {
// 获取父类名称
String superclass = sourceClass.getMetadata().getSuperClassName();
// 存在父类,且父类不是JDK中的类,且没有处理过该父类(即knownSuperclasses中不存在)
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
// 放入集合中
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
// 返回父类,便于上层方法(processConfigurationClass方法)继续循环处理
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
该方法较长,涉及的内容较多,但可以分为下面部分:
- 处理内部类;
- 处理@PropertySources注解;
- 处理@ComponentScan注解;
- 处理@Import注解;
- 处理@ImportResource注解;
- 处理@Bean方法;
- 处理父类;
下面依次针对上面几个处理操作进行详细分析。
处理内部类
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
// 获取类中的内部类信息
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
// 遍历内部类
for (SourceClass memberClass : memberClasses) {
// 判断内部类是不是配置类
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
// 筛选的是内部类,不能是当前类本身。这里对对上面调用的getMemberClasses方法不信任?
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 排序
OrderComparator.sort(candidates);
// 遍历内部配置类
for (SourceClass candidate : candidates) {
// 如果栈中已包含该类,
if (this.importStack.contains(configClass)) {
// 则报告环形导入错误
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 入栈
this.importStack.push(configClass);
try {
// 处理内部配置类
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
// 出栈
this.importStack.pop();
}
}
}
}
}
该类虽然较长,但是主要逻辑很清楚,就是递归调用上面介绍到的processConfigurationClass方法进行处理。
importStack记录当前调用栈中处理的配置类,避免出现循环导入的问题,否则那样会出现死循环。
处理@PropertySources注解
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
/*
* 获取@PropertySource注解的各个属性的值
*/
// 获取名称
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");
// 必须要求locations配置了值
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
// 创建属性源工厂,用于创建属性源
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
// 遍历每个location
for (String location : locations) {
try {
// 解析占位符
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 获取资源
Resource resource = this.resourceLoader.getResource(resolvedLocation);
// 添加数据源到environment中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
}
}
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
该访问中主要获取@PropertySources注解中的属性,然后创建指定的属性文件的资源对象并创建属性源对象,最后添加到environment中。
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
// 获取environment中的已有属性源
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
// 如果处理过了同名的属性源
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
// 获取已存在的属性源
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
// 是不是复合属性源
if (existing instanceof CompositePropertySource) {
// 将新属性源添加到列表最前面
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
// 创建复合属性源
CompositePropertySource composite = new CompositePropertySource(name);
// 将多个同名的属性源封装进一个复合属性源中
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
// 将属性源添加到environment中
propertySources.addLast(propertySource);
}
else {
// 这里明明是获取上一个处理的属性源,这里居然叫first?
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
// 将新属性源添加在上一个添加的属性源之前,这也就是说后添加的优先级更高咯?
propertySources.addBefore(firstProcessed, propertySource);
}
// 记录处理过的属性源
this.propertySourceNames.add(name);
}
该方法首先检查了是否存在同名的数据源,如果是,则创建一个复合数据源来封装多个同名的数据源。然后将数据源添加到environment的数据源列表首部。
处理@ComponentScan注解
这部分内容很重要,可以说在开发业务系统中,大部分的bean都是通过这种方式注册的。比如@Controller、@Service、@Repository等注解都是通过这种方式进行扫描和注册的。这里是委托给ComponentScanAnnotationParser类来实现的,在ConfigurationClassParser的构造方法中创建的就是该类型的实例。
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
// 创建组件扫描解析器
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
下面就来看看ComponentScanAnnotationParser是怎么实现的。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
// 创建类路径bean定义扫描器
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
/*
* 下面先获取@ComponentScan注解的属性,然后设置到扫描器中。
*/
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
}
else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
// 设置类文件的模式,默认是ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN,表示子任意目录下的任意class文件
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
// 根据@Filter注解(位于@ComponentScan注解文件中)来创建过滤器
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
this.resourceLoader, this.registry);
for (TypeFilter typeFilter : typeFilters) {
// 设置包含过滤器
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
// 创建过滤器
List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
this.resourceLoader, this.registry);
for (TypeFilter typeFilter : typeFilters) {
// 设置排除过滤器
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
// 设置延迟初始化bean
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
Set<String> basePackages = new LinkedHashSet<>();
// 获取直接指定的包名,会扫描当前包以及子包(因为上面的默认资源模式是任意子目录)
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
// 字符串风格,支持逗号,分号,空格,制表符和换行符
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
// 获取指定的类
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
// 获取指定类所在的包,会扫描这个类所在的整个包,包括子包
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果没有设置basePackages或basePackageClasses,那么则扫描@Component所修饰的类及其子包下的类
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
// 排序@Component所修饰的类本身
return declaringClass.equals(className);
}
});
// 扫描bean
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
该方法看起来很长,其实就完成了三件事情:
- 创建ClassPathBeanDefinitionScanner;
- 处理@ComponentScan注解中的属性;
- 调用ClassPathBeanDefinitionScanner的doParse方法;
关于ClassPathBeanDefinitionScanner是怎么工作的,请参考Spring中的ClassPathBeanDefinitionScanner一文。
处理@Import注解
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 检查循环导入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
// 报告循环导入错误
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
/*
* 遍历import的每一个类,分为了三部分:
* * ImportSelector
* * ImportBeanDefinitionRegistrar
* * other
*/
for (SourceClass candidate : importCandidates) {
// 处理ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 获取Class对象
Class<?> candidateClass = candidate.loadClass();
// 实例化目标类对象
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// 获取排除过滤器
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
// 两个过滤器或上,任意一个满足即符合排除要求
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 特殊的导入选择器,需要在所有的配置类处理完成后才处理。
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
/*
* 执行选择导入,传入是导入的它的那个类的元数据信息,也就是@Import注解作用在的那个类。
* 这里也就是在向被导入方传入导入方的信息。
*/
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 对需要导入的类创建sc对象
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归处理,所以这里看出来SelectImporter的作用就有点像是@Import注解的作用。
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 处理ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 获取Class对象
Class<?> candidateClass = candidate.loadClass();
// 实例化目标类对象
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
/*
* 这里先add,之后在ConfigurationClassBeanDefinitionReader中会被处理。
*/
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());
// 递归处理候选者,将其作为配置类对待,所以也说@Import导入类也具备配置类的效果。
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
finally {
this.importStack.pop();
}
}
}
该方法中针对导入的类的类型,分为下面三种情况:
- 处理ImportSelector;
- 处理ImportBeanDefinitionRegistrar(简称”注册器“);
- 处理普通类;
处理ImportSelector
这里是在调用ImportSelector的selectImports方法来获取需要导入哪些类,然后递归调用processImports方法来处理。
处理DeferredImportSelector
这部分比较重要,在Spring Boot中就是通过这种方式加载自动配置类的。因为Spring为延迟选择器引入了分组机制,所以整个处理过程稍微有一些复杂,注意理解。
这部分会涉及多个内部类,会比较绕,先来梳理一下:
- DeferredImportSelectorHandler:选择器处理器,负责对外层提供处理接口,ConfugurationClassParser中的主要过程都是调用该类中的两个方法handle和process。
- DeferredImportSelectorGroupingHandler:选择器分组处理器,负责对选择器进行分组,并调用每个分组的getImports方法来获取配置类。
- DeferredImportSelectorGrouping:选择器分组包装类,封装了实际的DeferredImportSelector.Group这个组对象,内部保存了同一个组下的选择器列表。
- DefaultDeferredImportSelectorGroup:默认的选择器分组类,如果选择器没有定义分组类型,即没有实现DeferredImportSelector接口的getImportGroup方法,则会使用默认的。
- DeferredImportSelectorHolder:封装了选择器,以及导入该选择器的配置类对象。
下面来看这5个内部类是如何相互协调工作的。
首先通过选择器处理器(DeferredImportSelectorHandler)的handle方法来记录该延迟选择器。这里先将其封装到holder对象中(DeferredImportSelectorHolder),然后放到一个列表中,并没有立马调用它的selectImports方法,而这正是Deferred(推迟)的含义。那又是怎么调用到的呢?在上面介绍的parse方法中,等处理完当前配置类中的其他配置后,最后会调用选择器处理器的process方法来处理。
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
// 将配置类封装进入holder对象
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
// 这里默认不是null
if (this.deferredImportSelectors == null) {
// 创建处理器对象
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
// 添加holder对象到列表中
this.deferredImportSelectors.add(holder);
}
}
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 排序
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 注册到导入组中
deferredImports.forEach(handler::register);
// 执行组导入
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
在上面选择器处理器的process方法中,会先对选择器排序,然后通过选择器分组处理器(DeferredImportSelectorGroupingHandler)的register方法来分组。先获取选择器的分组对象,然后封装进入包装对象(DeferredImportSelectorGrouping),如果选择器没有定义分组对象,那么使用默认的分组对象(DefaultDeferredImportSelectorGroup)。因为Spring在这里使用的LinkedHashMap来保存分组包装对象,所以分组也是有序的,而是就是选择器的顺序。
// 可以保证顺序
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
// 这个方法其实是在对延迟导入选择器(DeferredImportSelector)进行分组
public void register(DeferredImportSelectorHolder deferredImport) {
// 获取选择器的分组类型
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
// 获取或创建grouping对象,这里的grouping对象封装了group类型的对象
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
// 创建group类型的对象,然后封装到DeferredImportSelectorGrouping对象中
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 添加到组中
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
private Group createGroup(@Nullable Class<? extends Group> type) {
// 如果存在有效的组类型,则使用,默认的组类型是DefaultDeferredImportSelectorGroup
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
// 创建分组对象
return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
ConfigurationClassParser.this.environment,
ConfigurationClassParser.this.resourceLoader,
ConfigurationClassParser.this.registry);
}
// 可以保证顺序
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void processGroupImports() {
// 遍历每个导入组
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 遍历导入组中的导入
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
// 调用processImports执行导入,在整个大流程范围内算是递归调用
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
});
}
}
public Predicate<String> getCandidateFilter() {
Predicate<String> mergedFilter = DEFAULT_EXCLUSION_FILTER;
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 获取选择器的过滤器
Predicate<String> selectorFilter = deferredImport.getImportSelector().getExclusionFilter();
if (selectorFilter != null) {
// 和其他选择器的过滤器或上
mergedFilter = mergedFilter.or(selectorFilter);
}
}
return mergedFilter;
}
在processGroupImports方法中,先调用分组包装对象的getCandidateFilter来处理多个选择器的排除过滤器,然后会调用分组包装对象的getImports方法来获取配置类,最后调用上面介绍过的processImports方法来递归处理获取到的每个配置类。
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
这里会调用实际的Group对象的process方法来准备配置类,然后调用selectImports方法来获取配置类。比如在Spring Boot中的AutoConfigurationImportSelector就是用的自动配置组AutoConfigurationGroup。
如果有的选择器没有定义分组类型,那么会使用默认的分组。
private static class DefaultDeferredImportSelectorGroup implements Group {
private final List<Entry> imports = new ArrayList<>();
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
// 直接调用其selectImports方法
for (String importClassName : selector.selectImports(metadata)) {
this.imports.add(new Entry(metadata, importClassName));
}
}
@Override
public Iterable<Entry> selectImports() {
return this.imports;
}
}
处理ImportBeanDefinitionRegistrar
对与该类型的类,没有做太多的操作,仅仅是添加到配置类的集合属性中。后续ConfigurationClassPostProcessor会调用ConfigurationClassBeanDefinitionReader来处理。
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
处理普通类
这种情况的处理就非常简单了,直接调用processConfigurationClass方法来递归处理。
处理@ImportResource注解
@ImportResource注解可以导入其他spring配置文件,比如在传统的SSM应用中使用很多的XML配置文件,不过目前在Spring Boot中就很少使用了。但还是简单看一下这里是怎么处理的。 和上面的ImportBeanDefinitionRegistrar类似,这里也是把注解中的路径和文件读取器信息添加到配置类的对象中,待后续ConfigurationClassBeanDefinitionReader来处理。
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
处理bean方法
处理当前类本身中的bean方法
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
// 获取类的元数据
AnnotationMetadata original = sourceClass.getMetadata();
// 获取所有被@Bean注解修饰的方法
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
// 一般考虑这里是SimpleAnnotationMetadata,所以这里不满足条件
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
}
return beanMethods;
}
该方法中,获取了类中所有被@Bean注解修饰的方法。 获取到bean方法中,还是添加到配置类的属性中,待后续ConfigurationClassBeanDefinitionReader来处理。
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
void addBeanMethod(BeanMethod method) {
this.beanMethods.add(method);
}
处理实现的接口中的bean方法
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 获取当前类实现的所有接口
for (SourceClass ifc : sourceClass.getInterfaces()) {
// 获取接口中的被@Bean注解修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
// 遍历bean方法
for (MethodMetadata methodMetadata : beanMethods) {
// 如果不是抽象方法
if (!methodMetadata.isAbstract()) {
// A default method or other concrete method on a Java 8+ interface...
// 添加bean方法对象
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
// 递归处理父接口
processInterfaces(configClass, ifc);
}
}
这里会处理当前接口及其父接口中的bean方法,然后添加到配置类的属性中。
处理父类
这里通过knownSuperclasses属性来避免多次处理到同一个父类。
在doProcessConfigurationClass方法中,如果当前配置类的父类还没被处理过,那么会返回父类的SourceClass对象,在上层processConfigurationClass的do...while循环中会递归处理。processConfigurationClass方法的代码在上面就已经给出了,这里再次贴出一下,重点关注方法中的循环部分,就是处理父类的实现。
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 检查一下条件注解
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判断配置类是否已经处理过了
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) { // 如果当前类已经导入过了
if (existingClass.isImported()) { // 已存在的类也已经导入过了
existingClass.mergeImportedBy(configClass); // 合并两个配置类,用当前类替换已存在的类的配置
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else { // 如果当前类没有导入过
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
// 将配置类封装为sc对象,方便获取元数据
SourceClass sourceClass = asSourceClass(configClass, filter);
// 只要doProcessConfigurationClass方法不返回null,就一直递归。
do {
// 解析配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 添加配置类,这里的key-value都一样,为什么不用set?
this.configurationClasses.put(configClass, configClass);
}
总结
本文介绍了ConfigurationClassPostProcessor中registry的后置操作的核心过程。Spring Boot中大量使用的注解配置大部分最终都会在本文涉及的过程中来实现,这样Spring Boot中的一些配置过程就和容器刷新这条主线关联上了。