Spring注解源码解析(1):@Configuration和@Bean

Spring注解源码解析(1):@Configuration和@Bean

流程图:

image-20220611234454921

首先,Spring会去我们指定包路径下扫描出符合条件的类,而且,这里比较关键的是,Spring默认会在组件ClassPathBeanDefinitionScanner中,为注解@Component注册相应的注解过滤器,这样方便Spring筛选出那些标注了注解@Component的类,而且,我们在源码中也看到了Spring确实也是这样来过滤筛选的。

另外,我们发现添加了注解@Component的类最后也会封装为BeanDefinition,只不过BeanDefinition是注解类型的ScannedGenericBeanDefinition,ScannedGenericBeanDefinition不但继承了GenericBeanDefinition,同时也实现了接口AnnotatedBeanDefinition。

所以,我们看到Spring不仅为BeanDefinition设置了一些默认的属性信息,而且,Spring还进一步的解析类中的其他注解的信息,并且,将所有的注解信息都统一封装到了BeanDefinition中,然后将BeanDefinition注册到Spring容器里,核心思想和我们之前解析xml文件是类似的。

最后,我们又看了几个和注解@Component类似的注解,如注解@Controller、@Service和@Repository,最后发现它们底层也是通过注解@Component来实现功能的,本质和注解@Component是类似的,标注了这些注解的类同样也是会被Spring扫描到的。

 * @see AnnotationConfigApplicationContext
 * @see ConfigurationClassPostProcessor
 * @see org.springframework.core.env.Environment
 * @see org.springframework.test.context.ContextConfiguration
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
ConfigurationClassPostProcessor后置工厂处理器

image-20220611230713075

ConfigurationClassPostProcessor是接口BeanFactoryPostProcessor的实现类,也就是说ConfigurationClassPostProcessor本质上来说就是一个工厂级别的后处理器BeanFactoryPostProcessor。

实现了接口BeanDefinitionRegistryPostProcessor,Spring容器在初始化的refresh方法中,会先执行接口BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,最后统一执行接口BeanFactoryPostProcessor中的postProcessBeanFactory方法。

因为ConfigurationClassPostProcessor还实现了接口PriorityOrdered和Ordered,所以在具体执行时,还会根据优先级进行排序。

初探方法postProcessBeanDefinitionRegistry

Spring容器初始化,首先会执行ConfigurationClassPostProcessor中,接口BeanDefinitionRegistryPostProcessor中的方法postProcessBeanDefinitionRegistry,然后再执行BeanFactoryPostProcessor中的方法postProcessBeanFactory。

@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    //方法最开始先通过System的方法identityHashCode,获取容器registry对应的hash code,接下来再判断一下集合registriesPostProcessed以及集合factoriesPostProcessed中是否存在该hash cod
		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中的逻辑还比较关键,添加了注解@Configuration的类的解析

首先通过方法getBeanDefinitionNames,从Spring容器registry中,获取已经注册到Spring容器的BeanDefinition的名称,然后在for循环中遍历这些BeanDefinition。

可以看到,在for循环的第一个if分支中,如果发现BeanDefinition中不存在属性“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的值时,这就意味着注解配置类的BeanDefinition并没有被处理过。

第一次执行该方法时,默认是没有这个属性值的,所以接下来就会到else if分支中,通过ConfigurationClassUtils的方法checkConfigurationClassCandidate,看下当前的BeanDefinition是否满足候选条件,也就是判断是否属于添加了注解@Component的注解配置类。

	/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    //获取spring已经注册,符合条件的BeanDefinitionHolder
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    //获取已经注册到Spring容器的BeanDefinition的名称,
		String[] candidateNames = registry.getBeanDefinitionNames();
	//然后在for循环中遍历这些BeanDefinition。
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
      //看下当前的BeanDefinition是否满足候选条件,也就是判断是否属于添加了注解@Component的注解配置类。
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}	

如果BeanDefinition中的类名称都为null,或者当前BeanDefinition中配置了工厂方法。

方法checkConfigurationClassCandidate就是通过是否存在@Configuration注解,且该注解中的属性proxyBeanMethods的值是否为默认的true,以此来判断当前的BeanDefinition是符合条件的注解配置类。

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

	//从注解元数据中,尝试获取注解@Configuration的信息,并将信息封装在config中。
		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
  //如果config不为空,且注解@Configuration中的属性proxyBeanMethods值为true,此时第一个if分支条件成立,此时在BeanDefinition中将属性“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的值设置为CONFIGURATION_CLASS_FULL,也就是“full”
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

//通过getOrder方法,获取注解元数据中注解@Order的属性值,并设置到BeanDefinition中。
  Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

		return true;
	}

继续processCOnfigBeanDefinitions

接下来集合configCandidates通过sort方法,对集合内的元素进行一个排序,排序的规则也比较简单,就是通过解析到的注解@Order中的属性值来排序,值越小优先级越大。

然后判断registry是否是SingletonBeanRegistry的实例,默认registry是SingletonBeanRegistry的实例,接下来会初始化一个组件BeanNameGenerator,主要负责生成bean的名称,这里默认获取到的generator为null。


	// 集合configCandidates通过sort方法,对集合内的元素进行一个排序,排序的规则也比较简单,就是通过解析到的注解@Order中的属性值来排序,值越小优先级越大。
		configCandidates.sort((bd1, bd2) -> {
			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
			return Integer.compare(i1, i2);
		});

		// 判断registry是否是SingletonBeanRegistry的实例,默认registry是SingletonBeanRegistry的实例,接下来会初始化一个组件BeanNameGenerator,主要负责生成bean的名称,这里默认获取到的generator为null。
		SingletonBeanRegistry sbr = null;
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
						AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

初始化了一个组件ConfigurationClassParser,根据名称我们可以知道,它应该就是解析注解配置类的一个解析器

		// 初始化了一个组件ConfigurationClassParser,、解析注解配置类的一个解析器
		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();
		

方法parser中的逻辑还是挺清晰的,可以看到在if分支中,分别对注解类型的AnnotatedBeanDefinition、普通类型的AbstractBeanDefinition都有相应的解析逻辑。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
  //方法parser中的逻辑还是挺清晰的,可以看到在if分支中,分别对注解类型的AnnotatedBeanDefinition、普通类型的AbstractBeanDefinition都有相应的解析逻辑。
		for (BeanDefinitionHolder holder : configCandidates) {
			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());
				}
			}
		}
		this.deferredImportSelectorHandler.process();
	}

//在方法parse中,首先将注解元数据metadata以及bean的名称,都封装到了组件ConfigurationClass中,我们继续跟进看下:
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

执行方法doProcessConfigurationClass,对注解配置类信息configClass进行深度的解析,然后将解析完毕的configClass缓存到configurationClasses中。

可以看到,方法doProcessConfigurationClass中的逻辑,主要就是对注解配置类中的各种其他注解进行解析,毕竟添加了注解@Configuration的类当中,可能还会添加其他的注解。

在方法doProcessConfigurationClass中会解析注解@PropertySource、@ComponentScans、@ImportResource等,。

但是,我们可以发现最终会将解析到的注解信息,都设置到configClass中了,而且,我们在上个方法也看到了,解析得到的configClass最终会被添加到configurationClasses中。

@Nullable
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

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

		// No superclass -> processing is complete
		return null;
	}
解析添加了注解@Bean的方法

接下来会获取解析器parser中configurationClasses中的元素,并放到集合configClasses中。

同时还初始化了一个组件ConfigurationClassBeanDefinitionReader来解析它们,可以看到,接下来调用的是reader中的方法loadBeanDefinitions。

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());
			}
			this.reader.loadBeanDefinitions(configClasses);

在方法loadBeanDefinitionsForConfigurationClass中,果然开始处理注解配置类中的每个方法,我们再到方法loadBeanDefinitionsForBeanMethod中看下:

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

		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
    //比那里注解配置类的每个方法
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

开始解析方法上的注解@Bean,然后为方法创建一个ConfigurationClassBeanDefinition类型的BeanDefinition。

接下来就是为BeanDefinition填充各种各样的属性信息,这些属性大部分我们应该都很熟悉了,最后通过registry将BeanDefinition注入到Spring容器中。

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
		ConfigurationClass configClass = beanMethod.getConfigurationClass();
		MethodMetadata metadata = beanMethod.getMetadata();
		String methodName = metadata.getMethodName();

  	//获取方法上的Bean
  	AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
		Assert.state(bean != null, "No @Bean annotation attributes");

		//创建ConfigurationClassBeanDefinition
		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

		
		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
	}

接下来在if分支的逻辑中,如果发现当前Spring容器中注册的BeanDefinition数量,大于candidateNames中的数量,candidateNames其实就是当前方法刚执行时,Spring容器中注册BeanDefinition的数量。

现在,我们解析了注解配置类之后,又为每个添加了注解@Bean的方法封装了BeanDefinition,此时Spring容器中的BeanDefinition数量,肯定是大于之前容器中BeanDefinition的数量啊,所以,接下来我们看下if分支中的逻辑

可以看到,接下来会将已经解析完毕的configClasses,添加到集合alreadyParsedClasses中,然后在for循环中遍历集合newCandidateNames,也就是当前Spring容器中已经注册的那些BeanDefinition的名称。

目的也是很简单,就是想要看下注解@Bean对应的这些方法解析到的BeanDefinition,是否也存在添加了注解@Configuration的注解配置类,如果存在的话,也是需要添加到集合candidates中,准备开始下一轮while循环的解析的,保证不放过任何一个符合候选条件的注解配置类。

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;
			}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值