springboot多模块项目中使用RedisTemplate的一次坑(注入了两个实例导致项目报错)

问题描述

在一个springboot多模块项目中,common模块中有一个RedisTemplateConfig类,指定redis使用的序列化方式,代码如下:
最开始代码:

@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        redisTemplate.setValueSerializer(genericToStringSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }
}

同时在该模块下有一个操作redis的util类,注入了redisTemplate,代码如下:
RedisUtils代码

@Component
@Slf4j
public class RedisUtils {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    public RedisTemplate<Object, Object> getRedisTemplate() {
        return this.redisTemplate;
    }

有两个模块(pc端和console端)都依赖了common模块,pc端能正常启动,但console模块启动不了,报redisTemplate注入不了,不存在,错误如下:

Field redisTemplate in com.baidu.hcm.utils.RedisUtils required a bean of type 'org.springframework.data.redis.core.RedisTemplate' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)

The following candidates were found but could not be injected:
    - Bean method 'redisTemplate' in 'RedisAutoConfiguration' not loaded because @ConditionalOnMissingBean (names: redisTemplate; SearchStrategy: all) found beans named redisTemplate


Action:

Consider revisiting the entries above or defining a bean of type 'org.springframework.data.redis.core.RedisTemplate' in your configuration.

第一眼看到这个报错觉得很奇怪,明明在RedisTemplateConfig中注入了redisTemplate,而且pc端也能正常启动使用,为什么console端却不行。。。
第一次修改代码

@Configuration
public class RedisTemplateConfig {

    @Bean(name = "xxxRedisTemplate"))
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        redisTemplate.setValueSerializer(genericToStringSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }
}

修改后不能注入的问题解决了,pc和console能正常启动了,简单的验证了下pc端也能正常使用redis,以为没问题了,但是过了两天又出现另外一个新问题,之前RedisUtils中有一个自增的方法报:RedisCommandExecutionException: ERR value is not an integer or out of range

public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

当时修改没有全面验证redisUtil中的方法,导致过了两天这个自增的方法当时没有验证到。

解决

修改后代码

@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        redisTemplate.setValueSerializer(genericToStringSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }
}

RedisUtils代码修改:

@Component
@Slf4j
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public RedisTemplate<String, Object> getRedisTemplate() {
        return this.redisTemplate;
    }

去掉RedisTemplateConfig 中为RedisTemplate指定的名字,将RedisUtils 中的RedisTemplate泛型改为RedisTemplate<String, Object>,与RedisTemplateConfig 中的类型保持一致,从而保证注入容器中的类型跟我们需要从容器中取的类型一致。

原因

springboot自动配置类RedisAutoConfiguration中有一段代码:

public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

第一次console启动报错
在没修改代码之前,自己的RedisTemplateConfig配置类为容器注入了一个名为“redisTemplate”的RedisTemplate实例(类型RedisTemplate<String, Object>),springboot的自动配置类RedisAutoConfiguration中的自动注入未被调用,而RedisUtils中要求一个类型为RedisTemplate<Object, Object>的RedisTemplate实例,容器中不存在(这时需要值得注意的一个点:RedisTemplate<Object, Object>、RedisTemplate<String, Object>这两种类型并不相等),所以报错'org.springframework.data.redis.core.RedisTemplate' that could not be found.

第二次自增报错
RedisTemplateConfig 中手动为我们自己的redisTemplate指定名字后,我们主动的给容器中注入了一个名字为xxxRedisTemplate的RedisTemplate实例(类型RedisTemplate<String, Object>),springboot的自动配置类RedisAutoConfiguration 没有找到名字为"redisTemplate"的RedisTemplate实例,于是也往容器中注入了一个名字为redisTemplate的RedisTemplate实例(类型RedisTemplate<Object, Object>),这时容器中有两个RedisTemplate的实例:RedisTemplate<String, Object>、RedisTemplate<Object, Object>,而RedisUtils中使用的是RedisTemplate<Object, Object>,它使用的是默认的序列化器(org.springframework.data.redis.serializer.JdkSerializationRedisSerializer),而不是我们指定的StringRedisSerializer序列化器,可以参考详解

待解决

在第一次修改代码之前,启动pc端时RedisUtils也要求一个RedisTemplate<Object, Object>,启动没报错,通过debug程序发现RedisUtils中的RedisTemplate实例就是我们RedisTemplateConfig 中指定的,但为什么同样的启动console却报错了。
经过检查代码发现:pc端的启动类上多了一个注解@EnableXXXClient(内部一个组件),pc端引用了该组件,在组件中有一个RedisConfiguration类:

@Bean(name = "xxxRedisTemplate")
 public RedisTemplate redisTemplate() {
  //省略部分代码
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();

        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class) {
            // 省略部分代码
        };
        // 省略部分代码
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashValueSerializer(serializer);
        return redisTemplate;
 }

该配置类向容器中添加了一个RedisTemplate<Object, Object>类型的RedisTemplate 实例,故pc端启动时RedisUtils中能注入,而console没有依赖该组件,故注入报错。
下图为debug调试证明:
自己注入的RedisTemplate实例


图1

通过图1可以明显看到在debug模式启动时我们自己注入的RedisTemplate实例在设置的值
组件注入的RedisTemplate实例


图2

图3

图2是在debug模式下查看RedisUtils的get方法,此时redisTemplate对象中的值跟图3注入RedisTemplate实例时设置的值一样,验证了上面我们的结论。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • SpringBoot-Redis 入门 Redis 的数据类型 String 字符串 string 是 redis...
    寒沧阅读 1,306评论 0 0
  • 当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializ...
    月生89阅读 42,570评论 0 1
  • 一:简介Redis是一个基于键值对的开源内存数据存储,Spring对Redis的支持是通过Spring Data ...
    爱的旋转体阅读 1,241评论 0 0
  • 本文是本人在SpringBoot中使用Redis的笔记,若有误还请指正。本文目录1、配置2、Redis读写字符串3...
    树蜂阅读 209评论 0 0
  • @[toc] 一、缓存 1、缓存使用 为了系统性能的提升,我们一般都会将部分数据放入缓存中,加速访问。而db承担数...
    runewbie阅读 809评论 0 1