背景:

扩展RedisTemplate的实现的时候写了这样一段代码:

public class BusinessRedisTemplate extends RedisTemplate<String, String> {
        private final String prefix = "business";
        
        public BusinessRedisTemplate (RedisConnectionFactory connectionFactory) {
            setConnectionFactory(connectionFactory);
        }
        
        public BusinessRedisTemplate (RedisConnectionFactory connectionFactory) {
            setConnectionFactory(connectionFactory);
        }
    }

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

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new BusinessRedisTemplate (redisConnectionFactory);
}

然后在Service中注入它:

@Autowired
private BusinessRedisTemplate  redisTemplate;

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

a bean of type 'com.business.BusinessRedisTemplate ' that could not be found.

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

@Autowired
private RedisTemplate redisTemplate;

而且我debug了确实找到就是BusinessRedisTemplate 对象,那为什么上面那么写就找不到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对象时会针对属性进行依赖注入,会根据BusinessRedisTemplate 类型去Spring容器找Bean对象,不过这时Spring容器中是没有BusinessRedisTemplate 类型的Bean对象的,所以Spring会去找BusinessRedisTemplate 类型的Bean定义,那为什么找不到BusinessRedisTemplate 类型的Bean定义呢?我们再来看看我们定义Bean的方式:

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new BusinessRedisTemplate (redisConnectionFactory);
}

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

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

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

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

@Autowired
private BusinessRedisTemplate redisTemplate;

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

而如果改成:

@Autowired
private RedisTemplate redisTemplate;

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

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

@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new BusinessRedisTemplate (redisConnectionFactory);
}

改为:

@Bean
public BusinessRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
	return new BusinessRedisTemplate (redisConnectionFactory);
}

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

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