Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义

Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义

  • ConfigurationClassParser的validate
    • ConfigurationClass的validate
      • BeanMethod的validate
  • ConfigurationClassBeanDefinitionReader加载bean定义
    • 每一个ConfigurationClass基本的加载流程
    • loadBeanDefinitionsForConfigurationClass
      • registerBeanDefinitionForImportedConfigurationClass被import进来的
      • loadBeanDefinitionsForBeanMethod
      • loadBeanDefinitionsFromImportedResources
      • loadBeanDefinitionsFromRegistrars扩展点
      • 扩展实战

ConfigurationClassParser的validate

为什么要讲这个,因为这里涉及到一个CGLIB动态代理的条件问题,GCLIB的代理原理是继承目标类,覆盖相应的方法,可能还有一些方法拦截器,所以对类和方法是有要求的。
所以他会进行配置类的遍历验证

	public void validate() {
		for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
			configClass.validate(this.problemReporter);
		}
	}

ConfigurationClass的validate

具体验证是交给ConfigurationClass自身方法的。这里就是我们所说的Configuration注解的重要作用,可以进行类代理的增强。如果是Configuration注解的,默认proxyBeanMethods=true,后面就会进行GCLIB代理增强,所以这里要验证下元数据里有没有final修饰类,因为final修饰类是不可以被继承的,同时还要验证下所有的bean方法是可以被覆盖的。

	//进行验证,如果是proxyBeanMethods=true,默认用GCLIB做代理,是继承的,所以不可以是final类
	public void validate(ProblemReporter problemReporter) {
		// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
		Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
		if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {//要GCLIB代理
			if (this.metadata.isFinal()) {//配置类不可以是final的
				problemReporter.error(new FinalConfigurationProblem());
			}
			for (BeanMethod beanMethod : this.beanMethods) {
				beanMethod.validate(problemReporter);//验证方法可覆盖
			}
		}
	}

BeanMethod的validate

这里就是验证bean注解的方法,如果是静态的,就没关系,立即返回,否则的话要判断是否可以覆盖。

@Override
	public void validate(ProblemReporter problemReporter) {
		if (getMetadata().isStatic()) {//静态方法属于类的,不管
			// static @Bean methods have no constraints to validate -> return immediately
			return;
		}

		if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
			if (!getMetadata().isOverridable()) {//不能覆盖的话,也报错,因为不能GCLIB覆盖啦
				// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
				problemReporter.error(new NonOverridableMethodError());
			}
		}
	}
	//可以覆盖的条件,非静态且非final且不是私有的
	@Override
	public boolean isOverridable() {
		return !isStatic() && !isFinal() && !isPrivate();
	}

ConfigurationClassBeanDefinitionReader加载bean定义

每一个ConfigurationClass基本的加载流程

Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义_第1张图片

前面我们把配置类解析好了,放进ConfigurationClass集合里,但是还没有进行bean定义,所以后面就是进行bean定义了,遍历所有的ConfigurationClass,加载bean定义。

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

loadBeanDefinitionsForConfigurationClass

ConfigurationClass中获取bean名字,如果发现是条件过滤的且注册器里有相关bean定义,就要删除。然后处理是否是被import进来的,处理bean注解方法的,处理ImportedResourcesImportBeanDefinitionRegistrar接口实现类的。

	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()) {//是被import进来的
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {//处理bean注解方法
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());//从ImportedResources加载bean定义
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());//从Registrars接口加载bean定义
	}

registerBeanDefinitionForImportedConfigurationClass被import进来的

如果是被import进来的,直接将自己就封装成AnnotatedGenericBeanDefinition ,注册到注册器registry中。

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);
		AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
		configClass.setBeanName(configBeanName);

		if (logger.isTraceEnabled()) {
			logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
		}
	}

loadBeanDefinitionsForBeanMethod

看起来这个方法好像很长,其实就是将bean注解的方法元数据取出来分析,分析bean注解,有没有别名啊,有没有跟xml配置冲突啊,封装成一个ConfigurationClassBeanDefinition,然后设置工厂方法名,获取bean注解的属性,设置初始化方法,销毁方法,是否自动装配,是否需要代理,是JDK动态代理,还是CGLIB动态代理,如果用了动态代理,除了目标bean定义会注册以外,代理bean也会被注册,具体源码暂时不深入。

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;
		}
		//获取bean注解的属性
		AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
		Assert.state(bean != null, "No @Bean annotation attributes");

		// Consider name and any aliases获取别名
		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);
		}
		//是否存在同名bean定义就不处理了,比如处理器扩展的,XML定义的
		// 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,表示是来自配置类里的bean定义
		ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
		beanDef.setResource(configClass.getResource());//设置来源的类
		beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));//设置元数据

		if (metadata.isStatic()) {//静态的,设置BeanClass
			// static @Bean method
			if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
				beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
			}
			else {
				beanDef.setBeanClassName(configClass.getMetadata().getClassName());//设置配置类全限定名
			}
			beanDef.setUniqueFactoryMethodName(methodName);
		}
		else {
			// instance @Bean method 实例的,设置工厂名,也就是配置类名
			beanDef.setFactoryBeanName(configClass.getBeanName());
			beanDef.setUniqueFactoryMethodName(methodName);//设置工厂方法的名字,也就是方法名
		}
		//如果方法元数据是标准方法元数据的话,就设置解析的工厂方法
		if (metadata instanceof StandardMethodMetadata) {
			beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
		}
		//设置自定装配模式默认是构造器
		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()) {//如果是自动装配,也就是BY_NAME 或者 BY_TYPE,再设置了一次自动装配模式
			beanDef.setAutowireMode(autowire.value());
		}
		//自动装配候选,默认是true
		boolean autowireCandidate = bean.getBoolean("autowireCandidate");
		if (!autowireCandidate) {
			beanDef.setAutowireCandidate(false);
		}
		//初始化方法 @PostConstruct 和 @PreDestroy  或者XML 或者 InitializingBean和 DisposableBean接口
		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) {//如果范围不是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));
		}
		this.registry.registerBeanDefinition(beanName, beanDefToRegister);
	}

loadBeanDefinitionsFromImportedResources

这个就是加载ImportedResourcesxml的,有两种加载类GroovyBeanDefinitionReaderXmlBeanDefinitionReader,然后用他们去加载xml,这里面就是各种xml解析,就暂时不深入了。

private void loadBeanDefinitionsFromImportedResources(
			Map<String, Class<? extends BeanDefinitionReader>> importedResources) {

		Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();

		importedResources.forEach((resource, readerClass) -> {
			// Default reader selection necessary?
			if (BeanDefinitionReader.class == readerClass) {
				if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
					// When clearly asking for Groovy, that's what they'll get...
					readerClass = GroovyBeanDefinitionReader.class;
				}
				else {
					// Primarily ".xml" files but for any other extension as well
					readerClass = XmlBeanDefinitionReader.class;
				}
			}

			BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
			if (reader == null) {
				try {
					// Instantiate the specified BeanDefinitionReader
					reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
					// Delegate the current ResourceLoader to it if possible
					if (reader instanceof AbstractBeanDefinitionReader) {
						AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
						abdr.setResourceLoader(this.resourceLoader);
						abdr.setEnvironment(this.environment);
					}
					readerInstanceCache.put(readerClass, reader);
				}
				catch (Throwable ex) {
					throw new IllegalStateException(
							"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
				}
			}

			// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
			reader.loadBeanDefinitions(resource);
		});
	}

loadBeanDefinitionsFromRegistrars扩展点

这个是处理ImportBeanDefinitionRegistrar类型的import注解。具体就是去遍历被import进来的ImportBeanDefinitionRegistrarregisterBeanDefinitions自定义方法啦,这里也是一个扩展点,可以自己注册bean定义。

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

扩展实战

我们举个例子:
Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义_第2张图片
Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义_第3张图片
在这里插入图片描述
运行后回调到这里:
Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义_第4张图片
扩展后注册进去了。
Spring 5.x 源码之旅八ConfigurationClassParser验证配置类以及加载bean定义_第5张图片
至此对配置类的bean定义加载完成。主要步骤就是如果是被import进来的,就把自己注册进去,如果有bean注解方法的,把bean注解的方法作为bean定义注册进去,然后处理xml里的bean定义和ImportBeanDefinitionRegistrar实现类的扩展bean定义。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

你可能感兴趣的:(Spring,5.x,源码之旅,加载bean定义,Spring源码,Spring源码解析,Spring精通源码,Spring5.x源码)