开发者

关于spring中bean注册的优先级分析

开发者 https://www.devze.com 2024-09-19 10:34 出处:网络 作者: 潭影空人心
目录spring中bean注册的优先级目前已知 XML-bean 优先级最高在 AbstractApplicationContext#refresh优先级排序总结spring中bean注册的优先级
目录
  • spring中bean注册的优先级
    • 目前已知 XML-bean 优先级最高
    • 在 AbstractApplicationContext#refresh
  • 优先级排序
    • 总结

      spring中bean注册的优先级

      在 spring 中,我们知道,常见的定义 bean 的方式共有三种,xml 中 bean 标签定义、component-scan 注解扫描定义、配置类中 @Bean 修饰方法定义。

      这三种定义方式,也分别对应三个 BeanDefinition 的实现类:

      • xml-bean:GenericBeanDefinition
      • component-scan:ScannedGenericBeanDefinition
      • @Bean:ConfigurationClassBeanDefinition

      目前已知 xml-bean 优先级最高

      在 spring 中称之为 top-level,下面来看看,spring 具体是如何实现这一 bean 注册的优先级的。

      在 AbstractApplicationContext#refresh

      执行 obtainFreshBeanFactory 不但会创建一个 BeanFactory,还会对 xml 文件进行解析,加载注册 BeanDefinition。

      • 如果 component-scan 标签先于 xml-bean 标签解析,就会先注册一个 ScannedGenericBeanDefinition,待解析到 xml-bean 时,发现已经存在,就会进行覆盖。
      • 如果 xml-bean 标签先于 component-scan 标签解析,待解析到 component-scan 时,就会进行判断。
      // DefaultListableBeanFactory
      @Override
      public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      		throws BeanDefinitionStoreException {
      
      	Assert.hasText(beanName, "Bean name must not be empty");
      	Assert.notNull(beanDefinition, "BeanDefinition must not be null");
      
      	if (beanDefinition instanceof AbstractBeanDefinition) {
      		try {
      			((AbstractBeanDefinition) beanDefinition).validate();
      		}
      		catch (BeanDefinitionValidationException ex) {
      			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
      					"Validation of bean definition failed", ex);
      		}
      	}
      
      	// 注册时发现已经存在
      	// 除了 allowBeanDefinitionOverriding 为 false 时会抛出异常外,其余FPAaIvXaO都会覆盖
      	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
      	if (existingDefinition != null) {
      		// allowBeanDefinitionOverriding 默认 true,为 false 执行到此直接抛出异常
      		if (!isAllowBeanDefinitionOverriding()) {
      			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      		}
      		// 已存在的角色等级低,会覆盖
      		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
      			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
      			if (logger.isInfoEnabled()) {
      				logger.info("Overriding user-defined bean definition for bean '" + beanName +
      						"' with a framework-generated bean definition: replacing [" +
      						existingDefinition + "] with [" + beanDefinition + "]");
      			}
      		}
      		// equals 方法判定不相等,会覆盖
      		else if (!beanDefinition.equals(existingDefinition)) {
      			if (logger.isDebugEnabled()) {
      				logger.debug("Overriding bean definition for bean '" + beanName +
      						"' with a different definition: replacing [" + existingDefinition +
      						"] with [" + beanDefinition + "]");
      			}
      		}
      		// 其它情况打印覆盖信息,之后覆盖
      		else {
      			if (logger.isTraceEnabled()) {
      				logger.trace("Overriding bean definition for bean '" + beanName +
      						"' with an equivalent definition: replacing [" + existingDefinition +
      						"] with [" + beanDefinition + "]");
      			}
      		}
      		// 覆盖
      		this.beanDefinitionMap.put(beanName, beanDefinition);
      	}
      	else {
      		if (hasBeanCreationStarted()) {
      			// Cannot modify startup-time collection elements anymore (for stable iteration)
      			synchronized (this.beanDefinitionMap) {
      				this.beanDefinitionMap.put(beanName, beanDefinition);
      				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
      				updatedDefinitions.addAll(this.beanDefinitionNames);
      				updatedDefinitions.add(beanName);
      				this.beanDefinitionNames = updatedDefinitions;
      				removeManualSingletonName(beanName);
      			}
      		}
      		else {
      			// 仍在注册阶段
      			this.beanDefinitionMap.put(beanName, beanDefinition);
      			this.beanDefinitionNames.add(beanName);
      			removeManualSingletonName(beanName);
      		}
      		this.frozenBeanDefinitionNames = null;
      	}
      
      	if (existingDefinition != null || containsSingleton(beanName)) {
      		resetBeanDefinition(beanName);
      	}
      	else if (isConfigurationFrozen()) {
      		clearByTypeCache();
      	}
      }

      可以看到,一旦执行到 DefaultListableBeanFactory#registerBeanDefinition,就会对已经存在的 BeanDefinition 进行覆盖,因为 allowBeanDefinitionOverriding 默认为 true。

      所以,如果想进行判断,肯定是在 DefaultListableBeanFactory#registerBeanDefinition 执行之前。

      // ClassPathBeanDefinitionScanner
      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) {
      		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      		for (BeanDefinition candidate : candidates) {
      			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
      			candidate.setScope(scopeMetadata.getScopeName());
      			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
      			if (candidate instanceof AbstractBeanDefinition) {
      				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
      			}
      			if (candidate instanceof AnnotatedBeanDefinition) {
      				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
      			}
      			// 返回 false,并不会进行 candidate 的注册
      			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;
      }
      // ClassPathBeanDefinitionScanner
      protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
      	if (!this.registry.containsBeanDefinition(beanName)) {
      		return true;
      	}
      	BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
      	BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
      	if (originatingDef != null) {
      		existingDef = originatingDef;
      	}
      	if (isCompatible(beanDefinition, existingDef)) {
      		return false;
      	}
      	throw new ConflictingBeanDefinitionException("Annotation-specified bean name 'php" + beanName +
      			"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
      			"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
      }
      
      // ClassPathBeanDefinitionScanner
      protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
      	return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
      			(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // scanned same file twice
      			newDefinition.equals(existingDefinition));  // scanned equivalent class twice
      }

      可以看到,在 component-scan 时,在注册之前,会调用 checkCandidate 进行判断。如果不存在 beanName 对应的 BeanDefinition,直接就返回 true 进行后续的注册,否则委托给 isCompatible 进行判断。如果是先注册的 xml 封装的 GenericBeanDefinition,再遇见http://www.devze.com扫描到的 ScannedGenericBeanDefinition,此时调用 isCompatible,existingDefinition 不是 ScannedGenericBeanDefinition 子类,返回 true,接着 checkCandidate 返回 false,并不会进行 BeanDefinition 的注册。

      接着再来看第三种,@Bean 定义的 BeanDefinition。

      我们知道,在 Abstrac编程客栈tApplicationContext#invokeBeanFactoryPostProcessors 时,会通过注册的 ConfigurationClassPostProcessor 调用 processConfigBeanDefinitions 完成对配置类的解析以及配置类中 BeanDefinition 的注册。而具体加载和注册是通过 ConfigurationClassBeanDefinitionReader 来实现的,对于 @Bean 定义的方法,是通过 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 来处理的。在这个方法中,当确定了 beanName 之后,会调用 isOverriddenByExistingDefinition 来进行是否覆盖的判断。

      // ConfigurationClassBeanDefinitionReader
      // 返回 true,表明被已经存的 BeanDefinition 覆盖了,便不在执行配置类中 Bean 的定义和注册
      protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String beanName) {
      	if (!this.registry.containsBeanDefinition(beanName)) {
      		return false;
      	}
      	BeanDefinition existingBeanDef = this.registry.getBeanDefinition(beanName);
      
      	// 已经存在 ConfigurationClassBeanDefinition,比较工厂名和工厂方法名,只要工厂名相同就会返回 true
      	if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
      		ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
      		if (ccbd.getMetadata().getClassName().equals(
      				beanMethod.getConfigurationClass().getMetadata().getClassName())) {
      			if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
      				ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
      			}
      			return true;
      		}
      		else {
      			return false;
      		}
      	}
      
      	// 通过 component-scan 扫描到的 BeanDefinition 会被 @Bean 定义的 BeanDefinition 覆盖
      	if (existingBeanDef instanceof ScannedGenericBeanDefinition) {
      		return false;
      	}
      
      	// 比较角色,默认 0,即 ROLE_APPLICATION
      	if (existingBeanDef.getRole() > BeanDefinition.ROLE_APPLICATION) {
      		return false;
      	}
      
      	// 执行到此,证明已经存在的 BeanDefinition 是在配置类之前由 xml 定义的
      	// DefaultListableBeanFactory 中 allowBeanDefinitionOverriding 默认 true,如果 allowBeanDefinitionOverriding 为 false,不允许覆盖,出现了两个 BeanDefinition 直接抛出异常
      	if (this.registry instanceof DefaultListableBeanFactory &&
      			!((DefaultListableBeanFactory) this.registry).isAllowBeanDefinitionOverriding()) {
      		throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
      				beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
      	}
      	// 输出被覆盖的日志
      	if (logger.isDebugEnabled()) {
      		logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
      				"already exists. This top-level bean definition is considered as an override.",
      				beanMethod, beanName));
      	}
      	return true;
      }

      只有 isOverriddenByExistingDefinition 返回 false,程序才继续向下执行,会对当前 @Bean 定义的方法创建 ConfigurationClassBeanDefinition,之后执行 registerBeanDefiniition。

      可以看到,如果 existingBeanDef 是 ConfigurationClassBeanDefinition 类型,比较工厂名,相同,返回 true,不用当前 @Beanjs 进行覆盖,否则返回 false,对当前 @Bean 定义的方法创建 ConfigurationClassBeanDefinition,对以前的 ConfigurationClassBeanDefinition 进行覆盖。

      如果 existingBeanDef 是 ScannedGenericBeanDefinition 类型,返回 false,创建 ConfigurationClassBeanDefinition 进行覆盖。

      当对 ConfigurationClassBeanDefinition 和 ScannedGenericBeanDefinition 都判断完成后,剩下的 existingBeanDef 只能是 xml-bean 定义的 GenericBeanDefinition,此时 isOverriddenByExistingDefinition 返回 true,即针对 @Bean 定义 BeanDefinition 的注册会终止,还是采用 existingBeanDef。

      这就是为什么说 spring 中 xml 定义的 BeanDefinition 优先级最高。这样定义的好处就是,当 xml 发生变化后,只需重启项目,不需要编译代码。

      优先级排序

      如下:

      GenericBeanDefinition > ConfigurationClassBeanDefinition > ScannedGenericBeanDefinition

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号