【转】 springboot整合redis-sentinel支持Cache注解
【转】 springboot整合redis-sentinel支持Cache注解
一、前提
已经存在一个redis-sentinel集群,两个哨兵分别如下:
/home/redis-sentinel-cluster/sentinel-1.conf
port 26379 dir "/data" sentinel monitor mymaster 172.16.1.11 16379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 5000 sentinel parallel-syncs mymaster 1
/home/redis-sentinel-cluster/sentinel-2.conf
port 26380 dir "/data" sentinel monitor mymaster 172.16.1.11 16379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 5000 sentinel parallel-syncs mymaster 1
二、新建maven工程:redis-sentinel-demo 最终完整工程如下:
pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.redis.sentinel.demo</groupId> <artifactId>redis-sentinel-demo</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.7</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--以下是spring整合redis的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> </project>
1、application.yml
server:
port: 8083
context-path: /
spring:
redis:
sentinel:
master: mymaster
nodes: 172.16.1.11:26379,172.16.1.11:26380
pool:
max-active: 8
max-idle: 8
max-wait: -1
min-idle: 0
database: 0
2、新建redis的工具类RedisUtil
package com.redis.sentinel.demo.util; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.util.CollectionUtils; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @author Administrator * @date 2019/03/19 */ public class RedisUtil { private RedisTemplate<String, Object> redisTemplate; public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } //=============================common============================ /** * 指定缓存失效时间 * @param key 键 * @param time 时间(秒) * @return */ public boolean expire(String key,long time){ try { if(time>0){ redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据key 获取过期时间 * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long getExpire(String key){ return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key){ try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * @param key 可以传一个值 或多个 */ @SuppressWarnings("unchecked") public void del(String... key){ if(key!=null&&key.length>0){ if(key.length==1){ redisTemplate.delete(key[0]); }else{ redisTemplate.delete(CollectionUtils.arrayToList(key)); } } } //============================String============================= /** * 普通缓存获取 * @param key 键 * @return 值 */ public Object get(String key){ return key==null?null:redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key,Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存放入并设置时间 * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key,Object value,long time){ try { if(time>0){ redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); }else{ set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 递增 * @param key 键 * @param delta 要增加几(大于0) * @return */ public long incr(String key, long delta){ if(delta<0){ throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } /** * 递减 * @param key 键 * @param delta 要减少几(小于0) * @return */ public long decr(String key, long delta){ if(delta<0){ throw new RuntimeException("递减因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } //================================Hash================================= /** * HashGet * @param key 键 不能为null * @param item 项 不能为null * @return 值 */ public Object hget(String key,String item){ return redisTemplate.opsForHash().get(key, item); } /** * 获取hashKey对应的所有值 * @param key 键 * @return 对应的多个值 */ public Map<Object,Object> hmget(String key){ return redisTemplate.opsForHash().entries(key); } /** * HashSet * @param key 键 * @param map 对应多个键值 * @return true 成功 false 失败 */ public boolean hmset(String key, Map<String,Object> map){ try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * HashSet 并设置时间 * @param key 键 * @param map 对应多个键值 * @param time 时间(秒) * @return true成功 false失败 */ public boolean hmset(String key, Map<String,Object> map, long time){ try { redisTemplate.opsForHash().putAll(key, map); if(time>0){ expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * @param key 键 * @param item 项 * @param value 值 * @return true 成功 false失败 */ public boolean hset(String key,String item,Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 向一张hash表中放入数据,如果不存在将创建 * @param key 键 * @param item 项 * @param value 值 * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 * @return true 成功 false失败 */ public boolean hset(String key,String item,Object value,long time) { try { redisTemplate.opsForHash().put(key, item, value); if(time>0){ expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除hash表中的值 * @param key 键 不能为null * @param item 项 可以使多个 不能为null */ public void hdel(String key, Object... item){ redisTemplate.opsForHash().delete(key,item); } /** * 判断hash表中是否有该项的值 * @param key 键 不能为null * @param item 项 不能为null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String item){ return redisTemplate.opsForHash().hasKey(key, item); } /** * hash递增 如果不存在,就会创建一个 并把新增后的值返回 * @param key 键 * @param item 项 * @param by 要增加几(大于0) * @return */ public double hincr(String key, String item,double by){ return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递减 * @param key 键 * @param item 项 * @param by 要减少记(小于0) * @return */ public double hdecr(String key, String item,double by){ return redisTemplate.opsForHash().increment(key, item,-by); } //============================set============================= /** * 根据key获取Set中的所有值 * @param key 键 * @return */ public Set<Object> sGet(String key){ try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 根据value从一个set中查询,是否存在 * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key,Object value){ try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将数据放入set缓存 * @param key 键 * @param values 值 可以是多个 * @return 成功个数 */ public long sSet(String key, Object...values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 将set数据放入缓存 * @param key 键 * @param time 时间(秒) * @param values 值 可以是多个 * @return 成功个数 */ public long sSetAndTime(String key,long time,Object...values) { try { Long count = redisTemplate.opsForSet().add(key, values); if(time>0) expire(key, time); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 获取set缓存的长度 * @param key 键 * @return */ public long sGetSetSize(String key){ try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 移除值为value的 * @param key 键 * @param values 值 可以是多个 * @return 移除的个数 */ public long setRemove(String key, Object ...values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } //===============================list================================= /** * 获取list缓存的内容 * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 * @return */ public List<Object> lGet(String key, long start, long end){ try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 获取list缓存的长度 * @param key 键 * @return */ public long lGetListSize(String key){ try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 * @return */ public Object lGetIndex(String key,long index){ try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @return */ public boolean lSet(String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * @param key 键 * @param value 值 * @param time 时间(秒) * @return */ public boolean lSet(String key, List<Object> value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据索引修改list中的某条数据 * @param key 键 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index,Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 用于移除键中指定的元素。接受3个参数,分别是缓存的键名,计数事件,要移除的值。计数事件可以传入的有三个值,分别是-1、0、1。 -1代表从存储容器的最右边开始,删除一个与要移除的值匹配的数据;0代表删除所有与传入值匹配的数据;1代表从存储容器的最左边开始,删除一个与要移除的值匹配的数据。 * @param key 键 * @param count * @param value 值 * @return 移除的个数 */ public void lRemove(String key,long count,Object value) { try { redisTemplate.opsForList().remove(key, count, value); } catch (Exception e) { e.printStackTrace(); } } }
3、新建RedisConfig(主要是设置实例化redisTemplate,并设置序列化,同时实例化RedisUtil)
package com.redis.sentinel.demo.config; import com.redis.sentinel.demo.util.RedisUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @author Administrator * @date 2019/03/19 */ @Configuration @EnableCaching //开启缓存,还要继承于CachingConfigurerSupport,主要是为了注解@Cacheable、@CacheEvict、@CachePut等的使用 public class RedisConfig extends CachingConfigurerSupport { /** * 注入 RedisConnectionFactory */ @Autowired RedisConnectionFactory redisConnectionFactory; /** * 实例化 RedisTemplate 对象 * * @return */ @Bean public RedisTemplate<String, Object> functionDomainRedisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initDomainRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 设置数据存入 redis 的序列化方式 * * @param redisTemplate * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); //只有设置jdk序列化,才能新类对象比如User进行存储 redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); } /** * 实例化RedisUtil * @param redisTemplate * @return */ @Bean public RedisUtil redisUtil(RedisTemplate<String, Object> redisTemplate) { RedisUtil redisUtil = new RedisUtil(); redisUtil.setRedisTemplate(redisTemplate); return redisUtil; } }
4、创建RedisTestController
package com.redis.sentinel.demo.controller; import com.redis.sentinel.demo.model.User; import com.redis.sentinel.demo.util.RedisUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Administrator * @date 2019/03/19 */ @RestController @RequestMapping("/redis-sentinel") public class RedisTestController { @Autowired private RedisUtil redisUtil; @RequestMapping(value = "/test",method = RequestMethod.POST) public void postTest(){ testCommon(); testHash(); testSet(); testList(); } private void testCommon(){ System.out.println("================================测试普通缓存=================="); System.out.println("普通缓存,存入 key01 值为value01 到期时间为5秒"); redisUtil.set("key01","value01",5); System.out.println("从redis获取key01的值:"+redisUtil.get("key01")); System.out.println("到期时间为:"+redisUtil.getExpire("key01")); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("6秒后从redis获取key01的值:"+redisUtil.get("key01")); System.out.println("key01是否存在:"+redisUtil.hasKey("key01")); } private void testHash(){ System.out.println("================================测试Hash缓存=================="); System.out.println("hash缓存,存入 key03 值为{\"name\":\"zhangsan\",\"sex\":\"man\"}"); Map<String,Object> map = new HashMap<>(); map.put("name","zhangsan"); map.put("sex","man"); redisUtil.hmset("key03",map); System.out.println("key03:"+redisUtil.hget("key03","name")+" "+redisUtil.hget("key03","sex")); redisUtil.del("key03"); } private void testSet(){ System.out.println("================================测试Set缓存=================="); System.out.println("Set缓存,将两个User放入缓存key04"); redisUtil.sSet("key04",new User("name1","man"),new User("name2","femal")); Set<Object> users = redisUtil.sGet("key04"); for(Object o:users){ User user = (User)o; System.out.println(o.toString()); } System.out.println("获取Set key04的长度:"+redisUtil.sGetSetSize("key04")); System.out.println("删除key04"); redisUtil.del("key04"); System.out.println("获取Set key04的长度:"+redisUtil.sGetSetSize("key04")); } private void testList(){ System.out.println("================================测试List缓存=================="); System.out.println("List缓存key05"); redisUtil.lSet("key05", Arrays.asList("aa","bb","cc","dd","ee","ff","gg")); System.out.println("List缓存key06"); redisUtil.lSet("key06","11"); redisUtil.lSet("key06","22"); redisUtil.lSet("key06","33"); redisUtil.lSet("key06","44"); redisUtil.lSet("key06","55"); redisUtil.lSet("key06","66"); redisUtil.lSet("key06","77"); System.out.println("以上两种方式的缓存是有区别的,注意看下面的长度"); System.out.println("输出key05的长度:"+redisUtil.lGetListSize("key05")); List<Object> list = redisUtil.lGet("key05",0,redisUtil.lGetListSize("key05")); System.out.println("输出key05的所有元素"); for(Object str:list){ System.out.println(str); } System.out.println("输出key06的长度:"+redisUtil.lGetListSize("key06")); List<Object> list1 = redisUtil.lGet("key06",0,redisUtil.lGetListSize("key06")); System.out.println("输出key06的所有元素"); for(Object str:list1){ System.out.println(str); } System.out.println("删除key06的的55"); redisUtil.lRemove("key06",1,"55"); List<Object> list2 = redisUtil.lGet("key06",0,redisUtil.lGetListSize("key06")); System.out.println("输出key06的长度:"+redisUtil.lGetListSize("key06")); System.out.println("输出key06的所有元素"); for(Object str:list2){ System.out.println(str); } redisUtil.del("key06"); redisUtil.del("key05"); } }
5、创建springboot启动类
package com.redis.sentinel.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author Administrator * @date 2019/03/19 */ @SpringBootApplication public class Application { public static void main(String[] args){ SpringApplication.run(Application.class, args); } }
6、启动程序并用postman进行测试
================================测试普通缓存================== 普通缓存,存入 key01 值为value01 到期时间为5秒 从redis获取key01的值:value01 到期时间为:4 6秒后从redis获取key01的值:null key01是否存在:false ================================测试Hash缓存================== hash缓存,存入 key03 值为{"name":"zhangsan","sex":"man"} key03:zhangsan man ================================测试Set缓存================== Set缓存,将两个User放入缓存key04 User{name='name2', sex='femal'} User{name='name1', sex='man'} 获取Set key04的长度:2 删除key04 获取Set key04的长度:0 ================================测试List缓存================== List缓存key05 List缓存key06 以上两种方式的缓存是有区别的,注意看下面的长度 输出key05的长度:1 输出key05的所有元素 [aa, bb, cc, dd, ee, ff, gg] 输出key06的长度:7 输出key06的所有元素 11 22 33 44 55 66 77 删除key06的的55 输出key06的长度:6 输出key06的所有元素 11 22 33 44 66 77
三、使用Cache注解
1、@Cacheable
@Cacheable是用来声明方法是可缓存的。将结果存储到缓存中以便后续使用相同参数调用时不需执行实际的方法。直接从缓存中取值。最简单的格式需要制定缓存名称。主要用于查询。
参数 | 解释 | 示例 |
value | 缓存的名称,必须指定至少一个 |
@Cacheable(value=”users”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
@Cacheable(value = "users", key = "#name") @Cacheable(value = "users", key = "#p0") |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
@Cacheable(value = "users",key = "#p0", condition = "#p0 != null") |
2、@CachePut
如果缓存需要更新,且不干扰方法的执行,可以使用注解@CachePut。@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
参数 | 解释 | 示例 |
value | 缓存的名称,必须指定至少一个 |
@CachePut(value=”users”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
@CachePut(value = "users", key = "#name") @CachePut(value = "users", key = "#p0") |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
@CachePut(value = "users",key = "#p0", condition = "#p0 != null") |
3、@CacheEvict
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
参数 | 解释 | 示例 |
value | 缓存的名称,必须指定至少一个 |
@CacheEvict(value=”users”) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
@CacheEvict(value = "users", key = "#name") @CacheEvict(value = "users", key = "#p0") |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
@CacheEvict(value = "users",key = "#p0", condition = "#p0 != null") |
allEntries | 缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 |
@CacheEvict(value = "users",allEntries=true) |
beforeInvocation | 缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 |
@CacheEvict(value = "users",beforeInvocation=true) |
4、@Caching
有时候我们可能组合多个Cache注解使用
以上四个个注解的示例程序如下:
在上面的程中新建RedisCachableController
package com.redis.sentinel.demo.controller; import com.redis.sentinel.demo.model.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * @author Administrator * @date 2019/03/21 */ @RestController @RequestMapping("/redis-sentinel-cache") public class RedisCachableController { private Map<String, Object> userList = new HashMap<>(); /** * 按key为user.name进行缓存 * @param user * @return */ @RequestMapping(value = "/add", method = RequestMethod.POST) @CachePut(value = "users", key = "#user.name") public User addUser(@RequestBody User user) { userList.put(user.getName(), user); return user; } /** * 组合多个Cache注解使用,按user.name和user.sex两个维度进行缓存 * @param user * @return */ @RequestMapping(value = "/add2", method = RequestMethod.POST) @Caching(put = { @CachePut(value = "users", key = "#user.name"), @CachePut(value = "users", key = "#user.sex") }) public User addUser2(@RequestBody User user) { userList.put(user.getName(), user); return user; } /** * 先从缓存查数据,如果缓存没有,则到userList里面去拿。 * @param name * @return */ @RequestMapping(value = "/query", method = RequestMethod.GET) @Cacheable(value = "users", key = "#name", condition = "#name != null") public User queryUserByName(@RequestParam(value = "name") String name) { System.out.println("如果缓存没有,从map里面获取"); User user = (User) userList.get(name); return user; } /** * 删除数据,从缓存中删除 * @param name */ @RequestMapping(value = "/del", method = RequestMethod.PUT) @CacheEvict(value = "users", key = "#name", condition = "#name != null") public void deleteUserByName(@RequestParam(value = "name") String name) { } }
启动程序,用postman进行测试
1)执行add方法,将信息存入缓存
2)执行query方法,查看console
3)执行del方法,删除缓存
4)执行query方法,查看console
5、@CacheConfig
所有的@Cacheable()里面都有一个value=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了, 所以,有了@CacheConfig这个配置,作用在类上面,可以将上面的Controller类进行改造
在上面的程中新建RedisCachableController1
package com.redis.sentinel.demo.controller; import com.redis.sentinel.demo.model.User; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * @author Administrator * @date 2019/03/21 */ @RestController @RequestMapping("/redis-sentinel-cache2") @CacheConfig(cacheNames = "users") public class RedisCachableController1 { private Map<String, Object> userList = new HashMap<>(); /** * 按key为user.name进行缓存 * @param user * @return */ @RequestMapping(value = "/add", method = RequestMethod.POST) @CachePut(key = "#user.name") public User addUser(@RequestBody User user) { userList.put(user.getName(), user); return user; } /** * 组合多个Cache注解使用,按user.name和user.sex两个维度进行缓存 * @param user * @return */ @RequestMapping(value = "/add2", method = RequestMethod.POST) @Caching(put = { @CachePut( key = "#user.name"), @CachePut( key = "#user.sex") }) public User addUser2(@RequestBody User user) { userList.put(user.getName(), user); return user; } /** * 先从缓存查数据,如果缓存没有,则到userList里面去拿。 * @param name * @return */ @RequestMapping(value = "/query", method = RequestMethod.GET) @Cacheable( key = "#p0", condition = "#p0 != null") public User queryUserByName(@RequestParam(value = "name") String name) { System.out.println("如果缓存没有,从map里面获取"); User user = (User) userList.get(name); return user; } /** * 删除数据,从缓存中删除 * @param name */ @RequestMapping(value = "/del", method = RequestMethod.PUT) @CacheEvict(key = "#name", condition = "#name != null") public void deleteUserByName(@RequestParam(value = "name") String name) { } }
SpEL表达式
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | root.methodName |
method | root对象 | 当前被调用的方法 | root.method.name |
target | root对象 | 当前被调用的目标对象 | root.target |
targetClass | root对象 | 当前被调用的目标对象类 | root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache | root.caches[0].name |
argument name | 执行上下文 | 当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数 | user.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache evict’的beforeInvocation=false) | result |