springboot整合redis实现缓存

一、redis

二、spring boot 整合redis

三、基于注解的Redis缓存实现

使用@Cacheable、@CachePut、@CacheEvict注解定制缓存管理

对CommentService类中的方法进行修改使用@Cacheable、@CachePut、@CacheEvict三个注解定制缓存管理,修改后的方法如下

java 复制代码
@Cacheable(cacheNames = "comment",unless = "#result==null")
public Comment findById(int comment_id){

    Optional<Comment> optional = commentRepository.findById(comment_id);
    if(optional.isPresent()){
        return optional.get();
    }
return null;
}

@CachePut(cacheNames = "comment",key = "#result.id")
public Comment updateComment(Comment comment){

    commentRepository.updateComment(comment.getAuthor(), comment.getId());
    return comment;

}

@CacheEvict(cacheNames = "comment")
public void deleteComment(int comment_id){

    commentRepository.deleteById(comment_id);

}

基于注解的Redis查询缓存测试

项目启动成功后,通过浏览器访问http://localhost:8088/get/1,如果控制台显示如下:

复制代码
......

Hibernate: select comment0_.id as id1_0_0_, comment0_.a_id as a_id2_0_0_, comment0_.author as author3_0_0_, commen.......
......
java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.itheima.my06.entity.Comment]
......

需要对实体类进行序列化,对Comment修改如下:

java 复制代码
public class Comment implements Serializable

再次测试,并重复刷新浏览器,只出现一条sql语句

打开Redis客户端可视化管理工具Redis Desktop Manager,连接本地启用的Redis服务

可以看出,评论存储到了redis缓存库中comment名称空间下,key=comment::1,value=经过JDK默认序列化格式后的HEX格式值,不方便查看,需要自定义数据的序列化格式。

基于注解的Redis缓存更新测试

项目启动成功后,通过浏览器访问http://localhost:8088/update/1/shitou,结果如下:

查看缓存,已经改过来了,如下图:

执行updateComment()方法时只会执行一条更新SQL语句

基于注解的Redis缓存删除测试

项目启动成功后,通过Redis客户端可视化管理工具Redis Desktop Manager查看缓存信息。

如果不存在缓存,通过浏览器访问http://localhost:8088/get/1,缓存数据,

再次通过Redis Desktop Manager查看,reload下

接着访问http://localhost:8088/delete/1

reload,查看缓存信息,该条缓存被删除

四、基于API的Redis缓存实现

1.使用Redis API 进行业务数据缓存管理

编写一个进行业务处理的类ApiCommentService,使用@Autowired注解注入Redis API中常用的RedisTemplate(类似于Java基础API中的JdbcTemplate);

然后在数据查询、修改和删除三个方法中,根据业务需求分别进行数据缓存查询、缓存存储、缓存更新和缓存删除。

同时,Comment数据对应缓存管理的key值都手动设置了一个前缀"comment_",这是针对不同业务数据进行缓存管理设置的唯一key,避免与其他业务缓存数据的key重复。

java 复制代码
@Service
public class ApiCommentService {
    @Autowired
    private CommentRepository commentRepository;
    @Autowired
    private RedisTemplate redisTemplate;

    public Comment findById(int comment_id){
        // 先从Redis缓存中查询数据
        Object object =  redisTemplate.opsForValue().get("comment_"+comment_id);
        if (object!=null){
            return (Comment)object;
        }else {
            // 缓存中没有,就进入数据库查询
            Optional<Comment> optional = commentRepository.findById(comment_id);
            if(optional.isPresent()){
                Comment comment= optional.get();
                // 将查询结果进行缓存,并设置有效期为1天
                redisTemplate.opsForValue().set("comment_"+comment_id, comment,1, TimeUnit.DAYS);
                return comment;
            }else {
                return null;
            }
        }
    }

    public Comment updateComment(Comment comment){
        commentRepository.updateComment(comment.getAuthor(), comment.getId());
        // 更新数据后进行缓存更新
        redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);
        return comment;
    }

    public void deleteComment(int comment_id){
        commentRepository.deleteById(comment_id);
        // 删除数据后进行缓存删除
        redisTemplate.delete("comment_"+comment_id);
    }
}

2.创建Web访问层类ApiCommentController

在类上加入了@RequestMapping("/api")注解用于窄化请求,

并通过@Autowired注解注入了新编写的ApiCommentService实例对象,

然后调用ApiCommentService中的相关方法进行数据查询、修改和删除。

java 复制代码
@RestController
@RequestMapping("/api")  // 窄化请求路径
public class ApiCommentController {
    @Autowired
    private ApiCommentService apiCommentService;

    @GetMapping("/get/{id}")
    public Comment findById(@PathVariable("id") int comment_id){
        Comment comment = apiCommentService.findById(comment_id);
        return comment;
    }

    @GetMapping("/update/{id}/{author}")
    public Comment updateComment(@PathVariable("id") int comment_id,
                                 @PathVariable("author") String author){
        Comment comment = apiCommentService.findById(comment_id);
        comment.setAuthor(author);
        Comment updateComment = apiCommentService.updateComment(comment);
        return updateComment;
    }

    @GetMapping("/delete/{id}")
    public void deleteComment(@PathVariable("id") int comment_id){
        apiCommentService.deleteComment(comment_id);
    }
}

3.基于API的Redis缓存实现的相关配置

  1. 基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持。
  2. 基于API的Redis缓存实现需要在Spring Boot项目的pom.xml文件中引入Redis依赖启动器,并在配置文件中进行Redis服务连接配置,同时将进行数据存储的Comment实体类实现序列化接口。
  3. 缓存测试与基于注解的Redis缓存实现的测试基本一样,但访问路径加"/api",如http://localhost:8080/api/get/2

4.做测试

http://localhost:8088/api/get/1

jdk序列化导致出现上图红色框的内容,无意义但会占据内存,所以要更改序列化方式

五、通过 RedisTemplate 自定义缓存序列化 (通过api缓存数据)

1.Redis API 默认序列化机制

基于Redis API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,这里打开RedisTemplate类,查看源码可知:

java 复制代码
@Override
public void afterPropertiesSet() {
    super.afterPropertiesSet();
    boolean defaultUsed = false;
    if (defaultSerializer == null) {
        defaultSerializer = new JdkSerializationRedisSerializer(
            classLoader != null ? classLoader : this.getClass().getClassLoader());
}

1.使用RedisTemplate进行Redis数据缓存操作时,内部默认使用的是JdkSerializationRedisSerializer序列化方式,所以进行数据缓存的实体类必须实现JDK自带的序列化接口(例如Serializable);

2.使用RedisTemplate进行Redis数据缓存操作时,如果自定义了缓存序列化方式defaultSerializer,那么将使用自定义的序列化方式。

2.自定义RedisTemplate序列化机制

分析:

在项目中引入Redis依赖后,Spring Boot提供的RedisAutoConfiguration自动配置会生效。

打开RedisAutoConfiguration类,查看内部源码中关于RedisTemplate的定义方式可知:

java 复制代码
public class RedisAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
    return template;
    }
  1. 在Redis自动配置类中,通过Redis连接工厂RedisConnectionFactory初始化了一个RedisTemplate;该类上方添加了@ConditionalOnMissingBean注解(顾名思义,当某个Bean不存在时生效),用来表明如果开发者自定义了一个名为redisTemplate的Bean,则该默认初始化的RedisTemplate会被覆盖。
  2. 如果想要使用自定义序列化方式的RedisTemplate进行数据缓存操作,可以参考上述核心代码创建一个名为redisTemplate的Bean组件,并在该组件中设置对应的序列化方式即可。

解决:

在项目中创建一个Redis自定义配置类RedisConfig,通过@Configuration注解定义了一个RedisConfig配置类,并使用@Bean注解注入了一个默认名称为方法名的redisTemplate组件(注意,该Bean组件名称必须是redisTemplate)。在定义的Bean组件中,自定义了一个RedisTemplate,使用自定义的Jackson2JsonRedisSerializer数据序列化方式;在定制序列化方式中,定义了一个ObjectMapper用于进行数据转换设置。

java 复制代码
@Configuration   // 定义一个配置类
public class RedisConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        // 使用JSON格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //过时om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//用下面的方法代替上面的方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
        // 设置RedisTemplate模板API的序列化方式为JSON
        template.setDefaultSerializer(jacksonSeial);
        //设置key的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        return template;
    }
}

3.效果测试

项目启动成功后,通过浏览器访问 http://localhost:8088/api/get/3,并重复刷新浏览器查看同一条数据信息 ,数据库只执行了一次SQL语句

六、通过 RedisCacheManager 自定义缓存序列化(适用通过注解缓存数据)

1.Redis 注解默认序列化机制

1.Spring Boot整合Redis组件提供的缓存自动配置类RedisCacheConfiguration(org.springframework.boot.autoconfigure.cache),

其内部是通过Redis连接工厂RedisConnectionFactory定义了一个缓存管理器RedisCacheManager;

同时定义RedisCacheManager时,也默认使用了JdkSerializationRedisSerializer序列化方式。

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

	@Bean
	RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
			ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
			RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
		RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
				determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
		List<String> cacheNames = cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
		}
		if (cacheProperties.getRedis().isEnableStatistics()) {
			builder.enableStatistics();
		}
		redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
		return cacheManagerCustomizers.customize(builder.build());
	}

	private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
			CacheProperties cacheProperties,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
			ClassLoader classLoader) {
		return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
	}

	private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
			CacheProperties cacheProperties, ClassLoader classLoader) {
		Redis redisProperties = cacheProperties.getRedis();
		org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
				.defaultCacheConfig();
		config = config.serializeValuesWith(
				SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
		if (redisProperties.getTimeToLive() != null) {
			config = config.entryTtl(redisProperties.getTimeToLive());
		}
		if (redisProperties.getKeyPrefix() != null) {
			config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
		}
		if (!redisProperties.isCacheNullValues()) {
			config = config.disableCachingNullValues();
		}
		if (!redisProperties.isUseKeyPrefix()) {
			config = config.disableKeyPrefix();
		}
		return config;
	}

}

2.如果想要使用自定义序列化方式的RedisCacheManager进行数据缓存操作,可以创建一个名为cacheManager的Bean组件,并在该组件中设置对应的序列化方式即可

2.自定义RedisCacheManager

java 复制代码
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

    // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
    RedisSerializer<String>strSerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

    // 解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //过时om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    //用下面的方法代替上面的方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
    jacksonSeial.setObjectMapper(om);
    // 定制缓存数据序列化方式及时效
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial))
                .disableCachingNullValues();
    RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
    return cacheManager;
}

3.效果测试

项目启动成功后,通过浏览器访问 http://localhost:8088/get/3,并重复刷新浏览器查看同一条数据信息 ,数据库只执行了一次SQL语句

缓存结果如下:

相关推荐
普if加的帕2 小时前
java Springboot使用扣子Coze实现实时音频对话智能客服
java·开发语言·人工智能·spring boot·实时音视频·智能客服
尤物程序猿2 小时前
【2025面试Java常问八股之redis】zset数据结构的实现,跳表和B+树的对比
数据结构·redis·面试
〆、风神2 小时前
Spring Boot 整合 Lock4j + Redisson 实现分布式锁实战
spring boot·分布式·后端
橘猫云计算机设计3 小时前
springboot基于hadoop的酷狗音乐爬虫大数据分析可视化系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·hadoop·spring boot·爬虫·python·数据分析·毕业设计
卓怡学长4 小时前
w304基于HTML5的民谣网站的设计与实现
java·前端·数据库·spring boot·spring·html5
冰^4 小时前
MySQL VS SQL Server:优缺点全解析
数据库·数据仓库·redis·sql·mysql·json·数据库开发
敖云岚4 小时前
【AI】SpringAI 第五弹:接入千帆大模型
java·大数据·人工智能·spring boot·后端
zru_96024 小时前
Docker 部署 Redis:快速搭建高效缓存服务
redis·缓存·docker