【Spring编程常见错误50例】03.依赖注入常见错误-上

1.多个实现类 如何匹配

在实际的开发中,我们会使用@Autowired 注解进行依赖注入对应的bean,但是如果我们依赖的是一个接口,有对应多个实现的话,就会出现异常。

java 复制代码
@RestController
public class DbController {

    @Autowired
    private DbService dbService;

    @RequestMapping(path = "/hiDb",method = RequestMethod.GET)
    public String hiScope() {
        dbService.db();
        return "";
    }

}
java 复制代码
@Service
public class MySQLDbService implements DbService{

    @Override
    public void db() {
        System.out.println("mysql init");
    }
}

@Service
public class HbaseDbService implements DbService{

    @Override
    public void db() {
        System.out.println("HbaseDB");
    }
}
java 复制代码
Field dbService in com.qxlx.spingboot.controller.DbController required a single bean, but 2 were found:
	- hbaseDbService: defined in file [/Users/qxlx/work/qxlx/qxlx/spring/target/classes/com/qxlx/spingboot/service/HbaseDbService.class]
	- mySQLDbService: defined in file [/Users/qxlx/work/qxlx/qxlx/spring/target/classes/com/qxlx/spingboot/service/MySQLDbService.class]

原理分析

核心源码解析

AutowiredAnnotationBeanPostProcessor

java 复制代码
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	     // 找出依赖的字段和方法 构建元数据 通过递归的方式进行
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		// 完成对方法或者字段的注入 执行注入 将依赖注入到bean
		metadata.inject(bean, beanName, pvs);
		return pvs;
	}
java 复制代码
	if (matchingBeans.size() > 1) {
		// 根据1.优先级@Primay来决策 2.@prority 3.bean名字
		autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
		if (autowiredBeanName == null) {
			// @Autowired是必须注入的, 注解的属性类型并不是可以接受的多个Bean类型
			if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
				return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
			}
			else {
				return null;
			}
		}
	}

其实就是匹配多个条件,都不满足的时候,就抛出异常。因为没有办法决定使用那一个bean

解决方案

第一种 要么就是直接指定。

java 复制代码
    @Autowired
    private DbService mySQLDbService;

第二种指定

java 复制代码
	 @Autowired
	 @Qualifier("mySQLDbService")
	 private DbService dbService;

2.显示依赖bean首字母大小写 demo

但是如果我们故意写错了,回出现异常。

java 复制代码
	 @Autowired
	 @Qualifier("MySQLDbService")
	 private DbService dbService;

异常信息

java 复制代码
No qualifying bean of type 'com.qxlx.spingboot.service.DbService' 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=MySQLDbService)}

具体原因

java 复制代码
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
	// 如果是注解BD
	if (definition instanceof AnnotatedBeanDefinition) {
		// 查看bean是否有有name 有返回 
		String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
		if (StringUtils.hasText(beanName)) {
			// Explicit bean name found.
			return beanName;
		}
	}
	// 设置一个默认的名称
	// Fallback: generate a unique default bean name.
	return buildDefaultBeanName(definition, registry);
}
java 复制代码
protected String buildDefaultBeanName(BeanDefinition definition) {
	// 获取BeanClassName
	String beanClassName = definition.getBeanClassName();
	Assert.state(beanClassName != null, "No bean class name set");
	// 获取类名
	String shortClassName = ClassUtils.getShortName(beanClassName);
	// 生成类名
	return Introspector.decapitalize(shortClassName);
}
java 复制代码
public static String decapitalize(String name) {
	// 基本验证
    if (name == null || name.length() == 0) {
        return name;
    }
    // 如果类名第一个和第二个都是大写 返回原来的类名
    // 比如 如果是MYSQLService 返回的就是MYSQLService
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char[] chars = name.toCharArray();
    //将第一个字母小写
    // 比如MySQLService 返回的就是mySQLService
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

根据上述源码的分析,可以找到,在生成一个类名的时候,其实是有几个原则

1.如果类名提供了,那么按照对应的名称查找。

2.如果类名没有提供,并且前两个字母都是大写,那么默认就是原来的类名

3.类名是第一个字母小写。

掌握了上述原理,就可以轻松应对这些问题了。

3.引用内部类的bean类名

通过上面的案例,可以知道了beanName的规则,但是对于内部类来说。

java 复制代码
@Service
public class MySQLDbService implements DbService{

    @Override
    public void db() {
        System.out.println("mysql init");
    }

    @Service
    public static class MySQLDbServiceImpl implements DbService{
        @Override
        public void db() {
            System.out.println("mysql init");
        }
    }
}
java 复制代码
    @Autowired
    @Qualifier("MySQLDbService.MySQLDbServiceImpl")
    private DbService dbService;

发现出现错误。

java 复制代码
No qualifying bean of type 'com.qxlx.spingboot.service.DbService' 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=MySQLDbService.MySQLDbServiceImpl)}

原理分析

在生成类名的时候,有一个方法

java 复制代码
String shortClassName = ClassUtils.getShortName(beanClassName);

public static String getShortName(String className) {
	Assert.hasLength(className, "Class name must not be empty");
	int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
	int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
	if (nameEndIndex == -1) {
		nameEndIndex = className.length();
	}
	String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
	shortName = shortName.replace(NESTED_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
	return shortName;
}
相关推荐
黄公子学安全28 分钟前
Java的基础概念(一)
java·开发语言·python
qxlxi34 分钟前
【Spring事务】深入浅出Spring事务从原理到源码
spring
程序员一诺1 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
小木_.1 小时前
【Python 图片下载器】一款专门为爬虫制作的图片下载器,多线程下载,速度快,支持续传/图片缩放/图片压缩/图片转换
爬虫·python·学习·分享·批量下载·图片下载器
路在脚下@2 小时前
Spring Boot @Conditional注解
java·spring boot·spring
Jiude2 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试
唐小旭2 小时前
python3.6搭建pytorch环境
人工智能·pytorch·python
m0_548514772 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯2 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯2 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android