说明

@Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。

属性名称 属性描述 举例
value/cacheNames 指定缓存组件的名字 @Cacheable(value = "test")
key 缓存数据使用的 key,可以用它来指定。默认是使用方法参数的值 这个 key 你可以使用 spEL 表达式来编写,如: @Cacheable(value = "test", key = "#userId+'-'+#id")
keyGenerator key 的生成器,可以自己指定 key 的生成器的组件 id key 与 keyGenerator 二选一使用
cacheManager 指定缓存管理器;或者是 cacheResolver
condition 判断条件,指定符合条件的情况下才缓存 如 condition="#id!= null",就是在id不 为null的时候触发
unless 否定,unless 指定的条件为 true,方法的返回值就不会被缓存 如 unless="#id == null",就是在结果为null的 时候触发
sync 是否使用异步模式

引入Redis的Maven依赖

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

在启动类上写上这个注解,不然缓存是不会得到支持的。


@SpringBootApplication
@EnableCaching
public class JyhInterfaceServerApplication { public static void main(String[] args) {
SpringApplication.run(JyhInterfaceServerApplication.class, args);
}
}

使用

此时使用Cacheable注解,用的都是默认的配置,存Redis的key为参数,过期时间是-1,不过期。

    @Cacheable(key = "#id")
public String get(Long id) {
assert id != null;
return "成功";
}

实际业务中,往往我们的入参比较复杂,会遇到入参是实体类对象的情况,还有缓存的超时时间,这些都需要很灵活项目才方便使用。

方案一

批量设置接口超时时间、及生成key的序列化

创建Key的序列化方案


/**
* Redis缓存时Key的序列化方案
* 主要处理StringRedisSerializer是将Object强转为String
* 这样当Object是Integer和Long进强转会报错
*/
public class CustomStringRedisSerializer implements RedisSerializer { @Override
public byte[] serialize(Object o) throws SerializationException {
if (o == null) {
return null;
}
return String.valueOf(o).getBytes(Charset.forName("UTF-8"));
} @Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null) {
return null;
}
return new String(bytes, Charset.forName("UTF-8"));
}
}

RedisConfig 配置中使用上面的序列化方案


@EnableCaching
@Configuration
public class RedisConfig { @Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory))
.cacheDefaults(this.redisCacheConfiguration(Duration.ofHours(1).getSeconds()))
.withInitialCacheConfigurations(this.initialCacheConfigurations()).build();
} /**
* 这里设置过期时间,默认是1小时,@Cacheable(value = "jyhInterface") 匹配到的就是120秒
*/
private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("jyhInterface", this.redisCacheConfiguration(120)); return redisCacheConfigurationMap;
} private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
} private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
} /**
* 配置自定义redisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
template.setConnectionFactory(connectionFactory);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(customStringRedisSerializer);
template.setHashKeySerializer(customStringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
} /**
* json序列化
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
serializer.setObjectMapper(mapper);
return serializer;
} }

方案二

除了方案一批量设置超时时间之外,我们还可以在注解上自定义超时时间

添加扩展注解类


/**
* @desc 扩展注解Cacheable 使用过期时间
* @author zhangl
* @create 2023/3/30
**/
public class CustomRedisCacheManager extends RedisCacheManager { public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
/**
* 针对@Cacheable设置缓存过期时间
* @param name
* @param cacheConfig
* @return
*/
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
// 解析TTL
if (array.length > 1) {
long ttl = Long.parseLong(array[1]);
cacheConfig = cacheConfig.entryTtl(Duration.ofMinutes(ttl)); // 注意单位我此处用的是分钟
}
return super.createRedisCache(name, cacheConfig);
}
}

修改RedisConfig

注意这里时间单位是用的分钟,方案一是用的秒

@EnableCaching
@Configuration
public class RedisConfig { /**
* 实例化自定义的缓存管理器
* @param redisTemplate
* @return
*/
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
return new CustomRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
} private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
} private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
} /**
* 配置自定义redisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
template.setConnectionFactory(connectionFactory);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(customStringRedisSerializer);
template.setHashKeySerializer(customStringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
} /**
* json序列化
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
serializer.setObjectMapper(mapper);
return serializer;
}
}

使用

这个时候我们就可以在使用注解的时候默认带上超时时间就可以拉

个人习惯,一般 @Cacheable 注解都使用在impl层的接口类上,这样其他任何Controller类调用次接口都会用到,岂不美哉。(根据自己业务需求判断)

    // 表示超时时间为5分钟
@Cacheable(value = "jyhInterface#5")
public String get(Long id) {
assert id != null;
return "成功";
}

Redis基于@Cacheable注解实现接口缓存的更多相关文章

  1. 小白的springboot之路(八)、继承Redis以及@Cacheable注解实现Redis缓存

    0.前言 在项目中,缓存作为一种高效的提升性能的手段,几乎必不可少,Redis作为其中的佼佼者被广泛应用: 一.spring boot集成Redis 1.添加依赖 <dependency> ...

  2. springboot 2.x整合redis,spring aop实现接口缓存

    pox.xml: <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  3. redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现

    本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路    现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如 ...

  4. SpringBoot2.0 基础案例(13):基于Cache注解模式,管理Redis缓存

    本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.Cache缓存简介 从Spring3开始定义Cache和Cac ...

  5. SpringBoot + redis + @Cacheable注解实现缓存清除缓存

    一.Application启动类添加注解 @EnableCaching 二.注入配置 @Bean public CacheManager cacheManager(RedisTemplate redi ...

  6. 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)

    一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...

  7. 基于Spring Cache实现二级缓存(Caffeine+Redis)

    一.聊聊什么是硬编码使用缓存? 在学习Spring Cache之前,笔者经常会硬编码的方式使用缓存. 我们来举个实际中的例子,为了提升用户信息的查询效率,我们对用户信息使用了缓存,示例代码如下: @A ...

  8. spring redis @Cacheable注解使用部分错误及无效原因

    spring redis @Cacheable注解使用部分错误及无效原因 说明:     spring项目用到redis注解无效,解决问题中遇到一堆BUG,各种搜索,看了许多错误解决方案一一测试,对于 ...

  9. 缓存篇~第七回 Redis实现基于方法签名的数据集缓存(可控更新,分布式数据缓存)

    返回目录 本篇文章可以说是第六回 Microsoft.Practices.EnterpriseLibrary.Caching实现基于方法签名的数据集缓存(可控更新,WEB端数据缓存)的续篇,事实上,有 ...

  10. 基于注解的接口限流+统一session认证

    代码心得: 一个基本的做法:对于用户身份认证做到拦截器里,针对HandlerMethod进行统一拦截认证,根据方法上的注解标识,判别是否需要身份验证,并将查找出来的User实体存入ThreadLoca ...

随机推荐

  1. 一个属性同时使用Autowired和Resource注解会发生什么?

    首发于公众号:BiggerBoy 右侧图片wx扫码关注有惊喜 欢迎关注,查看更多技术文章 如题,如果在同一个属性上使用@Autowired注解注入bean1,然后使用@Resource注解注入bean ...

  2. Cursor 老改坏代码?六哥这几招超管用!

    大家好,我是六哥!最近不少小伙伴和我吐槽,在使用Cursor时,AI老是把代码改坏,让人头疼不已.我自己也用了大几十个小时Cursor,今天就来给大家分享一些实用小窍门,教大家如何巧妙规避这类问题. ...

  3. 【Python】Python实现解压rar文件

    Python实现解压rar文件 零.需求 最近在开发一个填分数的应用,需要用到selenium,那么自然需要用到浏览器,浏览器内置到应用中,但是上传到GitCode的时候被限制了,单个文件大小只能是1 ...

  4. Flutter 2025 年产品路线图发布

    每一年 Google Flutter 团队都会发布一份产品路线图,包括 Flutter 框架和 Dart 编程语言,让开发者能够了解官方团队的优先事项,并据此做出自己的计划安排. 产品路线图也会随着客 ...

  5. JAVA基础之多线程三期--线程安全问题

    一.线程安全问题就是指:多个线程并发访问同一个资源而发生安全性的问题, 线程安全问题都是由全局变量及静态变量引起的. 若每个线程中对全局变量.静态变量只有读操作,而无写 操作,一般来说,这个全局变量是 ...

  6. TextBox输入法控制,进入输入框则启用或禁用输入法(ime),禁用后只能输入英文

    有的场景需要禁止用户打开ime模式(禁止输入法输入),所以 TextBox 支持默认属性配置,效果如下: <Window x:Class="切换输入法.MainWindow" ...

  7. 【笔记】Excel 2021|VBA删除数组中的一个元素、循环时删除一行、选择一列删除指定一行

    主要问题是循环的时候删除一行比较麻烦,因为删除了一行后,循环仍然直接访问后一行,会导致一定的异常. 文章目录 选择一列,删除指定一行 删除数组中的一个元素 方法1:利用动态数组,在循环中条件判断删除 ...

  8. sqlalchemy多对多关联

    sqlalchemy_many_to_many.py #!-*-coding:utf-8-*-from sqlalchemy import Table,Column,Integer,String,DA ...

  9. Java和C++性能大比拼

    用来运行Java语言的HotSpot VM主要是用C++语言来写的,所以我们在研究JDK时不得不去学习C++这门语言.C++和Java都是面向对象的语言,所以它们常被拿来做比较.本文将从性能的角度对比 ...

  10. Flutter对于移动端软件跨平台后台保活的技术浅析

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 18px; o ...