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

如何進行高效的源碼閱讀:以Spring Cache擴展為例帶你搞清楚

新聞 前端
日常開發(fā)中,需要用到各種各樣的框架來實現API、系統(tǒng)的構建。作為程序員,除了會使用框架還必須要了解框架工作的原理。

摘要

日常開發(fā)中,需要用到各種各樣的框架來實現API、系統(tǒng)的構建。作為程序員,除了會使用框架還必須要了解框架工作的原理。這樣可以便于我們排查問題,和自定義的擴展。那么如何去學習框架呢。通常我們通過閱讀文檔、查看源碼,然后又很快忘記。始終不能融匯貫通。本文主要基于Spring Cache擴展為例,介紹如何進行高效的源碼閱讀。

Spring Cache的介紹

為什么以Spring Cache為例呢,原因有兩個:

  1. Spring框架是Web開發(fā)常用的框架,值得開發(fā)者去閱讀代碼,吸收思想;
  2. 緩存是企業(yè)級應用開發(fā)必不可少的,而隨著系統(tǒng)的迭代,我們可能會需要用到內存緩存、分布式緩存。那么Spring Cache作為膠水層,能夠屏蔽掉我們底層的緩存實現。

一句話解釋Spring Cache:通過注解的方式,利用AOP的思想來解放緩存的管理。

step1 查看文檔

首先通過查看官方文檔,概括了解Spring Cache。https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html

重點兩點:

1. 兩個接口抽象 Cache,CacheManager,具體的實現都是基于這兩個抽象實現。

典型的SPI機制,和eat your dog food。當需要提供接口給外部調用,首先自己內部的實現也必須基于同樣一套抽象機制。

  1. The cache abstraction does not provide an actual store and relies on abstraction materialized by the org.springframework.cache.Cache and org.springframework.cache.CacheManager interfaces. 

2. Spring Cache提供了這些緩存的實現,如果沒有一種CacheManage,或者CacheResolver,會按照指定的順序去實現。

  1. If you have not defined a bean of type CacheManager or a CacheResolver named cacheResolver (see CachingConfigurer), Spring Boot tries to detect the following providers (in the indicated order): 
  2. 1.Generic 
  3. 2.JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others) 
  4. 3.EhCache 2.x 
  5. 4.Hazelcast 
  6. 5.Infinispan 
  7. 6.Couchbase 
  8. 7.Redis 
  9. 8.Caffeine 
  10. 9.Simple 

step2 run demo

對Spring Cache有了一個大概的了解后,我們首先使用起來,跑個demo。

定義一個用戶查詢方法:

  1. @Component 
  2. public class CacheSample { 
  3.     @Cacheable(cacheNames = "users"
  4.     public Map<Long, User> getUser(final Collection<Long> userIds) { 
  5.         System.out.println("not cache"); 
  6.         final Map<Long, User> mapUser = new HashMap<>(); 
  7.         userIds.forEach(userId -> { 
  8.             mapUser.put(userId, User.builder().userId(userId).name("name").build()); 
  9.         }); 
  10.         return mapUser; 
  11.     } 

配置一個CacheManager:

  1. @Configuration 
  2. public class CacheConfig { 
  3.     @Primary 
  4.     @Bean(name = { "cacheManager" }) 
  5.     public CacheManager getCache() { 
  6.       return new ConcurrentMapCacheManager("users"); 
  7.     } 

API調用:

  1. @RestController 
  2. @RequestMapping("/api/cache"
  3. public class CacheController { 
  4.     @Autowired 
  5.     private CacheSample cacheSample; 
  6.     @GetMapping("/user/v1/1"
  7.     public List<User> getUser() { 
  8.         return cacheSample.getUser(Arrays.asList(1L,2L)).values().stream().collect(Collectors.toList()); 
  9.     } 
  10.     } 

step3 debug 查看實現

demo跑起來后,就是debug看看代碼如何實現的了。
因為直接看源代碼的,沒有調用關系,看起來會一頭霧水。通過debug能夠使你更快了解一個實現。

在這里插入圖片描述
通過debug我們會發(fā)現主要控制邏輯是在切面CacheAspectSupport
會先根據cache key找緩存數據,沒有的話put進去。

step4 實現擴展

知道如何使用Spring Cache后,我們需要進一步思考,就是如何擴展。那么帶著問題出發(fā)。比如Spring Cache不支持批量key的緩存,像上文我們舉的例子,我們希望緩存的key是userId,而不是Collection userIds。以userId為key,這樣的緩存命中率更高,存儲的成本更小。

  1. @Cacheable(cacheNames = "users"
  2.    public Map<Long, User> getUser(final Collection<Long> userIds) {} 

所以我們要實現對Spring Cache進行擴展。step3中我們已經大致了解了Spring Cache的實現。那么實現這個擴展的功能就是拆分Collection userIds,緩存命中的從緩存中獲取,沒有命中的,調用源方法。

  1. @Aspect 
  2. @Component 
  3. public class CacheExtenionAspect { 
  4.  
  5.     @Autowired 
  6.     private CacheExtensionManage cacheExtensionManage; 
  7.  
  8.     /** 
  9.      * 返回的結果中緩存命中的從緩存中獲取,沒有命中的調用原來的方法獲取 
  10.      * @param joinPoint 
  11.      * @return 
  12.      */ 
  13.     @Around("@annotation(org.springframework.cache.annotation.Cacheable)"
  14.     @SuppressWarnings("unchecked"
  15.     public Object aroundCache(final ProceedingJoinPoint joinPoint) { 
  16.      
  17.         // 修改掉Collection值,cacheResult需要重新構造一個 
  18.         args[0] = cacheResult.getMiss(); 
  19.         try { 
  20.             final Map<Object, Object> notHit = CollectionUtils.isEmpty(cacheResult.getMiss()) ? null 
  21.                     : (Map<Object, Object>) (method.invoke(target, args)); 
  22.             final Map<Object, Object> hits = cacheResult.getHit(); 
  23.             if (Objects.isNull(notHit)) { 
  24.                 return hits; 
  25.             } 
  26.             // 設置緩存 
  27.             cacheResult.getCache().putAll(notHit); 
  28.             hits.putAll(notHit); 
  29.             return hits; 
  30.     } 
  31. 然后擴展Cache,CacheManage 
  32. 重寫Cache的查找緩存方法,返回新的CacheResult 
  33.  
  34.   public static Object lookup(final CacheExtension cache, final Object key) { 
  35.         if (key instanceof Collection) { 
  36.             final Collection<Object> originalKeys = ((Collection) key); 
  37.             if (originalKeys == null || originalKeys.isEmpty()) { 
  38.                 return CacheResult.builder().cache(cache).miss( 
  39.                         Collections.emptySet()) 
  40.                         .build(); 
  41.             } 
  42.             final List<Object> keys = originalKeys.stream() 
  43.                     .filter(Objects::nonNull).collect(Collectors.toList()); 
  44.             final Map<Object, Object> hits = cache.getAll(keys); 
  45.             final Set<Object> miss = new HashSet(keys); 
  46.             miss.removeAll(hits.keySet()); 
  47.             return CacheResult.builder().cache(cache).hit(hits).miss(miss).build(); 
  48.         } 
  49.         return null
  50.     } 
  51. CacheResult就是新的緩存結果格式 
  52.  
  53.  @Builder 
  54.     @Setter 
  55.     @Getter 
  56.     static class CacheResult { 
  57.         final CacheExtension cache; 
  58.         // 命中的緩存結果 
  59.         final Map<Object, Object> hit; 
  60.         // 需要重新調用源方法的keys 
  61.         private Set<Object> miss; 
  62.     } 

然后擴展CacheManager,沒什么重寫,就是自定義一種manager類型。

為緩存指定新的CacheManager:

  1. @Primary @Bean public CacheManager getExtensionCache() { return new CacheExtensionManage("users2"); } 

完整代碼:

https://github.com/FS1360472174/javaweb/tree/master/web/src/main/java/com/fs/web/cache

總結

本文主要介紹一種源碼學習方法,純屬拋磚引玉,如果你有好的方法,歡迎分享。

責任編輯:張燕妮 來源: 博客園
相關推薦

2021-08-02 09:50:47

Vetur源碼SMART

2011-06-22 09:37:03

桌面虛擬化存儲

2020-11-16 08:37:16

MariaDB性能優(yōu)化

2020-12-16 11:09:27

JavaScript語言開發(fā)

2020-12-31 07:57:25

JVM操作代碼

2021-09-01 09:32:40

工具

2018-06-26 14:42:10

StringJava數據

2017-08-15 08:27:48

云備份問題恢復

2015-10-12 10:01:26

AndroidWindows應用Windows 10

2021-01-19 06:43:10

Netty框架網絡技術

2018-06-20 10:43:58

云端霧端霧計算

2011-03-07 17:44:59

中小企業(yè)實施虛擬化

2022-08-08 08:48:15

Go版本偽版本

2022-11-16 14:02:44

2020-04-28 17:26:04

監(jiān)督學習無監(jiān)督學習機器學習

2021-12-20 07:58:59

GitHub源碼代碼

2023-02-17 14:40:08

MySQLSQL優(yōu)化

2024-05-28 08:02:08

Vue3父組件子組件

2022-10-24 00:33:59

MySQL全局鎖行級鎖

2020-04-11 11:21:22

留存分析模型分析
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧洲免费视频 | 国产精品久久久 | 欧美五月婷婷 | 日韩精品一区二区三区免费视频 | 欧美日韩久久 | 国产乱码精品一区二区三区中文 | 美女黄网站视频免费 | 99精品国产一区二区青青牛奶 | 国产成人免费视频 | 欧美一二三区 | 热re99久久精品国99热观看 | 久久91精品 | 国产高清视频在线播放 | 一区二区三区四区不卡 | 亚洲精品一区二区三区中文字幕 | 亚洲人成人一区二区在线观看 | 日韩成人免费av | 国产香蕉视频在线播放 | 精品久久久一区 | 亚洲国产成人精品久久久国产成人一区 | 国产目拍亚洲精品99久久精品 | 国产黄色免费网站 | 99久久久久国产精品免费 | 亚洲视频区 | 日韩精品久久久久久 | 午夜精品一区二区三区免费视频 | 久久伦理电影 | 97人人干 | av网站免费看 | 神马久久久久久久久久 | 一级片在线观看 | 日韩电影免费在线观看中文字幕 | 国产精品无码久久久久 | 久久精品欧美一区二区三区不卡 | 综合一区二区三区 | 黄色激情毛片 | 精品一二区 | 久久久久久久久毛片 | 小h片免费观看久久久久 | 色婷婷亚洲一区二区三区 | 欧美九九|