@Autowired和 @Qualifier 注解

错误如下:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘com.xxx.xxx.xxx.callable.ITaskRunable’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=oAuthShopAccessTokenTask)}

@Autowired注解和@Qualifier注解 通常和 @Resource 注解一同比较。那我们再来回顾一下。

@Autowired 是基于Type进行注入的,即基于Bean的类型进行注入的。如果相同的Bean类型存在两个,比如 按接口类型进入注入,但是存在多个实现类,则会出现异常。此时我们可以使用@Qualifier注解进行配合使用。

@Qualifier就是当使用Autowired注解进行注入时,但是此时又存在多个相同类型的Bean的时候,就可以通过@Qualifier注解进行按名字指定。

@Resource 注解就当于上面两个注解组合使用的效果。@Resource默认是按照执行的名字进行注入的。如果遇到相同的名字则会按类型进行注入,最后如果选择不到一个唯一的,则会抛出异常。

以上@Autowired注解,@Qualifier注解,@Resource注解的作用我们基本都能知道。但是在实际操作中常常会遇到一些其他问题。多个Bean的异常可以好排查一点,但是无法注入的情况,就比较难搞。今天在开发过程中就排查了好久。

如果遇到Spring无法注入Bean的情况,可以从以下几个方向入手排查:

  1. 是否需要注入的类没有被扫描到。
  1. 如果是Spring项目,看配置文件中的 ​​<context: componentScan>​​ 注解的扫描范围,是否扫描到了。
  2. 如果是SpringBoot项目,默认扫描的是​​@SpringBootApplication​​ 类所在的包及其子包,所以看自己的组件是否满足条件。
  1. 按名称注入,名称是否一致。(遇到的坑)
  1. 普通首字母大写的类,如StudentService,使用@Service等加入到Spring容器中,其名字为类型的首字母小写形式。
  2. 如果是两个或两个以上大写字母开头的类,如 OAuthTaskService 类,使用@Service 等注解加入到Spring容器后,其名字是其 ​​类名​​.

我们来看一下源码, 类 ​​AnnotationBeanNameGenerator​

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
protected String buildDefaultBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
return buildDefaultBeanName(definition);
}
protected String buildDefaultBeanName(BeanDefinition definition) {
String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
return Introspector.decapitalize(shortClassName);
}
}

我们查看一下 buildDefaultBeanName 方法,调用了 ​​Introspector.decapitalize(String name)​

public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}

可以看到 ​​if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0)))​​ 这一行,就是 第一个和第二个字母是大写字母,则直接访问原类名。

否则把首字母变为小写,再返回。