微人事第九天:spring cache整合redis

本文详细介绍如何在SpringBoot项目中整合Redis缓存,包括工程创建、依赖引入、配置及使用,涵盖@Cacheable、@CachePut、@CacheEvict等注解的应用,以及缓存的读取、更新和删除操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot中整合redis有许多种方法,cache是springboot官方作为一个统一的定义标准。下面来实现spring cache整合redis

1.工程创建
创建springboot工程,创建时需要引入四个依赖:web,spring security,spring data redis,spring cache.
在这里插入图片描述
在pom.xml文件中可看到刚才添加的四个依赖已经被引入。

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

2.设置cache之前的操作
配置redis的基础信息和cache的名称

spring.redis.host=127.0.0.1
spring.redis.database=0
spring.redis.port=6379
spring.redis.password=

spring.cache.cache-names=c1

打开redis
在这里插入图片描述
编写实体类user

package org.javaboy.redis;

public class User {
    private Integer id;
    private String username;
    private String address;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

service类中的方法作用是根据id返回user。

package org.javaboy.redis;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public User getUserById(Integer id) {
        System.out.println("getUserById>>>" + id);
        User user = new User();
        user.setId(id);
        return user;
    }
}

测试类:
测试类中首先注入service类,通过userservice调用方法getUserById,这里调用两次getUserById是为了查看redis是否成功启用。

package org.javaboy.redis;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

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

    @Autowired
    private UserService userService;

    @Test
    public void contextLoads() {
        User u1 = userService.getUserById(1);
        User u2 = userService.getUserById(1);
        System.out.println(u1);
        System.out.println(u2);
    }

}

启动方法之后:
可以看出这里cache还未配置所以redis还没启用。

getUserById>>>1
getUserById>>>1
User{id=1, username='null', address='null'}
User{id=1, username='null', address='null'}

3.cache的相关配置
在启动类上添加注解@EnableCaching用来启用缓存

package org.javaboy.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class RedisApplication {

    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class, args);
    }

}

在service方法上添加注解:

 @Cacheable(cacheNames = "c1")

该注解用于开启缓存,该方法就具备缓存功能,名字就是先前在properties中配置的cache的名称。
现在参数就是缓存中的key,返回值就是缓存中的value。

package org.javaboy.redis;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(cacheNames = "c1")
    public User getUserById(Integer id) {
        System.out.println("getUserById>>>" + id);
        User user = new User();
        user.setId(id);
        return user;
    }
}

配置完之后现在来启动测试类:
可以看出报错了,因为我们的user实体类没有进行序列化。
在这里插入图片描述
将实体类user继承序列化接口,实现序列化

public class User implements Serializable

再次执行测试类:
方法执行成功了,而且和没有配置cache时的结果有所不同,缺少了getUserById>>>1语句,这是因为,我们测试的时候执行了两次service中的方法,而且两次的参数都是相同的。这就导致了第一次执行之后,结果存储到了缓存中,第二次再次执行时就不会执行该方法了,直接从缓存中将值取出。这就是redis最大的作用

getUserById>>>1
User{id=1, username='null', address='null'}
User{id=1, username='null', address='null'}

下面再来做个测试:
现在在serice方法中添加一个参数,响应的测试类中也传递一个值:

    public User getUserById(Integer id,String name) {
User u1 = userService.getUserById(1,"aa");
        User u2 = userService.getUserById(1,"bb");

再次执行测试方法:
因为传递的参数值不一样所以redis认为这两次查询不是相同的,所以第二次调用方法时就不会从缓存中去取值,而是再次执行service中的方法。在redis中将参数看出是key,无论参数有多少个。

getUserById>>>1
getUserById>>>1
User{id=1, username='null', address='null'}
User{id=1, username='null', address='null'}

如果现在非要以第一个参数为key呢?只需要在@Cacheable上添加key属性即可
key=“#id”表示这里将id当作是key,属性name就直接被忽略掉了。

    @Cacheable(cacheNames = "c1",key = "#id")

现在我们再来执行test方法:
可以看到方法只执行了一次,原理前面已经讲过,redis这里默认id是key,而这里两次的id值相同所以第二次执行时,就会从缓存中获取第一次的结果。

getUserById>>>2
User{id=2, username='null', address='null'}
User{id=2, username='null', address='null'}

@Cacheable中的key默认是以全部参数作为key,当然你也可以特定的参数为key,方法名为key,甚至可以设置cache名为key。不过一般我们都是把参数当成key。

如果key太复杂你也可以自己定义一个方法来设置具体的key
让这个方法去实现KeyGenerator接口,然后实现改接口中的方法。改方法用来定义key,改方法第一个参数是对象名,第二个参数是方法名,第三个参数是参数列表。这里我们将方法名+所有参数当成key。

package org.javaboy.redis;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Component
public class MyKeyGenerator implements KeyGenerator {

    @Override
    public Object generate(Object o, Method method, Object... objects) {
        return method.getName() + ":" + Arrays.toString(objects);
    }
}

定义好配置key的类之后我们需要在service方法中将该方法引入:
在注解中通过keyGenerator将配置的类引入,方法名称首字母需要改为小写。

@Cacheable(cacheNames = "c1",keyGenerator = "myKeyGenerator")

再次执行测试方法:
由于这里的参数值不一样,所以redis又失效了。

getUserById>>>2
getUserById>>>2
User{id=2, username='null', address='null'}
User{id=2, username='null', address='null'}

现在我们要试一下删除方法是否可以用缓存实现:
@CacheEvict注解主要用于清除缓存中的内容 ,注解中cacheNames为cache的名字,allEntries为false代表根据id删除缓存中的内容,为true则代表将缓存中的数据全部删除。默认为false,这里我们只需要让他根据id删除就可以了。

@CacheEvict(cacheNames = "c1")
    public void deleteUserById(Integer id) {
        System.out.println("deleteUserById>>>" + id);
    }

编写测试类:
分析:我们先要根据id去查询user,然后执行删除方法,将缓存中的结果删除,然后我们再次调用查询方法,保证和第一次调用的id值要一样。预期结果是:两次查询都会执行方法,因为中间将第一次在缓存中的内容删除了。

package org.javaboy.redis;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

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

    @Autowired
    private UserService userService;

    @Test
    public void contextLoads() {
        User u1 = userService.getUserById(3,"aa");
        userService.deleteUserById(3);
        User u2 = userService.getUserById(3,"bb");
        System.out.println(u1);
        System.out.println(u2);
    }

}

与预期相符,删除缓存生效

getUserById>>>3
deleteUserById>>>3
getUserById>>>3
User{id=3, username='null', address='null'}
User{id=3, username='null', address='null'}

查询,删除都说了,还剩下更新问题。加入我查询一条数据,然后去更新这条数据,再去查询时在缓存中得到的数据就是尚未更新时的数据值,这是不对的,下面来讲一下更新操作时cache的配置。
在service类中添加修改方法 @CachePut用于数据更新时即使更新缓存中的数据,key时user的id属性。

@CachePut(cacheNames = "c1",key = "#user.id")
    public User updataUserById(User user) {
        return user;
    }

测试类:
预期这里打印出的结果应该不一样

package org.javaboy.redis;

        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.test.context.SpringBootTest;
        import org.springframework.test.context.junit4.SpringRunner;

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

    @Autowired
    private UserService userService;

    @Test
    public void contextLoads() {
        User u1 = userService.getUserById(3,"aa");
//        userService.deleteUserById(3);
        User user = new User();
        user.setId(3);
        user.setUsername("javaboy");
        user.setAddress("上海");
        userService.updataUserById(user);
        User u2 = userService.getUserById(3,"bb");
        System.out.println(u1);
        System.out.println(u2);
    }

}

可以发现更新操作成功

User{id=3, username='null', address='null'}
User{id=3, username='javaboy', address='上海'}

这里还有一个问题就是每个方法的注解中都需要声明cache的名称,这样写非常麻烦,我们需要在service类最前面添加:cacheNames = (“c1”),这样service类中的方法就不需要在写cache的名称了。

总结:
以上这些cache的注解是规范并没有实现,实现是在redis中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值