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个依赖,如果同时满足两个条件会抛出上面的错误,添加代码
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添加优先级