這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)如何通過(guò)Springboot和@Cacheable注解完成方法調(diào)用拉去緩存以及@CacheEvict清空緩存的原理是怎樣的,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比岐山網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式岐山網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋岐山地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴。
關(guān)于項(xiàng)目中使用緩存的方式各不相同,今天來(lái)完成通過(guò)@Cacheable 注解完成方法調(diào)用拉去緩存的操作內(nèi)容;首先@Cacheable 注解生成的數(shù)據(jù)類型就是key-value類型的;只是會(huì)多生成一個(gè)
項(xiàng)目demo下載
引入redis的start maven配置
<!-- redis 依賴 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> <version>1.4.3.RELEASE</version></dependency>
配置springboot默認(rèn)的redis 的配置項(xiàng)內(nèi)容;測(cè)試使用的額的是單機(jī)環(huán)境
spring.redis.database=0 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password=khanyu spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.timeout=5000
首先redis默認(rèn)的key和value序列化方式不太方便在工具類中展示所以使用自定義的序列化方式處理key和value值 FastjsonRedisSerializer 是自定義的序列化方式類,通過(guò)fastJson處理 RedisConnectionFactory 是springboot默認(rèn)通過(guò)加載配置給初始化的
@Beanpublic RedisTemplate<String, String> redisTemplate(@Autowired RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory);template.setKeySerializer(new StringRedisSerializer());//設(shè)置redis中緩存的key 的序列化/反序列化方式template.setValueSerializer(new FastjsonRedisSerializer());//設(shè)置redis 中value 的序列化/反序列化方式template.afterPropertiesSet();return template;}
這里配置的是RedisCacheManager 實(shí)現(xiàn)@Cacheable注解內(nèi)容的cacheManager 屬性 指定緩存時(shí)指定到redis中 還是我們本地的內(nèi)存中 這個(gè)里面配置的是redis中的配置 里面的Map中配置的是各個(gè)key 值緩存的時(shí)間,然后默認(rèn)的key配置時(shí)間
@Bean("redisCacheManager")@Primarypublic RedisCacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate){ RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); Map<String, Long> expires = new HashMap<>(); expires.put("user",1000L);//設(shè)置redis中指定key 的緩存時(shí)間 expires.put("khy",1000L);//設(shè)置redis中指定key 的緩存時(shí)間//.... 每個(gè)key 都可以在這里設(shè)置 redisCacheManager.setExpires(expires); redisCacheManager.setDefaultExpiration(1500);//設(shè)置默認(rèn)的key緩存失效時(shí)間return redisCacheManager;}
配置的是默認(rèn)的本地的緩存內(nèi)容配置方式
/** * 設(shè)置緩存到本地配置的FactoryBean * @author khy * @createTime 2020年11月18日下午3:43:47 * @return */@Beanpublic ConcurrentMapCacheFactoryBean initConcurrentMapCacheFactoryBean(){ ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName("khy");return cacheFactoryBean;}/** * @Cacheable 注解中cacheManager 指定對(duì)應(yīng)的manager類型 是本地還是依賴Redis * @author khy * @createTime 2020年11月18日下午3:46:21 * @param concurrentMapCache * @return */@Bean("simpleCacheManager")public SimpleCacheManager initLocalCacheManager(@Autowired ConcurrentMapCache concurrentMapCache){ SimpleCacheManager simpleCacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(){ { add(concurrentMapCache);}}; simpleCacheManager.setCaches(caches);return simpleCacheManager;}
配置的是自定義的緩存key 的生成方式;
/** * 這個(gè)是設(shè)置前綴的方式 * 下面的代碼會(huì)用到的; * @author khy * @createTime 2020年10月22日上午10:20:02 * @return */@Bean("prefixKeyGenerator")public PrefixKeyGenerator initPrefixKeyGenerator(){ PrefixKeyGenerator prefixKeyGenerator = new PrefixKeyGenerator("prefix");return prefixKeyGenerator;}
自定義緩存key的生成器
public class PrefixKeyGenerator extends SimpleKeyGenerator { private static final String SLOT_CHANGER = "{Qi}";private static final String COLON = ":";private String module;public PrefixKeyGenerator(String module) { this.module = module;}/** * 自定義的前綴設(shè)置(通過(guò)將傳遞的對(duì)象轉(zhuǎn)化成key然后進(jìn)行拼接使用) * * ToStringStyle.SHORT_PREFIX_STYLE * 這個(gè)是無(wú)類前綴的toString樣式,使用User實(shí)例用ToStringBuilder類輸出的結(jié)果是 */public Object generate(Object target, Method method, Object... params) { StringBuilder key = new StringBuilder(); Object result = super.generate(target, method, params);if (result instanceof String && JedisClusterCRC16.getSlot((String) result) == 0) { result = result + "{Qi}";}// 拼接的是 前綴:方法class類.方法名:result//如果不同包下面出現(xiàn)相同的class相同名稱的則需要獲取target.getClass().getName(); 全路徑的class名稱 key.append(this.module).append(":").append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":").append(result);return key.toString();}}
通過(guò)請(qǐng)求本地的代碼實(shí)現(xiàn)將緩存設(shè)置到redis中 http://localhost:8080/cacheable/getSimpleString1?userName=candy http://localhost:8080/cacheable/getSimpleString1?userName=candy2 圖1.在工具中查看到緩存值是根據(jù)我們的請(qǐng)求參數(shù)是的值來(lái)進(jìn)行緩存的 再次去請(qǐng)求下面的鏈接因?yàn)榫彺嫘Ч且粯拥乃圆粫?huì)再進(jìn)入方法中 http://localhost:8080/cacheable/getSimpleString2?userName=candy http://localhost:8080/cacheable/getSimpleString2?userName=candy2 圖2.調(diào)用下面的方法會(huì)在兩個(gè)zset緩存 http://localhost:8080/cacheable/getSimpleString3?userName=candy2
/** * 最簡(jiǎn)單的緩存設(shè)置方式 * 請(qǐng)求指定參數(shù)內(nèi)容;然后緩存響應(yīng)的結(jié)果到redis緩存中去 * 當(dāng)前緩存會(huì)設(shè)置兩種緩存格式 * 一種是String 類型請(qǐng)求參數(shù)中的 userName的取值 value 是響應(yīng)結(jié)果 * 一種是zset格式 key =khy~keys row是userName 的值 * * 同時(shí)因?yàn)橹付ǖ膙alue都是 khy 在 RedisCacheableConfig 中的 RedisCacheManager 配置的 khy 的緩存存活時(shí)間是1000s * 所以兩種緩存的有效時(shí)間都是1000; * * 第一次請(qǐng)求 http://localhost:8080/cacheable/getSimpleString1?userName=candy * 則方法中的 內(nèi)容會(huì)被打印 但是在此請(qǐng)求的時(shí)間 因?yàn)閡serName=candy參數(shù)不變則直接從緩存中獲取,不再進(jìn)入方法 * 雖然多次請(qǐng)求但是對(duì)應(yīng)的String 類型的緩存的candy的有效時(shí)間是不會(huì)被更新的 * * 后面請(qǐng)求http://localhost:8080/cacheable/getSimpleString1?userName=candy1 * 則 新插入String類型的緩存 同事更新 khy~keys 中數(shù)據(jù)新增一條; * @author khy * @createTime 2020年10月22日上午10:28:23 * @param userName * @return */@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy")public String getSimpleString1(String userName){ System.out.println("getSimpleString 進(jìn)入方法執(zhí)行了"+userName);return "返回的userName="+userName;}/** * 和getSimpleString1 返回的結(jié)果是一致的; * @author khy * @createTime 2020年10月22日上午10:52:31 * @param userName * @return */@RequestMapping("/getSimpleString2")@Cacheable(cacheManager="redisCacheManager" ,value="khy" ,key="#userName")public String getSimpleString2(String userName){ System.out.println("getSimpleString2 進(jìn)入方法執(zhí)行了"+userName);return "返回的userName="+userName;}/** * String 類型的緩存和getSimpleString1 返回的結(jié)果是一致的; * 但是因?yàn)関alue 指定了兩種 ;所以生成2中的 zset * 分別 是 khy~keys 和 111~keys * 因?yàn)?nbsp;RedisCacheableConfig 中 RedisCacheManager 配置的默認(rèn)時(shí)間是1500秒 * @author khy * @createTime 2020年10月22日上午11:00:53 * @param userName * @return */@RequestMapping("/getSimpleString3")@Cacheable(cacheManager="redisCacheManager" ,value={ "khy","111"})public String getSimpleString3(String userName){ System.out.println("getSimpleString3 進(jìn)入方法執(zhí)行了"+userName);return "返回的userName="+userName;}
2.多請(qǐng)求參數(shù)的緩存的key 通過(guò)表達(dá)式獲取請(qǐng)求參數(shù)中的值作為key 來(lái)實(shí)現(xiàn)緩存的 http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=111 http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=1112 如果直接用對(duì)象去接受參數(shù),則通過(guò)表達(dá)式獲取對(duì)象中的某個(gè)屬性作為key http://localhost:8080/cacheable/getSimpleEntity4?userName=candy&password=1112
/** * 雖然傳遞多個(gè)請(qǐng)求參數(shù) 但是key 中指定的#userName 作為key值 * 則主要是根據(jù)userName的值 雖然后面的userId參數(shù)修改但是也不會(huì)進(jìn)入當(dāng)前方法 * @author khy * @createTime 2020年10月22日上午11:39:03 * @param userId * @param userName * @return */@RequestMapping("/getSimpleString4")@Cacheable(cacheManager="redisCacheManager" ,value="khy",key="#userName")public String getSimpleString4(Integer userId,String userName){ System.out.println("getSimpleString4 進(jìn)入方法執(zhí)行了"+userName+userId);return "返回的userName="+userName;}/** * 和getSimpleString4 中的卻別在于 將參數(shù)疊加在一起處理了; * 形成的key是根據(jù)userId_userName 兩者決定的 * @author khy * @createTime 2020年10月22日下午1:36:35 * @param userId * @param userName * @return */@RequestMapping("/getSimpleString5")@Cacheable(cacheManager="redisCacheManager" ,value="khy",key="#userId+'_'+#userName")public String getSimpleString5(Integer userId,String userName){ System.out.println("getSimpleString5 進(jìn)入方法執(zhí)行了"+userName+userId);return "返回的userName="+userName;}/** * 這個(gè)里面是將請(qǐng)求參數(shù)中的 userName 和password 組合起來(lái)作為redis中的key * @author khy * @createTime 2020年11月3日上午9:18:14 * @param userEntity * @return */@RequestMapping("/getSimpleEntity4")@Cacheable(cacheManager="redisCacheManager" ,value="user",key="#userEntity.userName+'_'+#userEntity.password")public UserEntity getSimpleEntity4(UserEntity userEntity){ UserEntity cacheEntity = new UserEntity(); cacheEntity.setId(1); cacheEntity.setAge(10); cacheEntity.setUserName("小康康"); cacheEntity.setPassword("password123"); cacheEntity.setCreateTime(new Date()); System.out.println("getSimpleEntity4 進(jìn)入方法內(nèi)容");return cacheEntity;}
請(qǐng)求通過(guò)請(qǐng)求下面的鏈接在redis中設(shè)置的key內(nèi)容 http://localhost:8080/prefixKey/getSimpleString1?userName=candy http://localhost:8080/prefixKey/getSimpleString1?userName=candy1
生成的key 的類型下圖 這個(gè)是在自定義的前綴生成器中
/** * 通過(guò)prefixKeyGenerator設(shè)置對(duì)應(yīng)的前綴內(nèi)容; * 通過(guò) 我們自定義的前綴類來(lái)設(shè)置對(duì)應(yīng)緩存中key的前綴內(nèi)容; * http://localhost:8080/prefixKey/getSimpleString1?userName=candy * 然后在對(duì)應(yīng)的緩存中設(shè)置的 key是 * prefix:PrefixKeyController.getSimpleString1:candy * @author khy * @createTime 2020年11月3日上午10:02:01 * @param userName * @return */@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString1(String userName){ System.out.println("getSimpleString 進(jìn)入方法執(zhí)行了"+userName);return "返回的userName="+userName;}
@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString1(String userName){ System.out.println("getSimpleString 進(jìn)入方法執(zhí)行了"+userName);return "返回的userName="+userName;}/** * 帶有多個(gè)參數(shù)的通過(guò)prefixKeyGenerator來(lái)拼接key的 * 請(qǐng)求 http://localhost:8080/prefixKey/getSimpleString2?userName=candy&password=123 * redis設(shè)置的緩存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [candy,123] * * 請(qǐng)求 http://localhost:8080/prefixKey/getSimpleString2 * redis設(shè)置的緩存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [null,null] * * 請(qǐng)求 http://localhost:8080/prefixKey/getSimpleString2?userName=candy * redis設(shè)置的緩存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [candy,null] * * @author khy * @createTime 2020年11月3日上午10:17:58 * @param userName * @param password * @return */@RequestMapping("/getSimpleString2")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString2(String userName,String password){ System.out.println("getSimpleString2 進(jìn)入方法執(zhí)行了"+userName+"password="+password);return "返回的userName="+userName;}
如果請(qǐng)求參數(shù)是一個(gè)對(duì)象的話 則需要toString()需要重寫(xiě)方法參數(shù)對(duì)象
/** * 請(qǐng)求參數(shù)是指定的對(duì)象內(nèi)容; * 但是如果不對(duì)UserEntity 處理的話生成的的 key是帶有 * prefix:PrefixKeyController.getSimpleEntity:com.khy.entity.UserEntity@e1475c6 * 內(nèi)存地址的;這樣每次請(qǐng)求都不是同一個(gè)是有問(wèn)題需要將對(duì)象的toString()方法重寫(xiě) * return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); * * 請(qǐng)求的是http://localhost:8080/prefixKey/getSimpleEntity * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=<null>,password=<null>,id=<null>,age=<null>,createTime=<null>] * * 請(qǐng)求的是http://localhost:8080/prefixKey/getSimpleEntity?userName=candy&password=123 * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=candy,password=123,id=<null>,age=<null>,createTime=<null>] * @author khy * @createTime 2020年11月3日上午10:25:46 * @param userEntity * @return */@RequestMapping("/getSimpleEntity")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public UserEntity getSimpleEntity(UserEntity userEntity){ UserEntity cacheEntity = new UserEntity(); cacheEntity.setId(1); cacheEntity.setAge(10); cacheEntity.setUserName("小康康"); cacheEntity.setPassword("password123"); cacheEntity.setCreateTime(new Date()); System.out.println("getSimpleEntity 進(jìn)入方法內(nèi)容");return cacheEntity;}
public class UserEntity implements Serializable{ @Overridepublic String toString() { try { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);} catch (Exception var2) { var2.printStackTrace();return super.toString();}}}
不管緩存中是否有值都會(huì)將當(dāng)前返回值更新到緩存中去
/** * @CachePut 注解是不管緩存里面有沒(méi)有都會(huì)執(zhí)行當(dāng)前方法并且將結(jié)果返回設(shè)置到緩存中去 * 每次請(qǐng)求訪問(wèn) http://localhost:8080/prefixKey/getSimpleEntity2?userName=candy&password=123 * 不管緩存里面是否包含key 都會(huì)執(zhí)行下面的方法并且設(shè)置返回值內(nèi)更新到緩存中 * prefix:PrefixKeyController.getSimpleEntity2:UserEntity[userName=candy,password=123,id=<null>,age=<null>,createTime=<null>] * @author khy * @createTime 2020年11月3日上午10:54:22 * @param userEntity * @return */@RequestMapping("/getSimpleEntity2")@CachePut(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public UserEntity getSimpleEntity2(UserEntity userEntity){ UserEntity cacheEntity = new UserEntity(); cacheEntity.setId(1); cacheEntity.setAge(10); cacheEntity.setUserName("小康康"); cacheEntity.setPassword("password123"); cacheEntity.setCreateTime(new Date()); System.out.println("getSimpleEntity2 進(jìn)入方法內(nèi)容");return cacheEntity;}
執(zhí)行當(dāng)前方法會(huì)將指定的緩存中的key值全部清空 清空緩存的原理主要是根據(jù) 注解屬性中的value指定的; 主要觀察我們?cè)贎Cacheable 注解中指定value 之后會(huì)在緩存中生成一個(gè)zset結(jié)構(gòu)的緩存, 每當(dāng)調(diào)用方法請(qǐng)求參數(shù)不同都會(huì)多一對(duì)key-value的緩存值同時(shí)zset中也會(huì)緩存對(duì)應(yīng)的key; 所以清空緩存的原理就是從zset中獲取所有要清空緩存的key然后清除之后將zset緩存也清空
/** * 清空緩存中的數(shù)據(jù)主要和value值有關(guān)系,也就是在@Cacheable注解 value屬性設(shè)置了khy * 則在緩存中生成一個(gè)zset格式的記錄里面的所有的key; 然后清除的時(shí)間是根據(jù)zset中到的元素 * 的所有元素進(jìn)行刪除也就是不管該方法userName傳遞的是不是上面方法傳遞的都會(huì)被清空 * 方法中的value可以指定多個(gè)key * @author khy * @createTime 2020年11月18日上午10:34:21 * @param userName * @return */@RequestMapping("/clearSimple")@CacheEvict(cacheManager="redisCacheManager" ,value="khy",allEntries=true)public UserEntity clearSimple(String userName){ UserEntity cacheEntity = new UserEntity(); cacheEntity.setId(1); cacheEntity.setAge(10); cacheEntity.setUserName("小康康"); cacheEntity.setPassword("password123"); cacheEntity.setCreateTime(new Date()); System.out.println("清空緩存功能方法已執(zhí)行");return cacheEntity;}
以上就是@Cacheable和相關(guān)注解的使用;其實(shí)除了覺(jué)得比較方便;一般項(xiàng)目中緩存也不會(huì)這么使用;都會(huì)有同一個(gè)工具類生成,而且都是根據(jù)我們指定數(shù)據(jù)結(jié)構(gòu)來(lái)生成的.而不是這種都當(dāng)成key-value來(lái)處理的
上述就是小編為大家分享的如何通過(guò)Springboot和@Cacheable注解完成方法調(diào)用拉去緩存以及@CacheEvict清空緩存的原理是怎樣的了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章標(biāo)題:如何通過(guò)Springboot和@Cacheable注解完成方法調(diào)用拉去緩存以及@CacheEvict清空緩存的原理是怎樣的
文章源于:http://www.rwnh.cn/article24/ggopje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開(kāi)發(fā)、、全網(wǎng)營(yíng)銷推廣、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)