明明定义了Bean,为什么就是依赖注入不了,奇了个大怪!

情况是这样的,我最近在扩展RedisTemplate的实现,大概是这样的:

java 复制代码
public class DaduduRedisTemplate extends RedisTemplate<String, String> {

    private final String prefix = "dadudu";

    public DaduduRedisTemplate(RedisConnectionFactory connectionFactory) {
        setConnectionFactory(connectionFactory);
    }

    @Override
    public void convertAndSend(String channel, Object message) {
        message = prefix + message;
        super.convertAndSend(channel, message);
    }
}

DaduduRedisTemplate继承了RedisTemplate,并重写了convertAndSend()方法,逻辑比较简单,统一给消息加个前缀,实际工作中更复杂一点,DaduduRedisTemplate要生效就需要把它定义为Bean,所以有了如下Bean定义代码:

java 复制代码
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new DaduduRedisTemplate(redisConnectionFactory);
}

然后在Service中注入它:

java 复制代码
@Autowired
private DaduduRedisTemplate redisTemplate;

然后启动SpringBoot,竟然报错了:

java 复制代码
a bean of type 'com.dadudu.DaduduRedisTemplate' that could not be found.

说找不到DaduduRedisTemplate这个类型的Bean,可是我明明定义了呀...,为什么会这样?关键是我如果把属性类型改为RedisTemplate就不报错了,也就是这样:

java 复制代码
@Autowired
private RedisTemplate redisTemplate;

而且我debug了确实找到就是DaduduRedisTemplate对象,那为什么上面那么写就找不到Bean呢?情况就是这么个情况,不知道各位大佬想到原因了没,暂时没想到的,那就听我来给大家分析分析。

首先,抛一个问题给大家:Spring在根据属性进行依赖注入时,所需的Bean对象是否已经存在了?

答案是不一定 ,得看Bean的创建顺序,比如顺序是A--->B,A里面依赖了B,就算A和B都是非懒加载的单例Bean,Spring也会按顺序进行创建,那么在创建A时就会进行依赖注入,而这个时候B对象是不存在的,所以A在进行依赖注入时需要判断:Spring容器中有没有B类型的Bean对象,如果没有则判断有没有B类型的Bean定义,如果有Bean定义,那就此时此刻根据Bean定义把B对象创建出来,如果没有,则报上述根据类型找不到Bean的错误

回到我们的场景,其实类似,原因就是在创建Service的Bean对象时会针对属性进行依赖注入,会根据DaduduRedisTemplate类型去Spring容器找Bean对象,不过这时Spring容器中是没有DaduduRedisTemplate类型的Bean对象的,所以Spring会去找DaduduRedisTemplate类型的Bean定义,那为什么找不到DaduduRedisTemplate类型的Bean定义呢?我们再来看看我们定义Bean的方式:

java 复制代码
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new DaduduRedisTemplate(redisConnectionFactory);
}

这个Bean的类型到底是RedisTemplate,还是DaduduRedisTemplate呢?不一样吗?它两不是父子关系吗?

来来来,重点来了,当Spring解析@Bean注解时,也就是在生成Bean定义时,会把方法的返回值类型RedisTemplate当做Bean类型,而不会把方法中真正返回的对象类型当做Bean类型,因为在解析@Bean注解时并不会真正执行该方法,所以这个Bean的类型一开始只能是RedisTemplate,只有真正执行了该方法之后才知道它具体的类型是DaduduRedisTemplate。

所以Service中在进行依赖注入时,只能找到RedisTemplate类型的Bean,而找不到DaduduRedisTemplate类型的Bean,除非!在进行本次依赖注入之前,DaduduRedisTemplate这个Bean对象已经被创建出来了,这样在进行依赖注入的时候,就能根据RedisTemplate类型找到DaduduRedisTemplate这个Bean对象了(根据父类找到子类对象)。

所以,我们再来看一下依赖注入的代码:

java 复制代码
@Autowired
private DaduduRedisTemplate redisTemplate;

如果属性类型是DaduduRedisTemplate,那么就有可能根据DaduduRedisTemplate类型即找不到Bean对象,也找不到Bean定义,从而报错。

而如果改成:

java 复制代码
@Autowired
private RedisTemplate redisTemplate;

就可以了,因为就算根据RedisTemplate类型找不到Bean对象,也能根据RedisTemplate类型找到Bean定义,最终也能根据Bean定义创建出来DaduduRedisTemplate对象完成依赖注入。

当然,最好的方式是改Bean的定义:

java 复制代码
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new DaduduRedisTemplate(redisConnectionFactory);
}

改为:

java 复制代码
@Bean
public DaduduRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new DaduduRedisTemplate(redisConnectionFactory);
}

这样不管是根据DaduduRedisTemplate类型找Bean定义,还是根据RedisTemplate类型找Bean定义,都能找到。

也许有读者会想到,改成@Resource行不行,只能说可能行,也可能不行,因为@Resource会先根据名字找Bean,找到了自然没问题,但是如果找不到仍然会再根据类型找Bean,最终也可能进入本文所分析的场景中。

好啦,如果你学到了,希望能给我一个赞,如果有不理解的,欢迎在评论区留言讨论!

我是大都督周瑜,欢迎关注我的公众号:Hoeller。这是我的个人号,非机构号,已从机构离职,重回一线了。

现在网络上有很多的文章和视频,但是其中大部分都是一些八股文和理论知识,由不同的人翻来覆去,重复写、重复发,这种做法对作者本身是有用的,但是对于大部分读者来说可能是没有意义的,对于读者来说,不管是面试还是实际工作,我相信实战经验才是真正有价值的,所以我现在的分享都来源于我的实际工作,再结合我多年讲课的经验,希望做到把实战经验、理论知识、授课技巧融合起来,让读者能轻轻松松的从我的文章或视频中学到真正有价值的知识。

我希望成为:自媒体圈中技术最好、实战经验最丰富的达人,技术圈中最会分享的架构师。加油!

相关推荐
Tisfy2 小时前
LeetCode 1927.求和游戏:抵消+看最值
java·leetcode·游戏·题解·博弈论
东风破_6 小时前
从输入 juejin.cn 到看到页面:DNS、Nginx、服务器集群与 CDN 到底在做什么?
后端·mysql
东风破_7 小时前
从 0 设计一个博客数据库:用户、头像与文章表应该怎么建?
后端·mysql
大鸡腿同学7 小时前
黄仁勋点透的 AI 真相:能实现你知道但做不到的,却实现不了你不知道的
后端
FfHUCisI7 小时前
Go 编译过程全景
开发语言·后端·golang
FfHUCisI7 小时前
Golang 语法分析与 AST:Parser 与 go/ast
开发语言·后端·golang
秋饼8 小时前
JDK 27 企业级 AI 服务落地实战:G1 默认、紧凑对象头、后量子 TLS 与 JFR 脱敏全解析
java·ai·技术分享·后端开发
卓怡学长8 小时前
w176基于SpringBoot的医院管理系统
java·spring boot·spring·maven·intellij-idea
lemon_sjdk8 小时前
ObjectProperty
java·开发语言·算法
hh9509 小时前
Agent Plan x DeepSeek Harness:Agent 供应链安全与第三方插件审计
java·开发语言·安全·agent plan·adg成都社区·adg社区