Springboot整合Redis实现缓存


Springboot整合Redis实现缓存

使用原始方式实现查询

application.yml

spring:
  datasource:
    url: jdbc:mysql:///springbootdata?serverTimezone=UTC&useUnicode=true&useSSL=true&characterEncoding=utf8
    username: root
    password: 1234
  jpa:
    show-sql: true

maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

实体类comment

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "t_comment") // 映射的数据库表名
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
    private Integer id;
    private String content;
    private String author;
    @Column(name = "a_id") // 对应的数据库列名
    private Integer aId;
}

mapper层

public interface CommentRepository extends JpaRepository<Comment, Integer> {
    // 根据id修改评论作者
    @Transactional // 更新的操作需要做事务控制
    @Modifying
    @Query("update t_comment c set c.author=?1 where c.id=?2")
    int updateComment(String author, Integer id);
}

service层

@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;
    public Comment findById(int comment_id){
        Optional<Comment> optional = commentRepository.findById(comment_id);
        if (optional.isPresent()){
            return optional.get();
        }
        return null;
    }
    public Comment updateComment(Comment comment){
        commentRepository.updateComment(comment.getAuthor(),comment.getId());
        return comment;
    }
    public void deleteComment(int comment_id){
        commentRepository.deleteById(comment_id);
    }
}

控制层

@Controller
@ResponseBody
public class CommentController {
    @Autowired
    private CommentService commentService;

    @RequestMapping("/get/{id}")
    public Comment findById(@PathVariable("id") int comment_id){
        Comment comment = commentService.findById(comment_id);
        return comment;
    }

    public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
        Comment comment = commentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = commentService.updateComment(comment);
        return updateComment;
    }
    public void deleteComment(@PathVariable("id") int comment_id){
        commentService.deleteComment(comment_id);
    }
}

基于注解实现缓存

在主启动类上添加注解:@EnableCaching

该注解配置在类上,通常都是在主启动类上

在需要加缓存的方法上加一个注解:@Cacheable

@Cacheable(cacheNames = "comment") // 通常用过缓存数据
@CachePut(cacheNames = "comment", key = "#result.id") // 用作更新数据
@CacheEvict(cacheNames = "comment") // 用作删除缓存数据
@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    @Cacheable(cacheNames = "comment", unless = "#result==null")
    public Comment findById(int comment_id) {
        Optional<Comment> optional = commentRepository.findById(comment_id);
        if (optional.isPresent()) {
            return optional.get();
        }
        return null;
    }

    @CachePut(cacheNames = "comment", key = "#result.id")
    public Comment updateComment(Comment comment) {
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        return comment;
    }

    @CacheEvict(cacheNames = "comment")
    public void deleteComment(int comment_id) {
        commentRepository.deleteById(comment_id);
    }
}
@Controller
@ResponseBody
public class CommentController {
    @Autowired
    private CommentService commentService;

    @RequestMapping("/get/{id}")
    public Comment findById(@PathVariable("id") int comment_id){
        Comment comment = commentService.findById(comment_id);
        return comment;
    }

    @RequestMapping("/update/{id}/{author}")
    public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
        Comment comment = commentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = commentService.updateComment(comment);
        return updateComment;
    }
    @RequestMapping("/{id}")
    public void deleteComment(@PathVariable("id") int comment_id){
        commentService.deleteComment(comment_id);
    }
}

基于API实现缓存

使用RedisTemplate

@Service
public class ApiCommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    public Comment findById(int comment_id){
        // 先从缓存中拿数据
        Object object = redisTemplate.opsForValue().get("comment_" + comment_id);
        if (object!=null){
            return (Comment)object;
        }
        else {
            // 如果缓存中没有,就到数据库中查数据然后放缓存里面
            Optional<Comment> optional = commentRepository.findById(comment_id);
            if (optional.isPresent()){
                Comment comment = optional.get();
                // 将查询结果放缓存中,时间为1天
                redisTemplate.opsForValue().set("comment_"+comment_id,comment,1, TimeUnit.DAYS);
                return comment;
            }else {
                return null;
            }
        }
    }

    public Comment updateComment(Comment comment){
        commentRepository.updateComment(comment.getAuthor(),comment.getId());
        // 更新数据后,将缓存数据也更新
        redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
        return comment;
    }
    public void deleteComment(int comment_id){
        commentRepository.deleteById(comment_id);
        // 删除后,将缓存也删除
        redisTemplate.delete("comment_"+comment_id);
    }
}
@Controller
@ResponseBody
@RequestMapping("api")
public class ApiCommentController {
    @Autowired
    private ApiCommentService commentService;

    @RequestMapping("/get/{id}")
    public Comment findById(@PathVariable("id") int comment_id){
        Comment comment = commentService.findById(comment_id);
        return comment;
    }

    @RequestMapping("/update/{id}/{author}")
    public Comment updateComment(@PathVariable("id")int comment_id,@PathVariable("author") String author){
        Comment comment = commentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = commentService.updateComment(comment);
        return updateComment;
    }
    @RequestMapping("/{id}")
    public void deleteComment(@PathVariable("id") int comment_id){
        commentService.deleteComment(comment_id);
    }
}

基于API自定义序列化缓存

上面的缓存机制是jdk,给我们的阅读造成了很大的困扰。我们可以考虑考虑使用自定义的缓存机制;

public class RedisAutoConfiguration {

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

我们可以参照这种写法来自定义一个序列化机制

RedisConfig.java

@Configuration
public class RedisConfig {
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 使用json格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        // 设置RedisTemplate模板API的序列化方式为json
        template.setDefaultSerializer(serializer);
        return template;
    }
}

使用redis做中间件为缓存查询验证码

首先我们还是需要先定义好阿里云短信工具类

import java.util.HashMap;
import java.util.Map;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;


public class AliyunSmsUtil {

    /**
     * 产品名称:云通信短信API产品
     */
    private static final String PRODUCT = "Dysmsapi";

    /**
     * 产品域名
     */
    private static final String DOMAIN = "dysmsapi.aliyuncs.com";

    /**
     * todo 开发者自己的AK(在阿里云访问控制台寻找)
     */
    private static final String ACCESS_KEY_ID = "LTAI4GCJ4nhyB3sCQj7ftJ9P";

    /**
     * todo accessKeySecret(在阿里云访问控制台寻找)
     */
    private static final String ACCESS_KEY_SECRET = "og4lIDixHgVlEYYXuHC9duhhZr0QT2";

    /**
     * todo 必填:短信签名
     */
    private static final String SIGN_NAME = "ABC商城";

    /**
     * todo 必填:REGION_ID
     */
    private static final String REGION_ID = "cn-hangzhou";

    /**
     * 发送方法
     *
     * @param phone         电话号码
     * @param templateCode  模板编号
     * @param templateParam 模板中的参数
     * @return 返回值
     * @throws ClientException 异常
     */
    public static SendSmsResponse sendSms(String phone, String templateCode, String templateParam) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile(REGION_ID, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        DefaultProfile.addEndpoint(REGION_ID, REGION_ID, PRODUCT, DOMAIN);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(phone);
        request.setSignName(SIGN_NAME); // 短信签名
        request.setTemplateCode(templateCode);
        request.setTemplateParam(templateParam);

        //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
        request.setOutId("10000");

        //hint 此处可能会抛出异常,注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
        return sendSmsResponse;
    }
    
    /**
     * 生成六位随机数
     *
     * @return
     */
    public static String createRandomVcode() {
        //验证码
        StringBuilder vcode = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            vcode.append((int) (Math.random() * 9));
        }
        return vcode.toString();
    }

    /*
     * 调用发送短信的接口,返回去一个map,用来取参数
     * */
    public static HashMap<String, Object> start(String phone) throws ClientException {
        HashMap<String, Object> codeMap = new HashMap<>();
        String code = createRandomVcode();
        // 随机生成6位数,添加在map参数中
        codeMap.put("code", code);
        String templateParam = JSONObject.toJSONString(codeMap); // 转json之后清空,方便多次使用
        String templateCode = "SMS_205138182";
        SendSmsResponse response = sendSms(phone, templateCode, templateParam);
        return codeMap;
    }
}

封装好了验证码之后,调用start方法,传入手机号,将获取到的验证码存在HashMap中使用,通过map.get(“code”)取出验证码数字。

加入springboot-redis的maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在yml中加入redis连接属性

spring:
  redis:
    port: 6379
    host: localhost
    password: 

修改掉redisTemplate的默认序列化方式,开启redis缓存

@EnableCaching // 开启缓存
@Configuration
public class RedisConfig {
    /*
    * 实现自定义序列化方式
    * */
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.json());
        template.setValueSerializer(RedisSerializer.string());
        template.setHashValueSerializer(RedisSerializer.json());
        return template;
    }

}

测试缓存

@SpringBootTest
class JqueryApplicationTests {


    @Autowired
    private RedisTemplate redisTemplate;
    private static String phone = "17628263062";

    @Test
    void contextLoads() throws ClientException {
        String code = AliyunSmsUtil.start(phone).get("code");
        redisTemplate.opsForValue().set(phone, code, 1, TimeUnit.DAYS); // 将短信存在redis缓存中,有效时间为1分钟
        /*
         * 在登录时只需要将redis中存入的短信取出来就行
         * */
        String tell = (String) redisTemplate.opsForValue().get(phone);
        System.out.println(tell);
        System.out.println(redisTemplate);
    }

}

image-20201123115027449


文章作者: 夏梦
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 夏梦 !
  目录