《spring编程常见错误50例》-学习笔记-Day2

  spirng 依赖注入常见错误(上)

1.过多赠与,无所适从

在使用@Autowired时,我们应该遇到过这种报错:

required a single bean, but 2 were found

意思就是我们只需要一个bean,但是时机缺又提供了多个。

产生这种问题的原因可能很多,可能是业务扩展,需要实现差不多的功能,可能就会新建一个实现某个接口,之前的也存在,也有可能我们自己写的类和依赖包的类冲突了。

分析:

对于这个问题,我们就需要对@Autowired实现的依赖注入的原理有一定的了解,我们先来看看@Autowired发生的位置和核心过程。

当一个bean被构建的时间,核心包括两个步骤

1.执行AbstractAutowireCapableBeanFactory#createBeanInstance,通过构造器反射构造出这个Bean。

2.执行 AbstractAutowireCapableBeanFactory#populate 方法:填充(即设置)这个 Bean

在2中,填充的过程就是执行BeanPostProcessor处理器,关键代码如下

for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					if (filteredPds == null) {
						filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
					}
					pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						return;
					}
				}

细分的话,又可以两个步骤:

1.寻找出所有需要依赖注入的字段和方法。参考:

@Nullable
		private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
			desc.setContainingClass(bean.getClass());
			Set autowiredBeanNames = new LinkedHashSet<>(1);
			Assert.state(beanFactory != null, "No BeanFactory available");
			TypeConverter typeConverter = beanFactory.getTypeConverter();
			Object value;
			try {
//寻找依赖
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
			}
			synchronized (this) {
				if (!this.cached) {
					Object cachedFieldValue = null;
					if (value != null || this.required) {
						cachedFieldValue = desc;
						registerDependentBeans(beanName, autowiredBeanNames);
						if (autowiredBeanNames.size() == 1) {
							String autowiredBeanName = autowiredBeanNames.iterator().next();
							if (beanFactory.containsBean(autowiredBeanName) &&
									beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
								cachedFieldValue = new ShortcutDependencyDescriptor(
										desc, autowiredBeanName, field.getType());
							}
						}
					}
					this.cachedFieldValue = cachedFieldValue;
					this.cached = true;
				}
			}
			return value;
		}

根据依赖信息寻找出依赖并完成注入,以字段注入为例,参考

@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
                //装配依赖
				field.set(bean, value);
			}
		}

如果我们找出2个依赖,如果同时满足两个条件会抛出上面的错误,添加代码

《spring编程常见错误50例》-学习笔记-Day2_第1张图片

1.调用determineAutowireCandidate 方法来选出优先级最高的依赖,但是并没有发现优先级可依据,具体参考如下:DefaultListableBeanFactory#determineAutowireCandidate

@Nullable
	protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
		Class requiredType = descriptor.getDependencyType();
		String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
		if (primaryCandidate != null) {
			return primaryCandidate;
		}
		String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
		if (priorityCandidate != null) {
			return priorityCandidate;
		}
		// Fallback
		for (Map.Entry entry : candidates.entrySet()) {
			String candidateName = entry.getKey();
			Object beanInstance = entry.getValue();
			if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
					matchesBeanName(candidateName, descriptor.getDependencyName())) {
				return candidateName;
			}
		}
		return null;
	}

如上面的代码,优先级的决策时先根据@Primary来决策,其次是@Priority决策,最后是根据Bean名字的严格匹配来决策。如果这些优先级的注解都没有使用,名字也不精准匹配,则返回null,告知无法决策出那个合适。

2.@Autowired 要求是必须注入的(即 required 保持默认值为 true),或者注解的属性类型并不是可以接受多个 Bean 的类型,例如数组、Map、集合。这点可以参考 DefaultListableBeanFactory#indicatesMultipleBeans 的实现:

private boolean indicatesMultipleBeans(Class type) {
		return (type.isArray() || (type.isInterface() &&
				(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
	}

我们看了这些问题,已经知道怎么解决了

给bean添加优先级

你可能感兴趣的:(spring,学习,java,后端,ide)