今天在一个练习的业务中,需要将验证码根据邮箱名存入缓存,但是将生成的验证码放入缓存后,在SpringBoot项目中的RedisTamplale却始终获取不到存入的验证码。再此记录这个坑!

邮箱发送接口

@ApiOperation(value = "发送邮箱验证码")
    @GetMapping(value = "sendEmail/{email}")
    @CostTime
    @PassToken
    public Result<Object> sendCode(@PathVariable String email) {
        log.info("邮箱码:{}",email);
        String code = redisTemplate.opsForValue().get(email);
        if (!StringUtils.isEmpty(code)) {
            return Result.ok().message(email + ":" + code + "已存在,还未过期");
        }
        code = CodeGeneratorUtil.generateCode(6);
        try {
            boolean b = sendEmailUtil.sendEmail(email, code);
            if (b) {
                redisTemplate.opsForValue().set(email, code,Duration.ofMinutes(1));
                return Result.ok().message("验证码发送成功!");
            }
            return Result.fail();
        } catch (Exception e) {
            throw new MyException(501, "邮箱不正确或为空!");
        }
    }

当前端根据邮箱发送验证码后,验证码会以邮箱名为key来存储验证码。

注册接口

/**
     * 用户注册
     *
     * @param
     * @return
     */
    @ApiOperation(value = "用户注册调用接口")
    @PostMapping(value = "/register")
    @CostTime
    @PassToken
    public Result<Object> register(@RequestBody @Validated RegisterDto registerDto, BindingResult result) {
        if (result.hasErrors()) {
            return Result.fail().message("填写的信息不齐");
        }
        Customer customer = new Customer();
        Customer one = customerService.getOne(new QueryWrapper<Customer>().eq("username", registerDto.getUsername()));
        if (!StringUtils.isEmpty(one)) {
            log.info("没走验证业务?");
            return Result.fail().message("账号已存在,请重新注册");
        }
        //验证邮箱验证码
        String code = registerDto.getCode();
        log.info("前端输入的验证码{}", code);
        String eml = registerDto.getEmail();
        log.info("前端的对象为{},邮箱=》{}",registerDto,eml);
        String s = redisTemplate.opsForValue().get(eml);
        log.info("从redis中获取code->{}",s);
        /*邮箱验证有点问题————————————————————————————————————————————————————————————————————————————*/
        if (Objects.equals(s, code)) {
            log.info("验证码正确{}", code);
            //通过雪花算法设置cid
            customer.setCid(CodeGeneratorUtil.snowflake());
            customer.setCreateTime(LocalDateTime.now());
            customer.setSex(true);
            customer.setAvatar(registerDto.getAvatar());
            customer.setUsername(registerDto.getUsername());
            customer.setEmail(registerDto.getEmail());
            customer.setRoleId(2);
            customer.setBirth(LocalDate.now());
            customer.setPassword(DigestUtil.md5Hex(registerDto.getPassword()));
            boolean user = customerService.save(customer);
            if (user) {
                log.info("账号注册成功?");
                return Result.ok().message("账号注册成功");
            }
            log.info("账号注册失败?");
            return Result.fail().message("账号注册失败");
        } else {
            log.info("验证码错误?{}", s);
            return Result.fail().message("验证码错误,请重新输入");
        }
    }
}

但是在获取value的时候一直为空,并且redis中根据邮箱key是可以获取到value值的。

2023-03-05 17:33:40.904  INFO 5132 --- [nio-8084-exec-2] com.yy.controller.CustomerController     : 前端输入的验证码b65bea
2023-03-05 17:33:40.904  INFO 5132 --- [nio-8084-exec-2] com.yy.controller.CustomerController     : 前端的对象为RegisterDto(username=young, password=123456, sex=null, code=b65bea, cid=0, email=1084708769@qq.com, birth=null, createTime=null, checkPass=123456),邮箱=》111111111@qq.com
2023-03-05 17:33:40.944  INFO 5132 --- [nio-8084-exec-2] com.yy.controller.CustomerController     : 从redis中获取code->null
2023-03-05 17:33:40.944  INFO 5132 --- [nio-8084-exec-2] com.yy.controller.CustomerController     : 验证码错误?null
2023-03-05 17:33:40.944  INFO 5132 --- [nio-8084-exec-2] com.yy.aspect.ServiceAspect              : 调用的是服务层中的com.yy.controller.CustomerController类,其中的的register方法--执行时间为:45毫秒
2023-03-05 17:35:16.390  INFO 5132 --- [nio-8084-exec-4] com.yy.aspect.LogAspect                  : 目前的访问时间为:2023年03月05日 17:35:16--访问接口为:Result com.yy.controller.LoginController.sendCode(String)

通过各种测试,最后找到的原因是因为在依赖注入时,笔者采用的时@Resource注解来注入RedisTamplate的!!!

@Resource
    private RedisTemplate<String, String> redisTemplate;

解决方式

将依赖注入的注解改为@Autowired就可以正常实现获取value的功能了。

/**
     * 不能用Resource,否则获取不到存得值
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@16e4b8a6]
2023-03-05 17:39:26.894  INFO 5132 --- [io-8084-exec-10] com.yy.controller.CustomerController     : 前端输入的验证码4de7d0
2023-03-05 17:39:26.894  INFO 5132 --- [io-8084-exec-10] com.yy.controller.CustomerController     : 前端的对象为RegisterDto(username=young, password=123456, sex=null, code=4de7d0, cid=0, email=495063157@qq.com, birth=null, createTime=null, checkPass=123456),邮箱=》111111111@qq.com
2023-03-05 17:39:26.942  INFO 5132 --- [io-8084-exec-10] com.yy.controller.CustomerController     : 从redis中获取code->4de7d0
2023-03-05 17:39:26.943  INFO 5132 --- [io-8084-exec-10] com.yy.controller.CustomerController     : 验证码正确4de7d0

虽然spring官方不建议使用@Autowired来进行属性注入(造成注入对象与IOC容器的强耦合性),但是不得不说,在有些方面:比如以上这个例子,或者在微服务中注入OpenFeign接口(@Resource注入也可能失败)的时候确实比@Resource好使。

但是具体原因暂时没想明白,有考虑过注入作用的问题,但是显然不是,默认都是单例的。还在找原因ing……,欢迎大佬指正原因~