首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >interface注入及报错分析

interface注入及报错分析

作者头像
LiosWong
发布于 2018-10-29 09:41:38
发布于 2018-10-29 09:41:38
93700
代码可运行
举报
文章被收录于专栏:后端沉思录后端沉思录
运行总次数:0
代码可运行

一个小case

上面错误原因我想大家开发中都遇到过,大致错误原因是注入bean时,spring找到2个实例userServiceImplTest、userServiceImpl,无法确认到底使用哪个。问题出在这,原因是什么呢,在说明前,看下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
public class OkController {
@Autowired
UserService userService;
@ResponseBody
@GetMapping(value = "/ok")
public String ok(){
    UserInfoEntity userInfoEntity = userService.selectByTel("lioswang");
}
//此时项目中UserService的实现类只有UserServiceImpl
@Service
public class UserServiceImpl implements UserService{
    @Override
    public UserInfoEntity selectByTel(String tel) {
        return  null;
    }
}

在OkController中为什么可以直接注入接口,当项目启动时,调用了UserServiceImpl类中的selectByTel方法,由于在OkController中引用了UserService,所以锁定在OkController初始化时Spring到底干了些什么,根据之前源码分析的经验,在 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法中打上条件断点,首先看方法 org.springframework.beans.factory.support.AbstractBeanFactory#createBean,调用了方法org.springframework.beans.factory.support.AbstractBeanFactory#createBean,继续跟进方法 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean, 再跟进去方法 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean,继续跟进去 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues,在方法中找到OkController注入的元数据UserService,调用了 org.springframework.beans.factory.annotation.InjectionMetadata#inject,跟进去 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject,继续跟 org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency,其中代码片段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//获取接口的依赖
result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);

调用了 org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency,该方法的代码片段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

继续跟 org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates,其中获取UserService所有的实现类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//获取到UserService的实现类
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());
//获取到实现类后,并初始化,保存在Map<String, Object>
result.put(candidateName, getBean(candidateName));
代码语言:javascript
代码运行次数:0
运行
复制

再看 org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中代码片段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
    if (descriptor.isRequired()) {
        raiseNoSuchBeanDefinitionException(type, "", descriptor);
    }
    return null;
}
//获取匹配到的bean数大于1时的逻辑处理
if (matchingBeans.size() > 1) {
    String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    if (primaryBeanName == null) {
        throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
    }
    if (autowiredBeanNames != null) {
        autowiredBeanNames.add(primaryBeanName);
    }
    return matchingBeans.get(primaryBeanName);
}
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
    autowiredBeanNames.add(entry.getKey());
}
return entry.getValue();
}

由于目前项目中UserService的实现类只有UserServiceImpl,所以最终获取到的只有一个。 再回到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject中,已经找到UserService的实现类,所以执行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
代码语言:javascript
代码运行次数:0
运行
复制

即把UserServiceImpl的实例设置到属性UserService中。 所以当再OkController中调用UserService的selectByTel方法,其实调用的是UserServiceImpl的selectByTel方法。

报错

上面分析那么多,其实就是为了说明我们注入接口时,为什么会调用实现类的方法。为了报错,很简单,再写一个类实现UserService接口即可,OkController中不需要修改,其实由上面的分析知道,报错的就是上面的这段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (matchingBeans.size() > 1) {
    String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    if (primaryBeanName == null) {
        throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
    }
    if (autowiredBeanNames != null) {
        autowiredBeanNames.add(primaryBeanName);
    }
    return matchingBeans.get(primaryBeanName);
}

也就是在UserService的实现类中找到多个bean实例,这个明显是错误的,

错误解决

如何解决这个问题呢,很简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
@Qualifier("userServiceImpl")
UserService userService;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Resource(name = "userServiceImpl")
UserService userService;

因为在 org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法中的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (String candidateName : candidateNames) {
    if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
        result.put(candidateName, getBean(candidateName));
    }
}

会根据注解过滤bean,所以加上上面的注解后会解决错误,具体代码就不分析了,感兴趣的同学可打断点调试。

思考拓展

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
public class OkController {
@Autowired
Map<String,UserService> userServiceMap;
@ResponseBody
@GetMapping(value = "/ok")
public String ok(){
   ...
}

若OkController中代码修改如上,项目启动后,发现没有报错,而且userServiceMap中有两个key-value元素,无疑是UserServiceImpl、UserServiceImplTest,我想原因不难看出, org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates返回了Map,其值和userServiceMap相同,不难看出spring功能非常强大。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
搭建随机图片API
摸索了一下自己又搭建了一个随机图片API (美女) ,PHP的随机图片API搭建方法有两种
qiangzai
2021/12/21
5.5K1
站点防红代码
复制以下代码到主题文件的header.php中,index.php中,保存就可以看到效果了! 如果你只想让首页变成这样,只加载index.php内即可。 代码:
meowrain
2021/04/22
2.1K0
站点防红代码
如何搭建一个简易的随机图片API
新建一个php文件 如api.php(这个api可以为任何字母数字 为你打开的网址后缀)
小狐狸说事
2022/11/17
1.8K0
如何搭建一个简易的随机图片API
超实用PHP判断页面访问是移动端还是PC设备?
最近想要找一段代码判断移动pc,以便隐藏显示不同内容,结果找网上的好多都是同一个版本比较复杂不太好用,其实WordPress本身自带了个亿函数判断移动还是pc设备的,wp_is_mobile();这个是可以直接使用的;
wordpress建站吧
2019/12/19
2K0
WordPress程序如何判断手机端和pc端?
通过程序来识别访问网页的设备是PC或者移动以便于我们自适应的显示不用的样式,但是一般php有判断的方法,js也有,css也可以判断,今天就分享一个WordPress程序自带的内置函数:wp_is_mobile(),大概的意思就是判断用户是否采用移动设备访问网站,返回的结果是一个布尔函数,返回值为true或false。
wordpress建站吧
2019/10/15
2.8K0
PHP搭建一个api随机图教程
$img_array = glob("img/*.{webp,gif,jpg,png}",GLOB_BRACE);
时间的距离
2024/06/15
5430
分享一个视频解析接口
在分享一个qq跳转防红代码,将代码防止在全局文件中,如:头部文件,尾部维文件等。苹果cms:找到根目录下的index.php文件,将这段代码防止在文件的最顶部。
用户1337665
2019/08/18
6.9K0
搭建随机图片API
搭建好BLOG和TECH,我一直觉得里面的随机图片很好看。依靠baidu我大概了解到这个是依靠API实现的。于是乎,我自己也想整一个(毕竟图片在自己手上用的放心捏)。再也不怕别人的API接口失效力!
夜梦星尘
2024/08/20
3530
搭建随机图片API
如何搭建自己的API接口(图片篇)
折腾前端少不了的就是会调用一些第三方接口,往往碰到接口挂掉的时间就非常难受,这就是图省事自己不写代码的后果,一旦图片都失效,网站打开速度慢不说,图片的背景还不显示。 于是百度研究制作图片API的方法,跟着小熊动手制作一个属于自己的图片API吧
小何.
2023/03/03
3.8K18
如何搭建自己的API接口(图片篇)
制作随机图片/视频api(本地/外链)
新建一个PHP文件(名字可以自己取), images.txt文档文件,把外链图片地址放进去
Eternity
2022/08/24
2.7K0
Z-BlogPHP 主题制作技巧,随机获得文章中的图片
调用其它数据参考文章标签,文章标签。 注意:此处需要使用foreach循环中as后面变量名,如案列中使用的 $related ,如需调用标题则用 {$related.Title} ,而并非是 {$article.Title}。
德顺
2019/11/12
5560
php实现随机图像功能
近期做个小开发需要用到随机图像。 直接上代码 方法一 <?php $img_array = glob('images/*.{gif,jpg,png,jpeg,webp,bmp}', GLOB_
青阳
2021/03/31
1.7K0
PHP之随机图API
去年三月四月发布了Q青之家APIV1.0 V1.1百度均可搜到,这期来讲讲随机图。
用户2590762
2021/08/11
1.9K0
续:WordPress 文章图片部署真正的懒加载(Lazy Load)
几天前分享了《WordPress 中部署真正的懒加载(Lazy Load)》一文,教会了大家在WordPress 中的两个地方部署懒加载:缩略图、头像图片。今天则深入一点,是对在文章页的图片部署懒加载(Lazy Load)。 文章页的图片部署懒加载,手动修改代码? 熟悉懒加载(Lazy Load)插件用法的朋友都知道,可知要实现懒加载,图片img 标签必须如下面那样写: <img class="lazy" src="img/grey.gif" data-original="img/example.jpg"
Jeff
2018/01/19
1.4K0
PHP判断用户是否是移动端访问的办法
有的网站需要判断用户是否使用移动端访问,如果是则跳转wap网页,否则不跳转,实现判断的方法也很简单,一个函数就搞定了。
Z4
2020/04/21
2.8K0
PHP丨如何判断访客PC及PE端?(图片API配合使用)
手机双端比如自适应网站的站长来说还是比较有用的,使用它不但单纯的可以完美解决如何用PHP判断用户通过电脑端还是手机端访问网站,还可以实现一些实际的网站问题,比如说通过判断用户通过电脑还是手机来引导用户的访问页面,或者引导用户去显示一些不同的信息等。
V站CEO-西顾
2018/06/08
9940
PHP丨如何判断访客PC及PE端?(图片API配合使用)
PHP 学习筆記[1] —— ThinkPHP 公共函数整理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/details/53733707
泥豆芽儿 MT
2018/09/11
9190
php判断访问者是否手机客户端实例
上面的方法也存在一些小问题,这里我根据自己的经验来告诉大我们可以使用屏幕宽度来实现再加机器类型了,因为有时HTTP_USER_AGENT信息在我们上面并未定义过了,不过上面实现几乎兼容了主流手机了。 我们还可以使用js
用户8099761
2023/05/10
2.1K0
利用PHP实现404页自动更换图片 附api
只拿来做404页背景图或许有点太单调了,实际上也可以用来写广告位banner随机图片,单个图标ico随机(效果会太花里胡哨)。甚至文章缩略图也能指定图片随机显示。等等等等...只要是能插图片的地方,都可以用随机图片API。
AlexTao
2019/12/13
1.6K1
利用PHP实现404页自动更换图片 附api
PHP简单判断手机设备的方法
PHP 简单判断手机设备的方法,涉及 php 基于服务器预定义变量进行判断操作的相关技巧,现在移动互联网越来越发到,很多的网站都普及了手机端浏览,为了更好的让网页在手机端显示,我们都选择了使用 CSS 媒体查询制作响应式模版,但这也有弊端,例如某些网站的结构是 CMS 类型的,太多的内容要显示,而使用 CSS 媒体查询设计响应式,只会隐藏但还是加载了,为了让手机端更快速的显示出内容,我们可以使用这个 PHP 判断手机设备代码,使用这个代码可以很方便的显示或不显示自定义的内容。 这是 PHP 判断手机设备函数
沈唁
2018/05/24
1.4K0
相关推荐
搭建随机图片API
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档