SpringBoot学习笔记(八)SpringBoot缓存、@Cacheable、SpringBoot使用Redis缓存、自定义CacheManager

JSR107规范

Java Caching定义了5个接口:CachingProvider、CacheManager、Cache、Entry、Expiry

Spring缓存抽象

在这里插入图片描述

SpringBoot中的默认缓存

@Cacheable及其属性

  1. 使用 @EnableCaching 开启缓存
  2. 属性及含义
属性名含义及作用
cacheName/value指定缓存组件的名字
key缓存数据时用的key,若不指定则默认使用方法参数值。value为方法返回值。
keyGeneratorkey的生成器。可以自己指定key的生成器的组件id。key与keyGenerator二者指定其一。
cacheManager指定缓存管理器,或指定cacheResolver
condition指定符合条件的情况下进行缓存
unless指定的条件为true时,不会进行缓存,可以获取到结果进行判断
sync是否使用异步

@Cacheable示例

SpringBoot入门学习笔记(七)SpringBoot数据访问 的基础上搭建。

  1. 给SpringBoot入口程序加上注解 @EnableCaching
@SpringBootApplication
@EnableCaching  //开启缓存
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}
  1. 创建数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pwd` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;
  1. 创建实体类
@Entity
@Table(name = "user")
public class User {

    @Id //这是主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) //主键自增列
    private Integer id;

    @Column(name = "name",length = 255)
    private String name;

    @Column //若省略则默认列名为属性名
    private String pwd;
	
	//省略getter、setter、toString方法
}
  1. 编写Service层
package com.hx.cache.service;

import com.hx.cache.entity.User;
import com.hx.cache.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * @ClassPath: com.hx.cache.service.UserService
 * @Author: Huathy
 * @Description:
 * @Date: 2020-09-20 23:21
 */
@Service
public class UserService {

    @Autowired
    UserRepository userRepository;

    /**
     * 将该方法的运行结果进行缓存,以后要相同的数据,直接到缓存中获取。不用调用方法
     *
     * cacheManager:管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中。每个缓存组件有自己唯一的名字
     * 几个属性:
     *      cacheName/value:指定缓存组件的名字
     *      key:缓存数据时用的key,若不指定则默认使用方法参数值。value为方法返回值。
     *      keyGenerator:key的生成器。可以自己指定key的生成器的组件id。key与keyGenerator二者指定其一。
     *      cacheManager:指定缓存管理器,或指定cacheResolver
     *      condition:指定符合条件的情况下进行缓存
     *      unless:指定的条件为true时,不会进行缓存,可以获取到结果进行判断
     *      sync:是否使用异步
     */
    @Cacheable(cacheNames = "user",key = "#id",condition = "#id>0",unless = "#result==null")
    public Optional<User> getUser(Integer id){
        System.out.println("getUser Executed...");
        Optional<User> user =  userRepository.findById(id);
        return user;
    }
}
  1. Controller层
package com.hx.cache.controller;

import com.hx.cache.entity.User;
import com.hx.cache.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

/**
 * @ClassPath: com.hx.jpa.controller.UserController
 * @Author: Huathy
 * @Description:
 * @Date: 2020-09-20 15:34
 */
@RestController
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping("/getUser/{id}")
    //Optional类是Java8为了解决null值判断问题,可避免null导致的NPE(空指针异常)
    public Optional<User> getUser(@PathVariable("id") Integer id){
        return userService.getUser(1);
    }

    @RequestMapping("/add")
    public User add(User user){
       User u = userService.add(user);
       return  u;
    }
}
  1. repository
package com.hx.cache.repository;

import com.hx.cache.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * 继承JpaRepository来完成对数据库的操作
 */
public interface UserRepository extends JpaRepository<User,Integer> {

}
  1. 配置文件
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/jpaTest?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: a
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    show-sql: true   #控制台显示sql
    hibernate:
      #更新或创建数据表结构
      ddl-auto: update
  1. 测试:访问 localhost:8080/getUser/1。当第一次访问时,调用了Service层的查询方法。而第二次访问时,该数据从缓存中读取。

属性KeyGenerator示例

  1. 编写配置类
package com.hx.cache.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @ClassPath: com.hx.cache.config.MyConfig
 * @Author: Huathy
 * @Description:
 * @Date: 2020-09-23 18:48
 */
@Configuration
public class CacheConfig {

    @Bean(name = "myKeyGenerator")  //指定该组件的id
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName() + "[" + Arrays.asList(params).toString() + "]";
            }
        };
    }
}
  1. 修改Service层
    @Cacheable(cacheNames = "user",keyGenerator = "myKeyGenerator")
    public Optional<User> getUser(Integer id){
        System.out.println("getUser Executed...");
        Optional<User> user =  userRepository.findById(id);
        return user;
    }
  1. 测试debug 访问:localhost:8080/getUser/1
    在这里插入图片描述
    在这里插入图片描述

@CachePut

先调用方法,再将目标结果进行缓存

示例

  1. Mapper映射接口
package com.hx.cache.mapper;

import com.hx.cache.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;

@Mapper
public interface UserMapper {
    @Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
    public int update(User user);
}
  1. Service层
@Service
public class UserService {

    @Autowired
    UserMapper userMapper;
    
    @Cacheable(cacheNames = "user",key = "#id")
    public Optional<User> getUser(Integer id){
        System.out.println("getUser Executed...");
        Optional<User> user =  userRepository.findById(id);
        return user;
    }
    
    /**
     * @CachePut : 既调用方法又更新缓存
     * 运行时机:先调用目标方法,再将目标结果进行缓存
     * 这里需要注意key与上面的key相同,才可以更新
     */
    @CachePut(value = "emp",key = "#u.id")
    public int updateUser(User u){
        System.out.println("UPDATE USER...");
        return userMapper.update(u);
    }
}
  1. Controller层
@RestController
public class UserController {

    @Autowired
    UserService userService;
	
    @RequestMapping("/update")
    public int update(User user){
        return userService.updateUser(user);
    }
}

@CacheEvict 缓存清除

示例如下:

  1. Service层
@Service
public class UserService {

    @Autowired
    UserRepository userRepository;
	
//    @CacheEvict(value = "user",key = "#id")   //删除指定数据
    @CacheEvict(value = "user",allEntries = true)   //删除user下的所有数据
    public int del(Integer id){
        userRepository.deleteById(id);
        return 1;
    }
}
  1. Controller层
@RestController
public class UserController {

    @Autowired
    UserService userService;
    @RequestMapping("/delUser")
    public int del(Integer id){
        return userService.del(id);
    }
}
  1. 测试:先查询,然后删除,再次查询可见缓存已被清除,查询数据库结果为null。

@Caching 定义复杂的缓存注解

示例如下:

  1. mapper映射接口
@Mapper
public interface UserMapper {
    @Select("select * from user where name=#{name} limit 0,1")
    public User getUserByName(String name);
}
  1. Service层
@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    /**
     * @Caching :定义复杂的缓存注解
     */
    @Caching(
            cacheable = {
                    @Cacheable(value = "user",key = "#name")
            },
            put = {
                    @CachePut(value = "user",key = "#result.id"),
            }
    )
    public User getUserByName(String name){
        return userMapper.getUserByName(name);
    }
}
  1. controller层
@RestController
public class UserController {

    @Autowired
    UserService userService;
    @RequestMapping("/getUserByName")
    public User getUserByName(String name){
        return userService.getUserByName(name);
    }
}

@CacheConfig 缓存配置

作用:用于配置缓存配置中的公共部分
示例如下:

/**
 * @CacheConfig : 缓存配置注解,配置缓存中的公共配置
 *      cacheNames属性 :可省去下边的value="user"
 */
@CacheConfig(cacheNames = "user")
@Service
public class UserService {
	//省略代码......
	//如以下代码中的cacheNames、value可以省略
	 @Cacheable(cacheNames = "user",key = "#id")
	 @CacheEvict(value = "user",allEntries = true)
}

SpringBoot使用Redis缓存

下载安装Redis缓存

下载地址:http://download.redis.io/releases/redis-6.0.6.tar.gz
redis中文网:http://www.redis.cn/
redis桌面工具:https://redisdesktop.com/

在SpringBoot中使用redis

  1. 引入redis依赖、jedis依赖
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 编写测试类
@SpringBootTest
class CacheApplicationTests {

    @Autowired
    StringRedisTemplate stringRedisTemplate;    //操作key-value都是字符串的

    @Autowired
    RedisTemplate redisTemplate;    //操作key-value都是对象的

    /**
     * Redis五种常见的数据类型
     * String,List,Set,ZSet,Hash
     * stringRedisTemplate.opsForValue();
     * stringRedisTemplate.opsForList();
     * stringRedisTemplate.opsForSet();
     * stringRedisTemplate.opsForZSet();
     * stringRedisTemplate.opsForHash();
     */
    @Test
    void test01(){
        stringRedisTemplate.opsForValue().append("msg","jiujiu");
    }

    /**
     * 测试保存对象
     * 默认保存对象会使用jdk序列化机制,将序列化后的对象保存到redis中
     * 注意:这里需要实体类必须实现序列化接口
     */
    @Test
    void test02(){
        Optional<User> users = userRepository.findById(5);
        User user = users.get();
        redisTemplate.opsForValue().set("user01",user);
    }
}

使用自定义序列化

  1. 编写缓存的配置类
package com.hx.cache.config;

import com.hx.cache.entity.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;

/**
 * @ClassPath: com.hx.cache.config.MyRedisConfig
 * @Author: Huathy
 * @Description:
 * @Date: 2020-09-25 09:02
 */
@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, User> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class);
        template.setDefaultSerializer(serializer);
        return template;
    }

}
  1. 使用测试

@SpringBootTest
class CacheApplicationTests {

    @Autowired
    UserRepository userRepository;

    @Autowired
    RedisTemplate<Object,User> userRedisTemplate;

    @Test
    void test03(){
        Optional<User> users = userRepository.findById(5);
        User user = users.get();
        userRedisTemplate.opsForValue().set("user02",user);
    }
}
  1. 使用RedisDesktopManager查看可见缓存的数据
    在这里插入图片描述

自定义CacheManager

  1. 引入Redis的starter后,容器中注入了RedisCacheManager。其他的CacheManager不起作用。
  2. RedisCacheManager帮我们创建RedisCache来作为缓存组件,RedisCache来操作redis缓存数据
  3. 默认保存数据key-value都是object,利用序列化保存
    • 引入redis的starter,cacheManager变为RedisCacheManager
    • 默认创建RedisCacheManager,操作redis时使用RedisTemplate<Object,Object>。其默认使用的是jdk序列化机制
    • 为使用json序列化,需要自定义CacheManager

自定义CacheManager示例

  1. RedisConfig配置类
package com.hx.cache.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hx.cache.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * @ClassPath: com.hx.cache.config.MyRedisConfig
 * @Author: Huathy
 * @Description:
 * @Date: 2020-09-25 09:02
 */
@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, User> userRedisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, User> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<User>(User.class);
        template.setDefaultSerializer(serializer);
        return template;
    }
	
	//这是Redis缓存管理器
    @Bean
    public RedisCacheManager userRedisCacheManager(RedisConnectionFactory connectionFactory){
        Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

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

        redisSerializer.setObjectMapper(objectMapper);

        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build();

        return redisCacheManager;
    }

}

注:本文示例代码均为关键部分代码。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Huathy-雨落江南,浮生若梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值