本文以一个spring的maven工程,整理记录使用注解缓存的问题,基本不需要自己写过多的封装的代码,很多人都实现Cache接口重新定义自己的缓存操作。其实不用也可以,Spring已经做了很多了。

预期目标

  • 查询:如果缓存中存在,直接从缓存中取,不查数据库。如果缓存中没有,从数据库查询并存入缓存,并设置超时时间。
  • 修改:删除缓存中的内容(如果存在)。
  • 删除:同步删除缓存中的内容。

实现步骤

  • service实现类, 在对应的方法上加上注解@Cacheable 调用方法时, 返回结果作为value放入缓存@CachePut 调用方法时 方法入参作为value 放入缓存@CacheEvict 调用方法是 从缓存中删除key 指定的数据condition 方法调用前判断,满足时,存入缓存, condition 默认为“”unless 方法调用后判断,满足时,不存 unless 默认为 “”这里的spel 表达式中,#result 代表返回值, 比如第一个selectByPrimaryKey接口上的#result就代表返回的user实例。
•  package com.susq.work.service.impl;

import com.susq.work.dao.UserMapper;
import com.susq.work.model.User;
import com.susq.work.service.UserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


@Service("userService")
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    @Cacheable(cacheNames = "common", key = "#id", unless = "#result != null")
    public User selectByPrimaryKey(String id) {
        return userMapper.selectByPrimaryKey(id);
    }

    @CachePut(value = "common", key = "#user.getId()")
    public User save(User user) {
        userMapper.saveUser(user);
        return user;
    }

    @CachePut(value = "common", key = "#user.getId()")
    public User update(User user) {
        userMapper.updateBySelective(user);
        return user;
    }

    @Override
    @CacheEvict(value = "common", key = "#id")
    public void delete(String id) {
        userMapper.delete(id);
    }
}

 

结果展示

  • 写了个Controller 测一测,单元测试也可以
•  package com.susq.work.controller;

import com.susq.work.model.User;
import com.susq.work.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;


@RestController
@Slf4j
@RequestMapping(value = "/mybatis")
public class MybatisController {

    @Resource
    private UserService userService;

    @RequestMapping(value = "/get")
    public User getUser(@RequestParam String id) {
        User user = userService.selectByPrimaryKey(id);
        log.info("my log " + user);
        return user;
    }

    @RequestMapping(value = "/save")
    public Map<</span>String, String> saveUser(@RequestBody User user) {
        userService.save(user);
        Map<</span>String, String> result = new HashMap<>();
        result.put("success", "00000000");
        return result;
    }

    @RequestMapping(value = "/update")
    public void updateUser(@RequestBody User user) {
        userService.update(user);
    }

    @RequestMapping(value = "/delete")
    public Map<</span>String, String> deleteUser(@RequestParam String id) {
        userService.delete(id);
        Map<</span>String, String> result = new HashMap<>();
        result.put("success", "00000000");
        return result;
    }
}
  • 调两次get接口,可以看到日志如下, 第一次查询的时候,缓存中还没有,直接从数据库中查的,后面再请求就是从redis拿了。但是common缓存我配置的超时时间是10秒,10秒过后,再请求又会查一次数据库放入缓存了。=1,  name=tingsuby,  address=中国)
  • 也可以运行redis终端,请求一次get, 会看到我们的缓存已经加进去了,并且带着我们配置的前缀COMMON:

上面 配置的 valueSerializer 是GenericJackson2JsonRedisSerializer , 所以get 以后基本我们能看到我们存进去的原始信息,虽然汉字不能正常显示,起码比JdkSerializationRedisSerializer 辨识度好多了。

一些问题

  • @Cacheable注解不起作用
    人们都说缓存的配置文件,要在spring的主配置文件中才生效,放在 里面不起作用,我就直接在 spring-mvc.xml 中 import resouce 引入了spring-cache的配置文件,发现根本无卵用。除非在springMVC容器扫描的时候,扫描除Controller外所有的Bean 才起作用。但是SpringMVC 的容器我们只是放Controller 的Bean的,其余的Bean应该加载在Spring 主容器中,所以这样做并不好。 于是我试了一下放在web.xml 的 节点中时可以的。而且如果有多个文件,加载的顺序并不重要,例如   contextConfigLocation   classpath:spring-mybatis.xml, classpath:spring-cache.xml 或者   contextConfigLocation   classpath:spring-cache.xml, classpath:spring-mybatis.xml 都是可以的。