目录

配置

具体实现

用户实体类

接口

接口实现类

缓存注解说明

@CachePut

@Cacheable

@CacheEvict

测试结果

saveUser

getUser

updateUser

removeUser


配置

首先在POM文件中加入Redis相关的依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

然后在application.properties文件中配置Redis相关属性

#配置Redis服务器属性
spring.redis.host=192.168.115.133
spring.redis.port=6379
#配置连接超时时间(毫秒)
spring.redis.timeout=3000
#配置连接池属性
spring.redis.jedis.pool.max-active=20
spring.redis.jedis.pool.max-idle=20
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-wait=3000
#缓存名称,一般会作为缓存在业务上的分类,以key的前缀出现(userCache::)
spring.cache.cache-names=userCache
#禁用缓存前缀
#spring.cache.redis.use-key-prefix=false
#缓存超时时间
spring.cache.redis.time-to-live=120000

具体实现

用户实体类

@Setter
@Getter
@ToString
public class User implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
}

接口

public interface CacheService {
    User saveUser(User user);
    User getUser(Integer id);
    User updateUser(User user);
    Integer removeUser(Integer id);
}

接口实现类

@Service
public class CacheServiceImpl implements CacheService {
    // 为了简化功能,用一个集合代替数据库
    private Map<Integer, User> db = new ConcurrentHashMap<>();

    @Override
    @CachePut(value = "userCache", key = "'user:' + #result.id")
    public User saveUser(User user) {
        // 保存user信息 省略保存数据库的过程
        db.put(user.getId(), user);
        return user;
    }

    @Override
    @Cacheable(value = "userCache", key = "'user:' + #id", unless = "#result == null")
    public User getUser(Integer id) {
        System.out.println("没有命中缓存,进入业务方法了");
        return db.get(id);
    }

    @Override
    @CachePut(value = "userCache", key = "'user:' + #result.id", condition = "#result != null")
    public User updateUser(User user) {
        // 调用getUser方法,user存在才会更新
        // 基于SpringAOP原理,同类中的方法调用会导致切面失效,因此这里会使getUser的缓存注解会失效
        User u = this.getUser(user.getId());
        if (u == null) {
            return null;
        }
        db.put(user.getId(), u);
        return user;
    }

    @Override
    @CacheEvict(value = "userCache", key = "'user:' + #id", beforeInvocation = true)
    public Integer removeUser(Integer id) {
        db.remove(id);
        return 1;
    }
}

缓存注解说明

在CacheServiceImpl实现类中,分别定义了对于用户信息的增删改查四个方法,其中涉及到了三类缓存注解。

@CachePut

表示将方法结果返回放到缓存中。其中value属性的值与在application.properties中配置的缓存名称对应,会以key的前缀出现,key属性的配置用到了Spring EL表达式,#result.id代表返回值的id属性的值,#id代表入参id的值。

@Cacheable

表示先从缓存中通过定义的键查询,如何可以查到则直接返回,否则执行方法具体内容返回数据,并将结果返回,unless = "#result == null"代表如果结果为null则不缓存,等同于condition = "#result != null"。

@CacheEvict

表示通过定义的键移除缓存,beforeInvocation属性代表在方法之前或者之后移除缓存,这里定义为true,代表在方法执行前移除缓存,默认为false。

测试结果

@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisDemoApplicationTests {

    @Autowired
    private CacheService cacheService;

    @Test
    public void test() {
        saveUser();
        getUser();
        updateUser();
        removeUser();
    }
    
    private void saveUser() {
        User u = new User();
        u.setId(1);
        u.setName("张三");
        u.setAge(25);
        User user = cacheService.saveUser(u);
        System.out.println("【save】" + user);
    }

    private void getUser() {
        User user = cacheService.getUser(1);
        System.out.println("【get】" + user);
    }
    
    private void updateUser() {
        User u = cacheService.getUser(1);
        u.setName("李四");
        u.setAge(30);
        User user = cacheService.updateUser(u);
        System.out.println("【update】" + user);
    }
    
    private void removeUser() {
        Integer r = cacheService.removeUser(1);
        System.out.println("【remove】" + r);
    }
}

test()方法先后执行了saveUser()、getUser()、updateUser()、removeUser()四个过程,可以在每一步设置断点,然后通过Redis客户端观察缓存真是的更新情况。

saveUser

首先执行saveUser,通过Redis客户端可以看到,user的缓存信息已经成功保存。

127.0.0.1:6379> keys *
1) "userCache::user:1"
127.0.0.1:6379> get userCache::user:1
"\xac\xed\x00\x05sr\x00\x1ecom.hujy.redisdemo.entity.User\xc3a\xa1\xe5\xf1\xa2B\x86\x02\x00\x03L\x00\x03aget\x00\x13Ljava/lang/Integer;L\x00\x02idq\x00~\x00\x01L\x00\x04namet\x00\x12Ljava/lang/String;xpsr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x19sq\x00~\x00\x04\x00\x00\x00\x01t\x00\x06\xe5\xbc\xa0\xe4\xb8\x89"

getUser

继续执行完getUser方法,此时的控制台信息:

【save】User(id=1, name=张三, age=25)
 【get】User(id=1, name=张三, age=25)

可以正确的返回我们期望的结果,而且发现方法内的打印信息并没有执行,说明已经命中了缓存。

updateUser

接着执行updateUser,红色部分是updateUser方法打印的信息,因为在updateUser方法内部又调用了getUser方法,基于SpringAOP原理,同类中的方法调用会导致切面失效,因此这里会使getUser的缓存注解会失效,进入了方法内部,最后可以看到缓存已经被更新。

控制台信息:

【save】User(id=1, name=张三, age=25)
 【get】User(id=1, name=张三, age=25)
没有命中缓存,进入业务方法了
 【update】User(id=1, name=李四, age=30)
 【get】User(id=1, name=李四, age=30)

removeUser

最后执行了removeUser方法,通过Redis客户端可以看到缓存信息已经被删除了。

127.0.0.1:6379> get userCache::user:1
(nil)
127.0.0.1:6379> keys *
(empty list or set)

完整的控制台信息:

【save】User(id=1, name=张三, age=25)
 【get】User(id=1, name=张三, age=25)
 没有命中缓存,进入业务方法了
 【update】User(id=1, name=李四, age=30)
 【get】User(id=1, name=李四, age=30)
 【remove】1

完整代码地址:https://github.com/hjy0319/redis-demo