本文為博主原創,未經允許不得轉載:
Guava是谷歌提供的一款強大的java工具庫,里面包含了很多方便且高效的工具,在項目開發中有業務場景需要保存數據到內存當中,
且只需要保存固定時間就可以,該數據只在服務調用其他服務的時候會獲取。主要有兩個場景:1.項目中需要調用第三方服務,第三方服務
每次調用時,需要獲取第三方提供的token,,2.項目中需要校驗第三方的一些固定數據。。所以考慮用Guava的緩存類,將上述中的數據
保存到Guava中,在獲取的時候直接使用,如果沒有則獲取數據,並將其保存到Guava中。
第一步:定義Guava緩存基類,其中要實現 InitializingBean接口,這個接口為Spring提供的接口,:
import java.util.concurrent.ExecutionException; import org.apache.http.client.utils.CloneUtils; import org.springframework.beans.factory.InitializingBean; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * 〈一句話功能簡述〉<br> * guava內存緩存基類 * * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public abstract class AbstractMemoryCache<PK, T> implements InitializingBean { private LoadingCache<PK, T> cache; protected abstract CacheBuilder<Object, Object> getCacheBuilder(CacheBuilder<Object, Object> cacheBuilder); protected abstract CacheLoader<PK, T> getCacheLoader(); protected LoadingCache<PK, T> getCache() { return cache; } public T getValue(PK pk) throws Exception { try { return CloneUtils.cloneObject(this.cache.get(pk)); } catch (CloneNotSupportedException | ExecutionException e) { throw new Exception(e); } } public void setValue(PK pk, T t) { this.cache.put(pk, t); } @Override public void afterPropertiesSet() throws Exception { CacheLoader<PK, T> cacheLoader = this.getCacheLoader(); CacheBuilder<Object, Object> cacheBuilder = this.getCacheBuilder(CacheBuilder.newBuilder()); this.cache = cacheBuilder.build(cacheLoader); } }
InitializingBean接口為spring提供的一個接口,用來加載保存數據,可打開源碼看下,通過注釋可了解到該接口主要用來初始化加載數據:
package org.springframework.beans.factory; /** * Interface to be implemented by beans that need to react once all their * properties have been set by a BeanFactory: for example, to perform custom * initialization, or merely to check that all mandatory properties have been set. * * <p>An alternative to implementing InitializingBean is specifying a custom * init-method, for example in an XML bean definition. * For a list of all bean lifecycle methods, see the BeanFactory javadocs. * * @author Rod Johnson * @see BeanNameAware * @see BeanFactoryAware * @see BeanFactory * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.context.ApplicationContextAware */ public interface InitializingBean { /** * Invoked by a BeanFactory after it has set all bean properties supplied * (and satisfied BeanFactoryAware and ApplicationContextAware). * <p>This method allows the bean instance to perform initialization only * possible when all bean properties have been set and to throw an * exception in the event of misconfiguration. * @throws Exception in the event of misconfiguration (such * as failure to set an essential property) or if initialization fails. */ void afterPropertiesSet() throws Exception; }
第2步:實現基類,封裝業務數據保存和調用
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import org.springframework.stereotype.Component; /** * 〈一句話功能簡述〉<br> * 〈緩存〉 * * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) * @date 20190807 */ @Component("tokenCache") public class TokenCache extends AbstractMemoryCache<String, Map<String, Object>> { // 過期時間: 3小時 private static final int EXPIRE_SEC_TIME = 3; // 最多保存的key的數量 private static final int MAX_KEY_SIZE = 500; ·
·// 設置存儲數量和過期時間 @Override protected CacheBuilder<Object, Object> getCacheBuilder(CacheBuilder<Object, Object> cacheBuilder) { return cacheBuilder.maximumSize(MAX_KEY_SIZE).expireAfterWrite(EXPIRE_SEC_TIME, TimeUnit.HOURS); } @Override protected CacheLoader<String, Map<String, Object>> getCacheLoader() { return new CacheLoader<String, Map<String, Object>>() { @Override public Map<String, Object> load(String key) throws Exception { return new HashMap<>(); } }; } // 根據key獲取token值 public Object genToken(String key) throws Exception { return super.getValue(key).get(key); }
// 在guava中根據key緩存值 public void setCache(String key,Object token) { Map<String, Object> tokenMap = new HashMap<>(); tokenMap.put(key, token); super.setValue(key, tokenMap); } }
設置過期時間
在構建Cache對象時,可以通過CacheBuilder類的expireAfterAccess和expireAfterWrite兩個方法為緩存中的對象指定過期時間,使用`CacheBuilder`構建的緩存不會“自動”執行清理和逐出值,也不會在值到期后立即執行或逐出任何類型。相反,它在寫入操作期間執行少量維護,或者在寫入很少的情況下偶爾執行讀取操作。其中,expireAfterWrite方法指定對象被寫入到緩存后多久過期,expireAfterAccess指定對象多久沒有被訪問后過期。
第三步調用:由於在第二步類上加了spring的@Component注解,在服務啟動時會自動加載到服務中,當做bean正常調用即可。
具體學習可參考以下博客:
Guava Cache用法介紹