1.最简单的缓存就是使用一个list或者map来实现,但是这样做有一个问题,线程不安全,除非你每次更新list或者map缓存的时候不是更新里面的子项,而是把整个引用都更新掉,像我这样做:
定义个list类型的数据存放我的一个叫status列表的缓存,然后写一个统一的更新方法:
我每次更新的时候,都从数据库重新拉取一次数据,然后直接通过调用setStatusList()方法把这个缓存的引用替换掉,这样便不存在线程不安全的问题了。但是这样做不好,缓存的目的就是减少数据库的访问量,而我每次更新都要请求一次数据库,这并不符合我们的初衷。所以这里面可以将原来的缓存数据copy一份出来,根据变更的情况,把copy的数据更新掉,然后直接替换掉原来缓存的引用,这样做仍然可以达到线程安全的目的,同时减少了数据库的访问次数。但是这样做,仍然显得笨拙了些。为何不直接根据更新情况修改statusList呢,如果要直接更新statusList,同时又要保证线程安全,那么就应该针对statusList做一下线程安全方面的处理,也就是说我们自己需要写一个线程安全地更新statusList的方法。实际上有了java.util.concurrent这个包之后,我们便没有必要自己做这样工作了。
2.java.util.concurrent这个包里面包含了很多东西,比如ConcurrentHashMap,这是一个线程安全的hashmap,有了这个东西,直接把上面的statuList类型从list换成ConcurrentHashMap的类型,便不需要我们自己考虑线程安全的问题了,我们可以在需要更新statusList放心去更新它了。
3.到现在我们都是在使用java本身的东西去解决缓存的问题,现在我们用springboot提供的方法来解决缓存问题。
springboot提供了Cache和CacheManager等来管理不同的缓存,使用起来非常简单,现在针对我们上面的案例进行改造。
a)首先在启动类上添加@EnableCaching
b)然后service层里面添加这样的方法:
这个方法多加了一句注解,就是将查询结果缓存起来。这样,第一次调用这个方法查询的时候是从数据库拿数据,后面再查询就是从缓存里面拿数据了。
c)如果这个StatusList里面的数据被更新了,那么怎么更新这个缓存呢,方法是在涉及到更新statuslist的方法上加上CacheEvict
注解,这样当statuslist被修改了,就清空当前的缓存,让它从数据库重新获取数据并缓存起来,如图:
到这里我们实现了使用springboot自带的Cache来实现缓存,springboot支持多种缓存方式,下面我会讲一下如何使用CacheManager来管理redis缓存。
4.springboot实现redis缓存非常简单,只需要在pom里面加上依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这样,CacheManager就自动使用redis来管理缓存了,当然,需要做一下redis配置,在application.properties里面添加:
spring.redis.host=192.168.129.128
spring.redis.port=6379
spring.redis.password=
这是redis的ip和端口,我这里没有给redis设置密码,所以为空。
5.上面的做好之后,还需要定义我们自己的RedisCacheManager,因为默认的使用的是RedisTemplate<Object, Object>,这会导致我们存储复杂的实体类数据的时候会报错,重写方法如下代码:
6.这样springboot就会自动使用Redis来管理缓存了。参考下图的结果,我的redis库里面多了一条StatusListCache的数据,这个就是springboot的缓存数据。当调用添加了@CacheEvict注解的方法后,这条数据就会被清空。