這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)如何通過Springboot和@Cacheable注解完成方法調(diào)用拉去緩存以及@CacheEvict清空緩存的原理是怎樣的,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比岐山網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式岐山網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋岐山地區(qū)。費用合理售后完善,十多年實體公司更值得信賴。
關(guān)于項目中使用緩存的方式各不相同,今天來完成通過@Cacheable 注解完成方法調(diào)用拉去緩存的操作內(nèi)容;首先@Cacheable 注解生成的數(shù)據(jù)類型就是key-value類型的;只是會多生成一個
項目demo下載
引入redis的start maven配置
org.springframework.boot spring-boot-starter-redis 1.4.3.RELEASE
配置springboot默認(rèn)的redis 的配置項內(nèi)容;測試使用的額的是單機環(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 是自定義的序列化方式類,通過fastJson處理 RedisConnectionFactory 是springboot默認(rèn)通過加載配置給初始化的
@Beanpublic RedisTemplateredisTemplate(@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 實現(xiàn)@Cacheable注解內(nèi)容的cacheManager 屬性 指定緩存時指定到redis中 還是我們本地的內(nèi)存中 這個里面配置的是redis中的配置 里面的Map中配置的是各個key 值緩存的時間,然后默認(rèn)的key配置時間
@Bean("redisCacheManager")@Primarypublic RedisCacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate){ RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); Mapexpires = new HashMap<>(); expires.put("user",1000L);//設(shè)置redis中指定key 的緩存時間 expires.put("khy",1000L);//設(shè)置redis中指定key 的緩存時間//.... 每個key 都可以在這里設(shè)置 redisCacheManager.setExpires(expires); redisCacheManager.setDefaultExpiration(1500);//設(shè)置默認(rèn)的key緩存失效時間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 指定對應(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(); Listcaches = new ArrayList (){ { add(concurrentMapCache);}}; simpleCacheManager.setCaches(caches);return simpleCacheManager;}
配置的是自定義的緩存key 的生成方式;
/** * 這個是設(shè)置前綴的方式 * 下面的代碼會用到的; * @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è)置(通過將傳遞的對象轉(zhuǎn)化成key然后進(jìn)行拼接使用) * * ToStringStyle.SHORT_PREFIX_STYLE * 這個是無類前綴的toString樣式,使用User實例用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();}}
通過請求本地的代碼實現(xiàn)將緩存設(shè)置到redis中 http://localhost:8080/cacheable/getSimpleString1?userName=candy http://localhost:8080/cacheable/getSimpleString1?userName=candy2 圖1.在工具中查看到緩存值是根據(jù)我們的請求參數(shù)是的值來進(jìn)行緩存的 再次去請求下面的鏈接因為緩存效果是一樣的所以不會再進(jìn)入方法中 http://localhost:8080/cacheable/getSimpleString2?userName=candy http://localhost:8080/cacheable/getSimpleString2?userName=candy2 圖2.調(diào)用下面的方法會在兩個zset緩存 http://localhost:8080/cacheable/getSimpleString3?userName=candy2
/** * 最簡單的緩存設(shè)置方式 * 請求指定參數(shù)內(nèi)容;然后緩存響應(yīng)的結(jié)果到redis緩存中去 * 當(dāng)前緩存會設(shè)置兩種緩存格式 * 一種是String 類型請求參數(shù)中的 userName的取值 value 是響應(yīng)結(jié)果 * 一種是zset格式 key =khy~keys row是userName 的值 * * 同時因為指定的value都是 khy 在 RedisCacheableConfig 中的 RedisCacheManager 配置的 khy 的緩存存活時間是1000s * 所以兩種緩存的有效時間都是1000; * * 第一次請求 http://localhost:8080/cacheable/getSimpleString1?userName=candy * 則方法中的 內(nèi)容會被打印 但是在此請求的時間 因為userName=candy參數(shù)不變則直接從緩存中獲取,不再進(jìn)入方法 * 雖然多次請求但是對應(yīng)的String 類型的緩存的candy的有效時間是不會被更新的 * * 后面請求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é)果是一致的; * 但是因為value 指定了兩種 ;所以生成2中的 zset * 分別 是 khy~keys 和 111~keys * 因為 RedisCacheableConfig 中 RedisCacheManager 配置的默認(rèn)時間是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.多請求參數(shù)的緩存的key 通過表達(dá)式獲取請求參數(shù)中的值作為key 來實現(xiàn)緩存的 http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=111 http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=1112 如果直接用對象去接受參數(shù),則通過表達(dá)式獲取對象中的某個屬性作為key http://localhost:8080/cacheable/getSimpleEntity4?userName=candy&password=1112
/** * 雖然傳遞多個請求參數(shù) 但是key 中指定的#userName 作為key值 * 則主要是根據(jù)userName的值 雖然后面的userId參數(shù)修改但是也不會進(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;}/** * 這個里面是將請求參數(shù)中的 userName 和password 組合起來作為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;}
請求通過請求下面的鏈接在redis中設(shè)置的key內(nèi)容 http://localhost:8080/prefixKey/getSimpleString1?userName=candy http://localhost:8080/prefixKey/getSimpleString1?userName=candy1
生成的key 的類型下圖 這個是在自定義的前綴生成器中
/** * 通過prefixKeyGenerator設(shè)置對應(yīng)的前綴內(nèi)容; * 通過 我們自定義的前綴類來設(shè)置對應(yīng)緩存中key的前綴內(nèi)容; * http://localhost:8080/prefixKey/getSimpleString1?userName=candy * 然后在對應(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;}/** * 帶有多個參數(shù)的通過prefixKeyGenerator來拼接key的 * 請求 http://localhost:8080/prefixKey/getSimpleString2?userName=candy&password=123 * redis設(shè)置的緩存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [candy,123] * * 請求 http://localhost:8080/prefixKey/getSimpleString2 * redis設(shè)置的緩存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [null,null] * * 請求 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;}
如果請求參數(shù)是一個對象的話 則需要toString()需要重寫方法參數(shù)對象
/** * 請求參數(shù)是指定的對象內(nèi)容; * 但是如果不對UserEntity 處理的話生成的的 key是帶有 * prefix:PrefixKeyController.getSimpleEntity:com.khy.entity.UserEntity@e1475c6 * 內(nèi)存地址的;這樣每次請求都不是同一個是有問題需要將對象的toString()方法重寫 * return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); * * 請求的是http://localhost:8080/prefixKey/getSimpleEntity * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=,password= ,id= ,age= ,createTime= ] * * 請求的是http://localhost:8080/prefixKey/getSimpleEntity?userName=candy&password=123 * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=candy,password=123,id= ,age= ,createTime= ] * @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();}}}
不管緩存中是否有值都會將當(dāng)前返回值更新到緩存中去
/** * @CachePut 注解是不管緩存里面有沒有都會執(zhí)行當(dāng)前方法并且將結(jié)果返回設(shè)置到緩存中去 * 每次請求訪問 http://localhost:8080/prefixKey/getSimpleEntity2?userName=candy&password=123 * 不管緩存里面是否包含key 都會執(zhí)行下面的方法并且設(shè)置返回值內(nèi)更新到緩存中 * prefix:PrefixKeyController.getSimpleEntity2:UserEntity[userName=candy,password=123,id=,age= ,createTime= ] * @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)前方法會將指定的緩存中的key值全部清空 清空緩存的原理主要是根據(jù) 注解屬性中的value指定的; 主要觀察我們在@Cacheable 注解中指定value 之后會在緩存中生成一個zset結(jié)構(gòu)的緩存, 每當(dāng)調(diào)用方法請求參數(shù)不同都會多一對key-value的緩存值同時zset中也會緩存對應(yīng)的key; 所以清空緩存的原理就是從zset中獲取所有要清空緩存的key然后清除之后將zset緩存也清空
/** * 清空緩存中的數(shù)據(jù)主要和value值有關(guān)系,也就是在@Cacheable注解 value屬性設(shè)置了khy * 則在緩存中生成一個zset格式的記錄里面的所有的key; 然后清除的時間是根據(jù)zset中到的元素 * 的所有元素進(jìn)行刪除也就是不管該方法userName傳遞的是不是上面方法傳遞的都會被清空 * 方法中的value可以指定多個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)注解的使用;其實除了覺得比較方便;一般項目中緩存也不會這么使用;都會有同一個工具類生成,而且都是根據(jù)我們指定數(shù)據(jù)結(jié)構(gòu)來生成的.而不是這種都當(dāng)成key-value來處理的
上述就是小編為大家分享的如何通過Springboot和@Cacheable注解完成方法調(diào)用拉去緩存以及@CacheEvict清空緩存的原理是怎樣的了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。