什么是声明式缓存 ?

Spring 框架提供一种抽象的缓存机制,且 Spring 只提供接口,不提供缓存的具体实现。所以在程序中使用时,需要有具体缓存的实现。目前支持的常见的缓存比如 JDK ConcurrentMap-based Cache、Ehcache、Redis、Caffeine Cache、Guava Cache 等。

所谓声明式缓存,即使用 Spring 框架提供的注解来使用缓存功能。这就需要知道一些常用的注解:

  • @EnableCaching

Spring 默认没有开启缓存注解支持,可以在配置类上使用该注解进行开启。

  • @Cacheable

程序在执行方法时首先会去缓存中检查 key 对应的 value 是否存在。如果存在,则方法体不再执行,直接取缓存中的 value 返回;否则执行方法体,并将方法的返回值进行缓存。@Cacheable 注解的使用倾向于减少方法的执行。

  • @CachePut

方法执行完毕之后,将返回值 update 或者 insert 到缓存中。

  • @CacheEvict

方法执行完毕之后,将缓存删除。

更多关于 Spring 框架缓存注解的说明可以参考官方文档:Spring Boot Caching

测试环境说明

  • SpringBoot版本:2.0.2.RELEASE
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.2.RELEASE</version><relativePath/>
</parent>

  • Redis 版本:3.2.9,pom 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

  • MySQL 版本:8.0.12,pom 依赖:
<!-- Java Persistence API, ORM 规范 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 数据库连接 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL 驱动, 注意, 这个需要与 MySQL 版本对应 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.12</version><scope>runtime</scope>
</dependency>

  • mock 数据 sql:
-- 测试数据库
CREATE DATABASE IF NOT EXISTS test;-- 城市信息表
CREATE TABLE IF NOT EXISTS `test`.`city_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增 id',`city` varchar(128) NOT NULL DEFAULT '' COMMENT '城市名称',`longitude` bigint(20) NOT NULL DEFAULT '0' COMMENT '经度(100倍存储)',`latitude` bigint(20) NOT NULL DEFAULT '0' COMMENT '纬度(100倍存储)',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='城市信息表';-- fake 测试数据
INSERT INTO `city_info` (`id`, `city`, `longitude`, `latitude`)
VALUES(1, '合肥', 11717, 3152),(2, '安庆', 11702, 3031),(3, '宿州', 11658, 3338);

测试代码

代码中已经给出了详细的注释说明,且使用方法也比较简单,不做过多解释。

Redis 配置

/*** <h1>Redis 配置</h1>* 继承 CachingConfigurerSupport 重写 CacheManager 和 KeyGenerator* Created by Qinyi.*/
@EnableCaching      // 启用缓存功能, 默认不启用
@Configuration
public class RedisConfig extends CachingConfigurerSupport {private final RedisConnectionFactory redisConnectionFactory;@Autowiredpublic RedisConfig(RedisConnectionFactory redisConnectionFactory) {this.redisConnectionFactory = redisConnectionFactory;}/*** <h2>配置 CacheManager: 序列化方式、过期时间</h2>* */@Overridepublic CacheManager cacheManager() {// 初始化一个 RedisCacheWriter// RedisCacheWriter 提供了对 Redis 的 set、setnx、get 等命令的访问权限// 可以由多个缓存实现共享,并负责写/读来自 Redis 的二进制数据RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);// 设置 CacheManager 的值序列化方式RedisSerializer<Object> jsonSerializer = new JdkSerializationRedisSerializer();RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);// 提供 Redis 的配置RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);// 设置默认超过期时间是 30 秒defaultCacheConfig.entryTtl(Duration.ofSeconds(30));// 初始化 RedisCacheManager 返回return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);}/*** <h2>定义 key 生成器: 类名、方法名、参数列表</h2>* 自定义 KeyGenerator 的核心思想是保证 key 不会冲突* 默认的是 SimpleKeyGenerator, 它使用方法参数组合生成的一个 key, 这里存在一个问题:*  如果2个方法, 参数是一样的. 但执行逻辑不同, 那么将会导致执行第二个方法时命中第一个方法的缓存. 所以, 通常需要自定义.* */@Overridepublic KeyGenerator keyGenerator() {return (clazz, method, args) -> {StringBuilder sb = new StringBuilder();sb.append(clazz.getClass().getName()).append("#");sb.append(method.getName()).append("(");for (Object obj : args) {sb.append(obj.toString()).append(",");}sb.deleteCharAt(sb.length() - 1);sb.append(")");return sb.toString();};}
}

城市信息实体

/*** <h1>城市信息实体</h1>* Created by Qinyi.*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "city_info")
public class CityInfo implements Serializable {/** 自增主键 */@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id", nullable = false)private Long id;/** 城市名称 */@Basic@Column(name = "city", nullable = false)private String city;/** 经度 */@Basic@Column(name = "longitude", nullable = false)private Long longitude;/** 纬度 */@Basic@Column(name = "latitude", nullable = false)private Long latitude;
}

城市信息服务接口定义

/*** <h1>城市信息服务接口定义</h1>* Created by Qinyi.*/
public interface ICityInfoService {/*** <h2>根据 id 获取城市信息</h2>* @param id 记录 id* @return {@link CityInfo}* */CityInfo getCityInfoById(Long id);/*** <h2>更新城市信息</h2>* @param newObj 新的城市信息* @return {@link CityInfo}* */CityInfo updateCityInfo(CityInfo newObj);/*** <h2>根据 id 删除城市信息</h2>* @param id 记录 id* */void deleteCityInfoById(Long id);
}

城市信息服务接口定义

/*** <h1>城市信息服务接口定义</h1>* Created by Qinyi.*/
@Slf4j
@Service
public class CityInfoServiceImpl implements ICityInfoService {/** CityInfo Dao */private final CityInfoRepository repository;@Autowiredpublic CityInfoServiceImpl(CityInfoRepository repository) {this.repository = repository;}/*** <h2>根据 id 获取城市信息</h2>* 如果不指定 key, 则会使用 KeyGenerator 来生成 key*  1. 如果指定了 key = "#id", redis 中的 key 是 city_info::1*  2. 如果使用自定义的 KeyGenerator(不指定 key), redis 中的 key 是:*      city_info::com.imooc.ad.service.impl.CityInfoServiceImpl#getCityInfoById(1)* */@Override@SuppressWarnings("all")@Cacheable(cacheNames = "city_info", key = "#id")
//    @Cacheable(cacheNames = "city_info")public CityInfo getCityInfoById(Long id) {log.info("get CityInfo by id: {}", id.toString());return repository.findById(id).get();}@Override@CachePut(cacheNames="city_info", key="#newObj.id")public CityInfo updateCityInfo(CityInfo newObj) {log.info("update CityInfo: {}", JSON.toJSONString(newObj));return repository.save(newObj);}@Override@CacheEvict(cacheNames = "city_info", key = "#id")public void deleteCityInfoById(Long id) {log.info("delete CityInfo by id: {}", id.toString());repository.deleteById(id);}
}

Spring 声明式缓存测试用例

/*** <h1>Spring 声明式缓存测试用例</h1>* Created by Qinyi.*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class CacheAnnotationTest {@Autowiredprivate ICityInfoService cityInfoService;/*** <h2>测试多次获取, 可以直接从缓存(Redis)中获取数据, 而不用查询数据库</h2>* */@Testpublic void testGetCityInfoById() {System.out.println(JSON.toJSONString(cityInfoService.getCityInfoById(1L)));}/*** <h2>测试更新缓存</h2>* */@Testpublic void testUpdateCityInfo() {System.out.println(JSON.toJSONString(cityInfoService.updateCityInfo(new CityInfo(1L, "合肥", 11717L, 3153L))));}/*** <h2>测试删除缓存</h2>* */@Testpublic void testDeleteCityInfoById() {cityInfoService.deleteCityInfoById(1L);}
}

执行测试用例之后,可以在 Redis 中看到自动生成的缓存 KV:

127.0.0.1:6379> keys *
1) "city_info::1"
2) "city_info::com.imooc.ad.service.impl.CityInfoServiceImpl#getCityInfoById(1)"
127.0.0.1:6379> type city_info::1
string
127.0.0.1:6379> get city_info::1
"xacxedx00x05srx00x1ccom.imooc.ad.entity.CityInfoxe2/O>xd3xe6xeexc8x02x00x04Lx00x04citytx00x12Ljava/lang/String;Lx00x02idtx00x10Ljava/lang/Long;Lx00blatitudeqx00~x00x02Lx00tlongitudeqx00~x00x02xptx00x06xe5x90x88xe8x82xa5srx00x0ejava.lang.Long;x8bxe4x90xccx8f#xdfx02x00x01Jx00x05valuexrx00x10java.lang.Numberx86xacx95x1dx0bx94xe0x8bx02x00x00xpx00x00x00x00x00x00x00x01sqx00~x00x05x00x00x00x00x00x00x0cPsqx00~x00x05x00x00x00x00x00x00-xc5"
12

注:
作者:张勤一
出处:http://www.imooc.com/article/283946

spring boot 缓存_SpringBoot 应用 Redis 声明式缓存相关推荐

  1. SpringBoot+Caffeine+Redis声明式缓存

    目录 [博客目的] [应用场景] [相关知识] [代码实践] 引入组件 配置文件 配置类 启动类 业务层 实体类 接口层 [博客目的] 记录一个项目中同时整合了Caffeine和Redis时,怎么使用 ...

  2. 搞懂分布式技术14:Spring Boot使用注解集成Redis缓存

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a724888/article/details/80785403 为了提高性能,减少数据库的压力,使用 ...

  3. Spring Boot(十一)Redis集成从Docker安装到分布式Session共享

    2019独角兽企业重金招聘Python工程师标准>>> 一.简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并 ...

  4. 为什么说dubbo的声明式缓存不好用!!!

    title: 为什么说dubbo的声明式缓存不好用!!! tags: dubbo mock zookeeper cache cluster categories: dubbo date: 2017-0 ...

  5. spring boot使用Jedis整合Redis

    文章目录 spring boot使用jedis整合redis 总结 Spring Boot整合Redis有两种方式,分别是Jedis和RedisTemplate,那么它们二者有什么区别呢? 1.Jed ...

  6. [WCF REST] 通过ASP.NET Output Caching实现声明式缓存

    ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存.对于后续针对相同资源的请求,只需要直接将缓存的H ...

  7. 电子商务商城源码 Spring Cloud、Spring Boot、Mybatis、Redis

    1. 涉及平台 Spring Cloud.Spring Boot.Mybatis.Redis 2. 核心架构 Spring Cloud.Spring Boot.Mybatis.Redis 3. 前端框 ...

  8. spring boot 注解_Spring-Boot项目中如何配置redis注解缓存?

    在pom中添加redis缓存支持依赖 <dependency><groupId>org.springframework.boot</groupId><arti ...

  9. Spring Boot细节挖掘(Redis的集成)

    Spring Boot 对 Redis 的支持已经非常完善,丰富的 API 足够我们日常的开发,这里我介绍几个最常用的供大家学习,其他 API 希望大家自己多学习,多研究,用到会去查即可. 目前有两个 ...

最新文章

  1. 建博客的原因。。。。
  2. MySQL+号的作用
  3. SpringCloud系列七:使用Ribbon实现客户端侧负载均衡
  4. 浅谈python中的一般方法、静态方法(staticmethod)和类方法(classmethod)
  5. CentOS 7主机名修改与查看命令详述
  6. Linux基础—3.Linux基础命令总结【有图有真相】
  7. nvarchar和varchar的区别
  8. ︰【】奥立诚生物科技 奥立诚生物科技研发的华龙6号蜈蚣 成养殖行业的亮点
  9. 关于如何使用DirectX11进行编程的相关问题
  10. 解决Field xxService in x.controller.x required a bean of type ‘x.service.x‘ that could no be found的方法
  11. TensorFlow-gpu使用方法
  12. vue run dev报错 缺少package.json文件、missing dev 命令 解决办法
  13. 百度二年级手工机器人_小学二年级手工科技制作方案
  14. 湖南大学计算机通信学院陈果,湖南大学考研研究生导师简介-陈果
  15. 百度智能云发布全新云智一体3.0架构,自研技术贯穿各层级
  16. Linux-基本使用
  17. 计算机主机突然断电有什么影响吗,断电对电脑硬件会产生哪些影响
  18. android培训学习班,常州android培训学习班
  19. [dpdk]rte_mempool_ops_table
  20. Redis整合SpringBoot,出现“\xac\xed\x00\x05t\x00\x03解决自定义RedisTemplate序列化

热门文章

  1. java的mock测试框架
  2. git部署与基本命令汇总
  3. vc++基于颜色直方图的图像检索,含代码
  4. .NET全栈开发工程师学习路径
  5. TLD(Tracking-Learning-Detection)学习与源码理解之(二)
  6. cout的输出格式初探
  7. 神经网络 Stanford UFLDL
  8. Nova 启动虚拟机流程解析
  9. windows installer无法启动
  10. MariaDB Spider:实现MySQL横纵向扩展的小能手