緩存在咱們開發中十分常見,許多框架提供了緩存機制,若是咱們本身須要實現一個緩存,該怎麼實現呢?java
如今有個需求:咱們有個配置信息,只有一份,這個信息咱們存儲到redis中:鍵的名稱爲config,值爲json字符串,好比:redis
{ "time":10, "type":1, "threshold":1000 }
假如咱們對這個config裏面的內容使用十分頻繁,可是這個配置信息更改卻不怎麼頻繁,而且這個更改不必定要實時生效,那麼咱們能夠不用每次使用這個配置信息的時候都去查詢redis,由於對redis的性能會有所影響。咱們考慮到在應用層使用緩存,將配置信息在應用層緩存起來,每隔一分鐘自動清空一下緩存,清空緩存以後,下次請求就會訪問redis,獲取最新的配置信息。固然這之間配置信息可能已經更改,更改以後到應用最近一次從redis獲取數據有一個時間間距,這段時間所使用的配置信息可能不是最新的,固然咱們能夠忍受這一點。編程
注意本次博文咱們想緩存一個對象,而不是不少數據。json
1,首先咱們假如已經有了一個查詢配置信息的方法:緩存
public MyConfig getConfig(){ return JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class); }
上面代碼只是演示,部分代碼在博文中沒有貼出。框架
當程序調用上面的getConfig方法,每次都會從redis獲取數據,如今咱們對代碼進行改造。函數式編程
2,新建一個緩存類ConfigCache:函數
public final class ConfigCache { private static MyConfig myConfig = null; private ConfigCache() { } static { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(() -> { myConfig = null; }, 0, 60, TimeUnit.SECONDS); } public static MyConfig get() { return myConfig; } public static void put(MyConfig newConfig) { myConfig = newConfig; } }
咱們定義了一個緩存類,持有一個要緩存的對象MyConfig,提供獲取和設置方法。而且在每隔60秒清空一次該MyConfig對象,這就實現了緩存對象過時時間的功能。性能
3,從新編寫查詢配置信息的代碼:優化
public MyConfig getConfig(){ MyConfig myConfig = ConfigCache.get(); if(myConfig == null){ myConfig = JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class); ConfigCache.put(myConfig); } return myConfig; }
先查詢本地緩存,若是本地緩存爲空,則從redis查詢,而且保存至本地緩存;若是本地緩存不爲空,則直接使用本地緩存。
到此上面的簡單需求咱們就實現了。
上面圖片來自《Java 8函數式編程》一書。
很顯然1.3節的內容和圖片上面的5-31節代碼相似,java8提供了computeIfAbsent 方法簡化開發。咱們能夠提供本身的computeIfAbsent 方法,而後優化代碼。
1,改造緩存類ConfigCache
public final class ConfigCache { private static MyConfig myConfig = null; private ConfigCache() { } static { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(() -> { myConfig = null; }, 0, 60, TimeUnit.SECONDS); } public static MyConfig computeIfAbsent(Supplier<MyConfig> supplier) { if (myConfig == null) { myConfig = supplier.get(); } return myConfig; } }
咱們刪除了get和put方法,新增了computeIfAbsent 方法,該方法須要一個Supplier,它提供一個MyConfig對象。
computeIfAbsent 代碼主要邏輯是若是myConifg不爲空,則返回該對象,不然經過Supplier構造一個對象給ConfigCache類的靜態屬性賦值,並返回該對象。
2,從新編寫查詢配置信息的實現的代碼:
public MyConfig getConfig(){ return ConfigCache.computeIfAbsent(JSONObject.parseObject(stringRedisTemplate.opsForValue().get("config"), MyConfig.class)); }
此時查詢配置信息的方法就很簡單了,和上面圖片改造的相似,代碼看起來也簡潔許多。