美文网首页JAVA基础(未看)
SpringBoot Redis缓存

SpringBoot Redis缓存

作者: 忧郁的小码仔 | 来源:发表于2017-06-26 15:36 被阅读379次

关于redis的安装、配置及启动,参考Redis简单配置使用

1.添加依赖

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

2.添加配置文件

#Redis数据库索引(默认为0)
spring.redis.database=0
#Redis服务器地址
spring.redis.host=localhost
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=123456
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=1000

3.添加配置类

Redis 不是应用的共享内存,它只是一个内存服务器,就像 MySql ,我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 RedisTemplate,这些都是 Redis 缓存所必需的配置.

Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。

代码:

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName());
                sb.append(method.getName());
                for (Object object:objects) {
                    sb.append(object.toString());
                }
                return sb.toString();
            }
        };
    }

// 要启用缓存支持,我们需要创建一个新的 CacheManager bean
    @SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);
        return manager;
    }


    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

以下内容是针对上面配置的一些解释说明:

1.关于public KeyGenerator keyGenerator():
对于使用 @Cacheable 注解的方法,每个缓存的 key 生成策略默认使用的是参数名+参数值,比如以下方法:

@Cacheable("users")
public User findByUsername(String username)

这个方法的缓存将保存于 key 为 users~keys 的缓存下,对于 username 取值为 "赵德芳" 的缓存,key 为 "username-赵德芳"。一般情况下没啥问题,但是如果方法 key 取值相等然后参数名也一样的时候就出问题了,如:

@Cacheable("users")
public Integer getLoginCountByUsername(String username)

这个方法的缓存也将保存于 key 为 users~keys 的缓存下。对于 username 取值为 "赵德芳" 的缓存,key 也为 "username-赵德芳",将另外一个方法的缓存覆盖掉。

解决办法是使用自定义缓存策略,对于同一业务(同一业务逻辑处理的方法,哪怕是集群/分布式系统),生成的 key 始终一致,对于不同业务则不一致:

于是上述两个方法,对于 username 取值为 "赵德芳" 的缓存,虽然都还是存放在 key 为 users~keys 的缓存下,但由于 key 分别为 "类名-findByUsername-username-赵德芳" 和 "类名-getLoginCountByUsername-username-赵德芳",所以也不会有问题。

这对于集群系统、分布式系统之间共享缓存很重要,真正实现了分布式缓存。

2.RedisTemplate:
spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。
RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。因此被缓存对象需要实现java.io.Serializable接口,否则缓存出错。(比如后面要用到的User)。

RedisTemplate中定义了对5种数据结构操作

     redisTemplate.opsForValue();//操作字符串
     redisTemplate.opsForHash();//操作hash
     redisTemplate.opsForList();//操作list
     redisTemplate.opsForSet();//操作set
     redisTemplate.opsForZSet();//操作有序set

模板中的Redis key的类型(通常为String)如:RedisTemplate<String, Object>
注意:如果没特殊情况,切勿定义成RedisTemplate<Object, Object>,否则根据里氏替换原则,使用的时候会造成类型错误 。

3.StringRedisTemplate与RedisTemplate

两者的关系是StringRedisTemplate继承RedisTemplate。

两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。

SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。

StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。

4.定一个User类

//RedisTemplate下被缓存的对象一定要实现Serializable接口
public class User implements Serializable {

    private String name;
    private int age;
    private String email;

    public User() {
        super();
    }


    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }
}

5.测试

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

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;


    @Test
    public void contextLoads() {

        stringRedisTemplate.opsForValue().set("myKey", "123456");
        System.out.println(stringRedisTemplate.opsForValue().get("myKey"));
    }

    @Test
    public void testObj() throws Exception {

        User user = new User("archerLj", 27, "lj0011977@163.com");
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        operations.set("myUser", user);
        operations.set("myUser2", user,1, TimeUnit.MINUTES); //设置缓存过期时间为1分钟。
        Thread.sleep(60000);

        boolean exists = redisTemplate.hasKey("myUser");
        if (exists) {
            System.out.println("key myUser exists");
        } else {
            System.out.println("key myUser not exists");
        }

        boolean exists2 = redisTemplate.hasKey("myUser2");
        if (exists2) {
            System.out.println("key myUser2 exists");
        } else {
            System.out.println("key myUser2 not exists");
        }
    }
}

----------------------下面是注解方式的使用-----------

1.添加以下依赖

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

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

使用mysql数据库,JPA来做数据操作。

2.定义实体类User

package com.example.demo.domain;
@Entity
public class User implements Serializable {

    @Id
    private int id;
    private String name;
    private int age;
    private String email;

    public User() {
        super();
    }


    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }
}

3. 在启动类添加实体类扫描目录并开启缓存。

@EnableCaching
@ComponentScan("com.example.demo.domain")
public class DemoApplication {}

4.UserRepository接口定义

@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> {

    @Cacheable(key = "#p0")
    User findByName(String name);

    @CachePut(key = "#p0.name")
    User save(User user);
}

5.启动类添加两个请求:

@RestController
@SpringBootApplication
@EnableCaching
@ComponentScan("com.example.demo.domain")
public class DemoApplication {

    @Autowired
    UserRepository userRepository;

    @RequestMapping("/getUser")
    public User getUser() {
        User user = userRepository.findByName("archerLj");
        return user;
    }

    @RequestMapping("/save")
    public User save() {
        User user = userRepository.findByName("archerLj");
        user.setAge(102);
        return userRepository.save(user);
    }

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

6.修改配置

spring.datasource.url=jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
spring.jpa.properties.hibernate.show_sql=true

数据库:

use test1;
create table User(
id int primary key auto_increment,
name varchar(20),
age int,
email varchar(30)
) default charset=utf8;

insert into test1.User(name, age, email) values("archerLj", 27, "lj0011977@163.com");

7.测试:

访问http://localhost:8080/getUser,第一次访问,后台会打印sql语句,第二次访问会直接从缓存中获取,不会再打印sql。
访问http://localhost:8080/save来修改用户的age属性,再访问http://localhost:8080/getUser,发现没有打印sql,但age属性已经随着修改了。修改的数据之所以能同步到缓存中,是因为@CachePut注解起的作用。

最终demo

参考:
如何使用RedisTemplate访问Redis数据结构
springboot(三):Spring boot中Redis的使用
Spring Boot中的缓存支持(二)使用Redis做集中式缓存

相关文章

网友评论

    本文标题:SpringBoot Redis缓存

    本文链接:https://www.haomeiwen.com/subject/ulkjcxtx.html