1.学一招用一招
前言 绝大多数系统都是读多写少的,众所周知,内存的访问速度很快,是磁盘访问速度的数十倍,如果不使用缓存,都通过数据库访问硬盘,对于双十一这样大的交易量是不可想象的有人专门写了一篇《让 CPU 告诉你硬盘和网络到底有多慢》。
2.学会了一招
https://zhuanlan.zhihu.com/p/24726196,将磁盘、内存、网络对数据的处理速度站在人类的角度来感知表述从内存中读取 1MB 的连续数据,耗时大约为 250us,换算成人类时间是 7.5天。
3.我来教你一招
从 SSD 读取 1MB 的顺序数据,大约需要 1ms,换算成人类时间是 1个月由此可见,内存和硬盘的差距,相当于拖拉机和法拉利的差距为什么要用Caffeine 在Java本地缓存神器—Caffeine(一)。
4.一天学会一招,十天学会一套的意思是什么?
里面已经介绍了caffeine和其他缓存框架的性能对比,结果显示,caffeine的性能远远强于其他缓存框架 如果想要了解关于caffeine更多的知识,可以看Java本地缓存神器—Caffeine(一)。
5.一招又一招
Java本地缓存神器—Caffeine(二) 这两篇文章SpringBoot整合caffeine SpringBoot默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager来实现缓存,ConcurrentMapCache实质是一个ConcurrentHashMap集合对象java内置,如果需要使用caffeine,那么需要引用caffeine的依赖。
6.又学会一招
1. 配置caffeine的maven依赖org.springframework.bootspring-boot-starter-cache
7.你教我一招
com.github.ben-manes.caffeinecaffeine
8.来点教你一招
2.在SpringBoot中开启缓存@SpringBootApplication//开启缓存@EnableCaching public class DemoApplication {
9.又学了一招
publicstaticvoidmain(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
3. 通过bean装配caffeine@Configurationpublicclass CaffeineConfig { @Autowired BookDao bookDao;
@Autowired CacheLoader cacheLoader; @Bean@Primarypublic CacheManager caffeineCache() { CaffeineCacheManager cacheManager =
new CaffeineCacheManager(); Caffeine caffeine = Caffeine.newBuilder() //cache的初始容量值
.initialCapacity(10) //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不可以同时使用,
.maximumSize(100) .expireAfterAccess(5, TimeUnit.SECONDS) .removalListener((Integer key,
Object value, RemovalCause cause) -> System.out.printf(“时间:%d,Key:%d,value:%s,移除原因(%s)%n”
,System.currentTimeMillis()/1000, key,value.toString(),cause) ) //使用refreshAfterWrite必须要设置cacheLoader
.refreshAfterWrite(5, TimeUnit.SECONDS); cacheManager.setCaffeine(caffeine);
//缓存加载策略,当key不存在或者key过期之类的都可以通过CacheLoader来重新获得数据 cacheManager.setCacheLoader(cacheLoader);
return cacheManager; } @Beanpublic CacheLoader cacheLoader() { CacheLoader<
Object, Object> cacheLoader = new CacheLoader() { @OverridepublicObject load(
Object key) throws Exception { System.out.println(“重新从数据库加载数据:” + key);
return bookDao.getBookById((int)key); } // 达refreshAfterWrite所指定的时候会触发这个事件方法
@OverridepublicObject reload(Object key, Object oldValue) throws Exception { //可以在这里处理重新加载策略,本例子,没有处理重新加载,只是返回旧值。
return oldValue; } }; return cacheLoader; } }4. 定义实体对象@Getter@Setter@ToString
publicclassBookBeanimplementsSerializable{ privatestaticfinallong serialVersionUID = -6585766340444705937L
; privateint bookId; private String bookName; private String author; privatefloat price; }
5. 定义访问数据库类@Component publicclassBookDao { /** * 模拟数据库 */privatestatic Map bookMap =
new HashMap<>(); @PostConstruct privatevoidinitDB() { BookBean xiyou = new BookBean(); xiyou.setBookId(
1); xiyou.setBookName(“西游记”); xiyou.setAuthor(“吴承恩”); xiyou.setPrice(55.5f); bookMap.put(
1, xiyou); BookBean honglou = new BookBean(); honglou.setBookId(2); honglou.setBookName(
“红楼梦”); honglou.setAuthor(“曹雪芹”); honglou.setPrice(66.6f); bookMap.put(2, honglou); }
public BookBean getBookById(int bookId) { return bookMap.get(bookId); } public BookBean
update(BookBean bookBean) { bookMap.put(bookBean.getBookId(), bookBean); return bookMap.
get(bookBean.getBookId()); } public BookBean save(BookBean bookBean) { bookMap.put(bookBean.getBookId(), bookBean);
return bookMap.get(bookBean.getBookId()); } publicvoiddelete(int bookId) { bookMap.remove
(bookId); } }6. 定义服务接口publicinterfaceBookService { public BookBean getBookById(int bookId);
public BookBean updata(BookBean bookBean); public BookBean save(BookBean bookBean); public String
delete(int bookId); }7. 定义服务实现类@Service//可以在此处统一指定cacheNames的名称@CacheConfig(cacheNames = {“book”})public
classBookServiceImplimplementsBookService{ @Autowired BookDao bookDao; /** * 如果缓存存在,直接读取缓存值;如果缓存不存在,则调用目标方法,并将结果放入缓存 * value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名 * key:缓存对象存储在Map集合中的key值,非必需,默认按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:
@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值 * * @param bookId * @return */@Cacheable(cacheNames = {
“book”}, key = “#bookId”)// @Cacheable(value = “book” ,key = “targetClass + methodName +#p0”)@Override
public BookBean getBookById(int bookId) { System.out.println(“查询数据库”); return bookDao.getBookById(bookId); }
@CachePut(cacheNames = {“book”}, key = “#bookBean.bookId”)@Overridepublic BookBean updata(BookBean bookBean) { System.
out.println(“更新数据库:” + bookBean.toString()); return bookDao.update(bookBean); } @CachePut(cacheNames = {
“book”}, key = “#bookBean.bookId”)//写入缓存,key为user.id,一般该注解标注在新增方法上@Overridepublic BookBean save(BookBean bookBean) { System.
out.println(“保存至数据库:” + bookBean.toString()); return bookDao.save(bookBean); } /** * CacheEvict 用来从缓存中移除相应数据 * allEntries=true:方法调用后清空所有cacheName为book的缓存 * beforeInvocation=true:方法调用前清空所有缓存 * *
@param bookId * @return */@CacheEvict(cacheNames = {“book”})@Overridepublic String delete(int bookId) { bookDao.delete(bookId);
return”成功”; } }8. 定义controller@RestController@RequestMapping(“book”) public class BookController {
@Autowired BookService bookService; @GetMapping(“get/{bookId}”) @ResponseBody public BookBean getBook(
@PathVariable(“bookId”) int bookId) { System.out.println(“时间:”+System.currentTimeMillis()/1000
+”,查询book接口被调用”); returnbookService.getBookById(bookId); } @PostMapping(“updata”) @
ResponseBodypublicBookBeanupdata(@RequestBody BookBean bookBean) { System.out.println(“时间:”+System.currentTimeMillis()/
1000+”,更新book接口被调用”); returnbookService.updata(bookBean); } @PostMapping(“save”) @
ResponseBodypublicBookBeansave(@RequestBody BookBean bookBean) { System.out.println(“时间:”+System.currentTimeMillis()/
1000+”,保存book接口被调用”); returnbookService.save(bookBean); } @PostMapping(“delete/{bookId}”
) @ResponseBodypublicStringdelete(@PathVariable(“bookId”) int bookId) { System.out.println
(“时间:”+System.currentTimeMillis()/1000+”,删除book接口被调用”); returnbookService.delete(bookId); } }
9. 测试1.查询测试(1) 连续两次访问 http://localhost:8080/book/get/2


可以看到第二次访问的时候,是直接从缓存中查询的数据,并没有从数据库重新加载(2) 超过五秒之后再访问 http://localhost:8080/book/get/2

我们能看到会重新从数据库加载数据,并且会caffeine访问时间的回收策略(3) 测试修改对缓存的影响先连续两次访问 http://localhost:8080/book/get/2五秒之内再 调用http post方法访问 http://localhost:8080/book/updata,修改《红楼梦》价格为99.9 实体数据为:{ “bookId”: 2, “bookName”: “红楼梦”, “author”: “曹雪芹”, “price”:99.9 }
再访问http://localhost:8080/book/get/2


我们可以看到,第一次调接口查询,因为触发了回收策略,会从数据库重新加载数据;第二次调接口查询时,会从缓存中获取数据;第三次调接口修改,会更新数据库,并且更新缓存;第四次调接口查询,会直接从缓存中获取数据。
源码https://github.com/coding-hao/spring-learn/tree/main/spring_cache欢迎大家关注微信公众号:CodingTao

2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容