成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringBoot 如何快速使用 Caffeine 緩存?

存儲 存儲軟件
引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 注解方法實現緩存。SpringCache幫我們封裝了Caffeine pom文件引入。

[[433190]]

引言

前面我們有學習Caffeine 《本地緩存性能之王Caffeine》,并且也提到SpringBoot默認使用的本地緩存也是Caffeine啦,今天我們來看看Caffeine如何與SpringBoot集成的。

集成caffeine

caffeine與SpringBoot集成有兩種方式:

  • 一種是我們直接引入 Caffeine 依賴,然后使用 Caffeine 方法實現緩存。相當于使用原生api
  • 引入 Caffeine 和 Spring Cache 依賴,使用 SpringCache 注解方法實現緩存。SpringCache幫我們封裝了Caffeine pom文件引入
  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-cache</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6.     <groupId>com.github.ben-manes.caffeine</groupId> 
  7.     <artifactId>caffeine</artifactId> 
  8.     <version>2.6.0</version> 
  9. </dependency> 

第一種方式

首先配置一個Cache,通過構造者模式構建一個Cache對象,然后后續關于緩存的增刪查都是基于這個cache對象。

  1. @Configuration 
  2. public class CacheConfig { 
  3.     @Bean 
  4.     public Cache<String, Object> caffeineCache() { 
  5.         return Caffeine.newBuilder() 
  6.                 // 設置最后一次寫入或訪問后經過固定時間過期 
  7.                 .expireAfterWrite(60, TimeUnit.SECONDS) 
  8.                 // 初始的緩存空間大小 
  9.                 .initialCapacity(100) 
  10.                 // 緩存的最大條數 
  11.                 .maximumSize(1000) 
  12.                 .build(); 
  13.     } 

第一種方式我們就一一不介紹了,基本上就是使用caffeineCache來根據你自己的業務來操作以下方法

這種方式使用的話是對代碼有侵入性的。

第二種方式

  • 需要在SpingBoot啟動類標上EnableCaching注解,這個玩意跟很多框架都一樣,比如我們肴集成dubbo也需要標上@EnableDubbole注解等。
  1. @SpringBootApplication 
  2.   @EnableCaching 
  3.   public class DemoApplication { 
  4.       public static void main(String[] args) { 
  5.           SpringApplication.run(DemoApplication.class, args); 
  6.       } 
  • 在application.yml配置我們的使用的緩存類型、過期時間、緩存策略等。
  1. spring: 
  2.   profiles: 
  3.     active: dev 
  4.   cache: 
  5.     type: CAFFEINE 
  6.     caffeine: 
  7.       spec: maximumSize=500,expireAfterAccess=600s 

如果我們不習慣使用這種方式的配置,當然我們也可以使用JavaConfig的配置方式來代替配置文件。

  1. @Configuration 
  2. public class CacheConfig { 
  3.         @Bean 
  4.         public CacheManager cacheManager() { 
  5.             CaffeineCacheManager cacheManager = new CaffeineCacheManager(); 
  6.             cacheManager.setCaffeine(Caffeine.newBuilder() 
  7.                     // 設置最后一次寫入或訪問后經過固定時間過期 
  8.                     .expireAfterAccess(600, TimeUnit.SECONDS) 
  9.                     // 初始的緩存空間大小 
  10.                     .initialCapacity(100) 
  11.                     // 緩存的最大條數 
  12.                     .maximumSize(500)); 
  13.             return cacheManager; 
  14.         } 

接下來就是代碼中如何來使用這個緩存了。

  1. @Override 
  2. @CachePut(value = "user"key = "#userDTO.id"
  3. public UserDTO save(UserDTO userDTO) { 
  4.     userRepository.save(userDTO); 
  5.     return userDTO; 
  6.  
  7. @Override 
  8. @CacheEvict(value = "user"key = "#id")//2 
  9. public void remove(Long id) { 
  10.     logger.info("刪除了id、key為" + id + "的數據緩存"); 
  11.  
  12. @Override 
  13. @Cacheable(value = "user",key = "#id"
  14. public UserDTO getUserById(Long id) { 
  15.     return userRepository.findOne(id); 

上述代碼中我們可以看到有幾個注解@CachePut、@CacheEvict、@Cacheable我們只需要在方法上標上這幾個注解,我們就能夠使用緩存了,我們分別來介紹下這幾個注解。

@Cacheable

@Cacheable它是既可以標注在類上也可以標注在方法上,當它標記在類上的時候它表述這個類上面的所有方法都會支持緩存,同樣的 當它作用在法上面時候它表示這個方法是支持緩存的。比如上面我們代碼中的getUserById這個方法第一次緩存里面沒有數據,我們會去查詢DB,但是第二次來查詢的時候就不會走DB查詢了,而是直接從緩存里面拿到結果就返回了。

value 屬性

  • @Cacheable的value屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪個Cache上的,對應Cache的名稱。

key

  • @Cacheable的key 有兩種方式一種是我們自己顯示的去指定我們的key,還有一種默認的生成策略,默認的生成策略是SimpleKeyGenerator這個類,這個生成key的方式也比較簡單我們可以看下它的源碼:
  1. public static Object generateKey(Object... params) { 
  2.         // 如果方法沒有參數 key就是一個 new SimpleKey() 
  3.   if (params.length == 0) { 
  4.    return SimpleKey.EMPTY; 
  5.   } 
  6.   // 如果方法只有一個參數 key就是當前參數 
  7.   if (params.length == 1) { 
  8.    Object param = params[0]; 
  9.    if (param != null && !param.getClass().isArray()) { 
  10.     return param; 
  11.    } 
  12.   } 
  13.   // 如果key是多個參數,key就是new SimpleKey ,不過這個SimpleKey對象的hashCode 和Equals方法是根據方法傳入的參數重寫的。 
  14.   return new SimpleKey(params); 
  15.  } 

上述代碼還是非常好理解的分為三種情況:

  • 方法沒有參數,那就new使用一個全局空的SimpleKey對象來作為key。
  • 方法就一個參數,就使用當前參數來作為key
  • 方法參數大于1個,就new一個SimpleKey對象來作為key,new 這個SimpleKey的時候用傳入的參數重寫了SimpleKey的hashCode和equals方法, 至于為啥需要重寫的原因話,就跟Map用自定義對象來作為key的時候必須要重寫hashCode和equals方法原理是一樣的,因為caffein也是借助了ConcurrentHashMap來實現,

小結

上述代碼我們可以發現默認生成key只跟我們傳入的參數有關系,如果我們有一個類里面如果存在多個沒有參數的方法,然后我們使用了默認的緩存生成策略的話,就會造成緩存丟失。或者緩存相互覆蓋,或者還有可能會發生ClassCastException 因為都是使用同一個key。比如下面這代碼就會發生異常(ClassCastException)。

  1. @Cacheable(value = "user"
  2.   public UserDTO getUser() { 
  3.       UserDTO userDTO = new UserDTO(); 
  4.       userDTO.setUserName("Java金融"); 
  5.       return userDTO; 
  6.   } 
  7.   @Cacheable(value = "user"
  8.   public UserDTO2 getUser1() { 
  9.       UserDTO2 userDTO2 = new UserDTO2(); 
  10.       userDTO2.setUserName2("javajr.cn"); 
  11.       return userDTO2; 
  12.   } 

所以一般不怎么推薦使用默認的緩存生成key的策略。如果非要用的話我們最好自己重寫一下,帶上方法名字等。類似于如下代碼:

  1. @Component 
  2. public class MyKeyGenerator extends SimpleKeyGenerator { 
  3.  
  4.     @Override 
  5.     public Object generate(Object target, Method method, Object... params) { 
  6.         Object generate = super.generate(target, method, params); 
  7.         String format = MessageFormat.format("{0}{1}{2}", method.toGenericString(), generate); 
  8.         return format; 
  9.     } 

自定義key

我們可以通過Spring的EL表達式來指定我們的key。這里的EL表達式可以使用方法參數及它們對應的屬性。使用方法參數時我們可以直接使用“#參數名”或者“#p參數index”這也是我們比較推薦的做法:

  1. @Cacheable(value="user"key="#id"
  2.  public UserDTO getUserById(Long id) { 
  3.      UserDTO userDTO = new UserDTO(); 
  4.      userDTO.setUserName("java金融"); 
  5.      return userDTO; 
  6.  } 
  7.  @Cacheable(value="user"key="#p0"
  8.  public UserDTO getUserById1(Long id) { 
  9.      return null
  10.  } 
  11.  @Cacheable(value="user"key="#userDTO.id"
  12.  public UserDTO getUserById2(UserDTO userDTO) { 
  13.      return null
  14.  } 
  15.  @Cacheable(value="user"key="#p0.id"
  16.  public UserDTO getUserById3(UserDTO userDTO) { 
  17.      return null
  18.  } 

@CachePut

@CachePut指定的屬性是和@Cacheable一樣的,但是它們兩個是有區別的,@CachePut標注的方法不會先去查詢緩存是否有值,而是每次都會先去執行該方法,然后把結果返回,并且結果也會緩存起來。

為什么是這樣的一個流程我們可以去看看它的源碼關鍵代碼就是這一行,

  1. Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); 

當我們使用方法上有@Cacheable注解的時候再contexts里面會把CacheableOperation加入進去,只有contexts.get(CacheableOperation.class)取到的內容不為空的話,才會去從緩存里面取內容,否則的話cacheHit會直接返回null。至于contexts什么時候加入CacheableOperation的話我們看下SpringCacheAnnotationParser#parseCacheAnnotations這個方法就會明白的。具體的源碼就不展示了,感興趣的可以自己去翻。

@CacheEvict

把緩存中數據刪除,用法跟前面兩個注解差不多有value和key屬性,需要注意一點的是它多了一個屬性beforeInvocation

  • beforeInvocation 這個屬性需要注意下它的默認值是false,false代表的意思是再執調用方法之前不刪除緩存,只有方法執行成功之后才會去刪除緩存。設置為true的話調用方法之前會去刪除一下緩存,方法執行成功之后還會去調用刪除緩存這樣就是雙刪了。如果方法執行異常的話就不會去刪除緩存。
  • allEntrie 是否清空所有緩存內容,默認值為 false,如果指定為 true,則方法調用后將立即清空所有緩存

@Caching

這是一個組合注解集成了上面三個注解,有三個屬性:cacheable、put和evict,分別用于來指定@Cacheable、@CachePut和@CacheEvict。

小結

第二種方式是侵入式的,它的實現原理也比較簡單就是通過切面的方法攔截器來實現,攔截所有的方法,它的核心代碼如下:看起來就跟我們的業務代碼差不了多少,感興趣的也可以去瞅一瞅。

  1. if (contexts.isSynchronized()) { 
  2.    CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next(); 
  3.    if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) { 
  4.     Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT); 
  5.     Cache cache = context.getCaches().iterator().next(); 
  6.     try { 
  7.      return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker)))); 
  8.     } 
  9.     catch (Cache.ValueRetrievalException ex) { 
  10.      // The invoker wraps any Throwable in a ThrowableWrapper instance so we 
  11.      // can just make sure that one bubbles up the stack. 
  12.      throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause(); 
  13.     } 
  14.    } 
  15.    else { 
  16.     // No caching required, only call the underlying method 
  17.     return invokeOperation(invoker); 
  18.    } 
  19.   } 
  20.  
  21.  
  22.   // Process any early evictions 
  23.   // beforeInvocation 屬性是否為true,如果是true就刪除緩存 
  24.   processCacheEvicts(contexts.get(CacheEvictOperation.class), true
  25.     CacheOperationExpressionEvaluator.NO_RESULT); 
  26.  
  27.   // Check if we have a cached item matching the conditions 
  28.   Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); 
  29.  
  30.   // Collect puts from any @Cacheable miss, if no cached item is found 
  31.   List<CachePutRequest> cachePutRequests = new LinkedList<>(); 
  32.   if (cacheHit == null) { 
  33.    collectPutRequests(contexts.get(CacheableOperation.class), 
  34.      CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); 
  35.   } 
  36.  
  37.   Object cacheValue; 
  38.   Object returnValue; 
  39.  
  40.   if (cacheHit != null && !hasCachePut(contexts)) { 
  41.    // If there are no put requests, just use the cache hit 
  42.    cacheValue = cacheHit.get(); 
  43.    returnValue = wrapCacheValue(method, cacheValue); 
  44.   } 
  45.   else { 
  46.    // Invoke the method if we don't have a cache hit 
  47.    returnValue = invokeOperation(invoker); 
  48.    cacheValue = unwrapReturnValue(returnValue); 
  49.   } 
  50.  
  51.   // Collect any explicit @CachePuts 
  52.   collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); 
  53.  
  54.   // Process any collected put requests, either from @CachePut or a @Cacheable miss 
  55.   for (CachePutRequest cachePutRequest : cachePutRequests) { 
  56.    cachePutRequest.apply(cacheValue); 
  57.   } 
  58.  
  59.   // Process any late evictions 
  60.   processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); 
  61.  
  62.   return returnValue; 
  63.  } 

結束

由于自己才疏學淺,難免會有紕漏,假如你發現了錯誤的地方,還望留言給我指出來,我會對其加以修正。

感謝您的閱讀,十分歡迎并感謝您的關注。 

站在巨人的肩膀上摘蘋果: https://www.cnblogs.com/fashflying/p/6908028.html#!comments

責任編輯:武曉燕 來源: java金融
相關推薦

2021-07-11 18:06:18

緩存過期淘汰

2024-12-03 14:38:07

CaffeineRedis二級緩存

2024-10-28 07:15:00

SpringBoot緩存預熱數據加載

2024-12-06 10:02:46

2024-12-18 17:20:07

緩存預熱緩存系統Spring

2022-03-15 08:22:31

Ehcachespring緩存

2024-07-25 14:04:16

2025-03-26 03:25:00

SpringGuavaCaffeine

2022-03-31 13:58:37

分布式SpringRedis

2012-02-08 11:01:53

HibernateJava

2025-03-20 10:50:08

RedisCaffeine緩存監控

2024-01-19 14:03:59

Redis緩存系統Spring

2023-05-05 18:38:33

多級緩存Caffeine開發

2022-03-18 13:59:46

緩存RedisCaffeine

2025-03-12 08:42:28

2024-01-03 21:50:32

緩存機制請求

2017-03-22 10:06:40

ubuntuceph集群

2018-09-12 19:46:53

數據庫MySQLRedis

2011-06-01 09:03:12

Android 緩存

2025-04-23 09:31:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久久久蜜桃一区二区 | 日韩美女一区二区三区在线观看 | 很很干很很日 | 日韩色图在线观看 | 国产欧美一级二级三级在线视频 | 人人草人人干 | 国产精品美女久久久久aⅴ国产馆 | 亚洲成人精品视频 | 国产真实乱对白精彩久久小说 | 国产日韩欧美在线观看 | 成人精品 | 偷拍自拍在线观看 | 日本免费黄色一级片 | 九九精品热| 91精品国产自产在线老师啪 | 精彩视频一区二区三区 | jlzzjlzz欧美大全 | 99久久久久久| 日韩二三区 | 亚洲精品中文字幕在线观看 | 91在线 | 欧美v在线观看 | 国内精品视频在线观看 | 国产成人网 | 国产超碰人人爽人人做人人爱 | 成人精品毛片国产亚洲av十九禁 | 久久不卡日韩美女 | 亚洲国产成人一区二区 | 亚洲免费观看视频网站 | 在线观看视频你懂得 | 国产精品成人一区二区 | 亚洲永久免费 | 免费av一区二区三区 | 日本一区二区影视 | 亚洲国产精品99久久久久久久久 | 中文字幕 视频一区 | 久久伊人影院 | 中文字幕在线观看一区 | 黄色毛片视频 | 精品国产免费人成在线观看 | 一区二区国产精品 |