spring boot 加载过程分析--ConfigurationClassPostProcessor

1,423 阅读7分钟

ConfigurationClassPostProcessor

前面说到ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor接口的实现,在调用invokeBeanFactoryPostProcessors时,会先被调用;而且通过之前的分析我们可以知道,它是在容器初始化置AnnotatedBeanDefinitionReader时注册到注册的(分析(二)),主要用来处理@Configuration注解。因实现还是比较复杂的,所以单独拿出来分析下。

先看一下该类实现的接口

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
        ....
}

它除了是BeanDefinitionRegistryPostProcessor的实现之外,还实现了一堆的Aware接口。我们在前面有介绍ApplicationContextAwareProcessor这个bean的扩展实现的功能,注入上下文属性(在调用getBean进行实例化时调用的,关于getBean调用过程后面再介绍)。

1. postProcessBeanDefinitionRegistry

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);
	}

代码比较简单,取得registry的id并做判重处理或记录,直接看processConfigBeanDefinitions方法,代码如下.

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		// 获取容器中注册的所有bean名字
		String[] candidateNames = registry.getBeanDefinitionNames();
                //遍历bean
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			//判断bean是否已经处理过
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 判断是否是Configuration类, 这里其实主要是看是否有
			// @Configuration, @Component, @ComponentScan, @Import, @Bean等注解
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// 这里筛选完之后,符合条件的只剩启动类 @SpringBootApplication
		if (configCandidates.isEmpty()) {
			return;
		}

		// 排序
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// 设置beanName的生成策略
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

		if (this.environment == null) {
			this.environment = new StandardEnvironment();
		}

		// 配置@Configuration的解析器,这里的部分属性就是通过ApplicationContextAwareProcessor来设置注入的。
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		// 解析
		do {
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// 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());
			}
			// 读取配置类,加载beanDefinition
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			// 如果在解析过程中,有新的配置类型beanDefinition注册加入,则循环处理
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				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());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}
                //  清除缓存
		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}
  • 获取已经注册的bean名称,进行遍历; 判断是否已经处理过,判断是否为配置类;如果有,排下序。
  • 如果BeanDefinitionRegistry 是SingletonBeanRegistry 子类的话,由于我们当前传入的是DefaultListableBeanFactory,是 SingletonBeanRegistry 的子类。因此会将registry强转为SingletonBeanRegistry;然后获取beanName的生成策略设置给成员变量。
  • 实例化ConfigurationClassParser 为了解析各个配置类.实例化2个set,candidates 用于将之前加入的configCandidates 进行去重,alreadyParsed 用于判断是否处理过。
  • 调用ConfigurationClassParser#parse进行解析 将解析过的配置类加入到configClasses;实例化ConfigurationClassBeanDefinitionReader 调用ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 进行加载,并加入到alreadyParsed中,用于去重 将candidates进行清空,如果registry中注册的bean的数量 大于 之前获得的数量,则意味着在解析过程中又新加入了很多,那么就需要对其进行解析
  • 注册一个,bean 为 ImportRegistry;清除缓存。

这里有一个判断是否为配置类的方法,其实检查的就是该类是否有@Configuration,@Component,@ComponentScan, @Import, @ImportResource, @Bean方法。

2. ConfigurationClassParser.parse

该方法的代码简单,最后调用会到processConfigurationClass方法,我们直接看该方法实现。

processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
                //是否跳过
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		// 处理Imported 的情况
		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.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
}
  1. 先看ConditionEvaluator.shouldSkip判断是否跳过,根据@Condition条件来判断。
  2. 判断处理imported的情况
  3. 递归解析configClass

3. doProcessConfigurationClass

解析class配置的核心方法,代码如下:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// 首先递归处理内部类
		processMemberClasses(configClass, sourceClass);

		// 处理 @PropertySource 注解
		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());
				// 检查扫描到的definition是否还有配置类型,有则递归解析
				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 注解
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			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 != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
}

3.1 处理内部类,不再贴代码;

  • 遍历configClass的内部类,判断是否为配置类且类名不与configClass相同
  • 判断是否存在循环引用

3.2 处理@PropertySource.通过遍历该类中的@PropertySource的注解,如果该类中的environment是ConfigurableEnvironment 子类的话,则调用processPropertySource进行处理.否则打印警告日志.一般都是ConfigurableEnvironment的子类

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
                //获取注解上的属性name
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		//获取注解上的属性encoding
		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");
		
                // 获取Facotry,如果没指定使用默认的DefaultPropertySourceFactory
		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		for (String location : locations) {
			try {
			        // 解析点位符,多环境配置
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				// 加载property配置文件
				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;
				}
			}
		}
}

解析@PropertySource注解上的各属性值,加载配置文件,然后添加到运行环境中。

3.3 处理@ComponentScan:判断是否应该跳过,并依次通过ComponentScanAnnotationParser.parse进行解析。最终调用ClassPathBeanDefinitionScanner.doScan。代码如下:

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
		        // 扫描basePackage路径下的java文件
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
			    // 解析是否有@Scop
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 生成beanName
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				// 设置默认配置
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				// 通用注解的解析设置
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 判断beanName容器中是否存在,不存在则注册,如存在判断是否兼容,不兼容抛异常
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

那么,通过上面一步后,我们自己写的@Controller, @Service等就被注册到容器中了。只是完成了beanDefinition的查找,解析并注册的工作,此时这些bean还未被实例化。ok, 回到doProcessConfigurationClass方法中对@ComponentScan的解析处接着看,因为@Controller, @Service, @Configuration等注解都是符合配置类校验的(ConfigurationClassUtils.checkConfigurationClassCandidate),所以还需要递归解析来看其他内部是否还有其他配置,比如内部类,@Bean等。

接着再往下,以次解析的是@Import, @ImportResource, 收集@Bean方法与接口的默认方法。这部分不再一一分析,流程和上面类似递归处理,最后把configClass都放到了集合中。

解析处理完之后,回到我们的ConfigurationClassPostProcessor方法继续看,实例化了reader类ConfigurationClassBeanDefinitionReader用来读取加载configClass的解析。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
	    loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
        //如果应该跳过,且容器中有,则移除
	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}
        // 注册@Import 
	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	// @注册Bean
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}

        // 注册@ImportResource 通过XmlBeanDefinitionReader读取配置
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	// 注册ImportBeanDefinitionRegistrar实现
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

通过之前的分析,其实我们知道@PropertySource,@ComponentScan的解析最后会把相关注解增加到环境配置或者注册到容器中去。但对@Import, @Bean的只是收集到集合中,并未注册到容器中,所以上面的load主要处理这一部分。

  1. registerBeanDefinitionForImportedConfigurationClass 注册@Import
  2. loadBeanDefinitionsForBeanMethod 注册@Bean
  3. loadBeanDefinitionsFromImportedResources 注册@ImportResource 通过XmlBeanDefinitionReader读取配置
  4. loadBeanDefinitionsFromRegistrars 注册ImportBeanDefinitionRegistrar实现

processConfigBeanDefinitions方法再下面的处理就是判断新增的beanDefinition是否有配置类,有则递归处理,无则清除缓存。

该篇文章分析详细,建议阅读一下。

参考链接:blog.csdn.net/qq_26000415…