最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何使用Guava Cache做緩存

 更新時間:2023年11月17日 11:33:41   作者:randy.lou  
Cache在ConcurrentHashMap的基礎(chǔ)上提供了自動加載數(shù)據(jù)、清除數(shù)據(jù)、get-if-absend-compute的功能,本文給大家介紹如何使用Guava Cache做緩存,感興趣的朋友一起看看吧

1. 概述

1.1 適用場景

CacheConcurrentHashMap的基礎(chǔ)上提供了自動加載數(shù)據(jù)、清除數(shù)據(jù)、get-if-absend-compute的功能,適用場景:

  • 愿意花一些內(nèi)存來提高訪問速度
  • 緩存的數(shù)據(jù)查詢或計算代碼高昂,但是需要查詢不止一次
  • 緩存的數(shù)據(jù)在內(nèi)存中放得下,否則應(yīng)該考慮Redis、Memcached

1.2 Hello world

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build (
           new CacheLoader<Key, Graph>() {
             @Override
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           }
        );

2. 數(shù)據(jù)加載使用

2.1 CacheLoader.load(K key)

LoadingCache是包含了數(shù)據(jù)加載方式的Cache,加載方式由CacheLoader指定,CacheLoader可以簡單到只實(shí)現(xiàn)一個V load(K key)方法,如:

CacheLoader<Key,Graph> cacheLoader = new CacheLoader<Key,Graph> {
  public Grapch load(Key key) throws AnyException {
    return createExpensiveGraph(key);
  }
}

LoadingCacheCache都是通過CacheBuilder創(chuàng)建,唯一的區(qū)別是LoadingCache需要要提供CacheLoader實(shí)例。

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().maximumSize(1000).build(cacheLoader);
graphs.get(key);

LoadingCache經(jīng)典的使用方式是通過get(K)獲取數(shù)據(jù),有緩存則直接返回,否則調(diào)用CacheLoader.load(K)計算并寫入緩存。

CacheLoader可以拋出異常,檢查型異常會被封裝為ExecutionException,RuntimeException會被封裝為UncheckedExecutionException。

如果不想在客戶端代碼里處理異常,可以使用LoadingCache.getUnchecked(K)方法,該方法只會拋出UncheckedExecutionException,它是一個RuntimeException。

2.2 CacheLoader.loadAll(keys) 批量加載

在客戶端調(diào)用LoadingCache.getAll的時候,會優(yōu)先嘗試CacheLoader.loadAll(Iterable<? extends K> keys)方法,這個方法默認(rèn)實(shí)現(xiàn)是拋出UnsupportedLoadingOperationException,LocalCache默認(rèn)優(yōu)先嘗試調(diào)用ClassLoader.loadAll,如果異常則挨個Key調(diào)用CacheLoader.load(K)并組成Map<Key,Value>返回。

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("going to load from data, key:" + s);
        return s.matches("\\d+") ? Integer.parseInt(s) : -1;
    }
    @Override
    public Map<String, Integer> loadAll(Iterable<? extends String> keys) throws Exception {
        System.out.println("going to loadAll from data, keys:" + keys);
        Map<String, Integer> result = new LinkedHashMap<>();
        for (String s : keys) {
            result.put(s, s.matches("\\d+") ? Integer.parseInt(s) : -1);
        }
        result.put("99", 99);
        result.put("WhatIsTheFuck", 100);
        return result;
    }
});
System.out.println(cache.get("10"));
List<String> ls = Lists.newArrayList("1", "2", "a");
System.out.println(cache.getAll(ls));
System.out.println(cache.get("WhatIsTheFuck"));

getAll調(diào)用CacheLoader.loadAll,該方法返回一個Map,可以包含非指定Key數(shù)據(jù),整個Map會被緩存,但getAll只返回指定的Key的數(shù)據(jù)。

2.3 Callable.call

所有Guava Cache的實(shí)現(xiàn)類都支持get(K, Callable<V>)方法, 返回K對應(yīng)的緩存,或者使用Callable<V>計算新值并存入緩存,實(shí)現(xiàn)get-if-absent-compute
相同的Key如果有多個調(diào)用同時進(jìn)入,Guava保證只有一個線程在加載,且其他線程會阻塞等待加載結(jié)果。
Guava Cache內(nèi)部使用了類型ConcurrentHashMap的概念,為了將鎖分片,減少race-condition發(fā)生的范圍。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(10).build();
final String key = "2";
Integer value = cache.get(key, new Callable<Integer>() {
    public Integer call() throws Exception {
        System.out.println("Callable.call running, key:" + key);
        return key.matches("\\d+") ? Integer.parseInt(key) : -1;
    }
});
System.out.println(value);
System.out.println(value);

2.4 手工寫入

我們可以通過cache.put(key,value)直接寫入緩存,寫入會覆蓋之前的值。 也可以通過cache.asMap()視圖來操作數(shù)據(jù)。 cache.asMap()并不會促發(fā)緩存的自動加載,應(yīng)該盡可能使用cache.putcache.get。

Cache<String,Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
cache.put("1",1);
cache.put("2",2);
cache.put("3",3);
cache.put("4",4);
System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個,get("1")數(shù)據(jù)被清除,返回null
System.out.println(cache.asMap().get("2"));

3. 緩存清除

現(xiàn)實(shí)實(shí)際我們總是不可能有足夠的內(nèi)存來緩存所有數(shù)據(jù)的,你總是需要關(guān)注緩存的清除策略。

3.1 基于maximumSize的清除

用于控制緩存的大小,通過CacheBuilder.maximumSize(long),當(dāng)緩存的數(shù)據(jù)項(xiàng)解決maximum的數(shù)量時,采用類似LRU的算法過期歷史數(shù)據(jù)。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
cache.put("1", 1);
cache.put("2", 2);
cache.put("3", 3);
cache.put("4", 4);
System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個,get("1")數(shù)據(jù)被清除,返回null
System.out.println(cache.asMap().get("2"));

3.2 基于maximumWeight的清除

和maximun類似,只是統(tǒng)計的weight而不是緩存的記錄數(shù)。

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumWeight(10).weigher(new Weigher<String, Integer>() {
    public int weigh(String s, Integer integer) {
        return integer;
    }
}).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

3.3 基于時間的清除

數(shù)據(jù)寫入指定時間后過期(expireAfterWrite),也可以指定數(shù)據(jù)一段時間沒有訪問后清除(expireAfterAccess)。

final long start = System.nanoTime();
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(new CacheLoader<String, Integer>() {
    public Integer load(String s) throws Exception {
        System.out.println("loading data from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

測試基于時間的清除,緩存一個小時,然后我們真的等一個小時后來驗(yàn)證是不現(xiàn)實(shí)的,Guava提供了Ticker類用于提供模擬時鐘,返回的是時間納秒數(shù)。

下面這個實(shí)例通過自定義Ticker,讓1s變成10分鐘(*600),緩存一個小時的數(shù)據(jù),實(shí)際過6s后數(shù)據(jù)就會過期。

final long start = System.nanoTime();
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).ticker(new Ticker() {
    public long read() {
        long current = System.nanoTime();
        long diff = current - start;
        System.out.println("diff:" + (diff / 1000 / 1000 / 1000));
        long time = start + (diff * 600);
        return time;
    }
}).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading data from CacheLoader, key:" + s);
        return Integer.parseInt(s);
    }
});

3.4 使用WeakReferenct、SoftReference保存Key和Value

Guava允許設(shè)置弱引用(weak reference)和軟銀用(soft reference)來引用實(shí)際的Key、Value數(shù)據(jù)。

通過CacheBuilder.weakKeys、CacheBuilder.weakValues、CacheBuilder.softValues來運(yùn)行JVM的垃圾回收,同時帶來的問題是Cache的Key只用==來比較而不是equals,要想從Cache里取回之前的緩存,必須保存Key的Reference對象。

3.5 顯示的移除緩存

刪除單個Key、批量刪除Key、清空緩存

Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll()

3.6 緩存清除監(jiān)聽

不是太實(shí)用,并不是Key一過期就會觸發(fā)RemovalListener回調(diào),你需要再次寫入數(shù)據(jù)的時候才會觸發(fā)同一個Segment的過期,Cache.get官網(wǎng)文檔說特定條件下也會觸發(fā)清空過期數(shù)據(jù)。

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).expireAfterWrite(10, TimeUnit.SECONDS)
    .removalListener(new RemovalListener<String, Integer>() {
    public void onRemoval(RemovalNotification<String, Integer> r) {
        System.out.println("Key:" + r.getKey());
        System.out.println("Value:" + r.getValue());
        System.out.println("Cause:" + r.getCause());
    }
}).build();

4. 緩存的清除時機(jī)

Cache不會自動的清除緩存,不會在數(shù)據(jù)過期后立即就清除,只有發(fā)生寫入動作(如Cache.put)才會觸發(fā)清除動作(包括LoadingCache.get新加載數(shù)據(jù)也會清除當(dāng)前Segement過期數(shù)據(jù))。

這樣做的目的好處是不用額外維護(hù)一個線程做緩存管理動作,如果想要定期清除,開發(fā)者可以自行創(chuàng)建一個線程,定期調(diào)用Cache.cleanUp()方法。

4.1 通過refresh優(yōu)化讀取性能

LoadingCache.refresh(K)和清除緩存(eviction)不同,refresh會導(dǎo)致Cache重新加載Key對應(yīng)的值,加載期間,老的值依然可用; 而清除(eviction)之后,其他現(xiàn)在再來取值會阻塞直至新數(shù)據(jù)加載完成。

CacheLoader.reload(K,V)方法是專門處理refresh提供的方法,refresh調(diào)用后實(shí)際會調(diào)用CacheLoader.reload(K,V)方法,這個方法的第2個入?yún)?shí)際是當(dāng)前K的歷史值。

通過CacheBuilder.refreshAfterWrite(long,TimeUnit)設(shè)定,Key在寫入Cache指定時間區(qū)間后,自動刷新Key的值,而此時歷史數(shù)據(jù)仍然對外提供服務(wù)。

CacheBuilder.refreshAfterWrite(long,TimeUnit)只會在下次查詢的時候生效,你可以同時指定refreshAfterWrite和expireAfterWrite,這樣在指定的時間段過了之后,如果數(shù)據(jù)還沒有被查詢,數(shù)據(jù)會把清除。

final ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).refreshAfterWrite(3, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
    @Override
    public Integer load(String s) throws Exception {
        System.out.println("loading from load...s:" + s);
        return Integer.parseInt(s);
    }
    @Override
    public ListenableFuture<Integer> reload(final String key, final Integer oldValue) throws Exception {
        if (oldValue > 5) { // 立即返回舊值
            System.out.println("loading from reload immediate...key:" + key);
            return Futures.immediateFuture(oldValue);
        } else {
            ListenableFutureTask<Integer> fi = ListenableFutureTask.create(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println("loading from reload...key:" + key);
                    return oldValue;
                }
            });
            es.execute(fi);
            return fi;
        }
    }
});

5. 緩存性能指標(biāo)

通過調(diào)用CacheBuilder.recordStats()可以打開統(tǒng)計功能,打開功能后可以通過Cache.stats()返回統(tǒng)計信息

LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).recordStats().build(new CacheLoader<String, Integer>() {
    public Integer load(String s) throws Exception {
        return Integer.parseInt(s);
    }
});
CacheStats stats = cache.stats();
System.out.println(stats.hitRate()); // 緩存命中率
System.out.println(stats.averageLoadPenalty()); // 平均數(shù)加載時間,單位納秒
System.out.println(stats.evictionCount()); // 緩存過期數(shù)據(jù)數(shù)

6. 原理、長處和限制

LocalLoadingCache通過公式Math.min(concurrencyLevel, maxWeight / 20)計算Segment數(shù)量,數(shù)據(jù)根據(jù)key的Hash值被分散到不同的Segment中。
默認(rèn)的concurrencyLevel是4,相當(dāng)于默認(rèn)情況下Segment數(shù)量最大就是4。

LocalLoadingCache指定Capacity,默認(rèn)是16,Capacity會轉(zhuǎn)換為大于指定Capacity的最小的2冪次方。
SegmentCapacity等于Capacity/SegmentCount, 轉(zhuǎn)換為大于SegmentCapacity的最小的2冪次方。

SegmentCapacity的值指定了Segment下AtomicReferenceArray的長度,AtomicReferenceArray每一個下標(biāo)對應(yīng)一個鏈表。

SegmentCount和SegmentCapacity決定了緩存數(shù)據(jù)被切分的份數(shù),相當(dāng)于決定了查找效率。

Segment內(nèi)部還維護(hù)著writeQueue、accessQueue、recencyQueue每一次讀寫操作都會更新對應(yīng)隊(duì)列,后續(xù)expireAfterWrite、expireAfterAccess只需要順著隊(duì)列找即可,因?yàn)殛?duì)列的順序就是操作的順序, writeQueue、accessQueue是特制的隊(duì)列,只用簡單的鏈表實(shí)現(xiàn),從鏈表移除插入都很高效。

Segement還維護(hù)了keyReferenceQueue、valueReferenceQueue,他們是Java里的ReferenceQueue,當(dāng)采用WeakReference、SoftReference做為Key/Value存儲時,自動加入到keyReferenceQueue和valueReferenceQueue中,Guava處理并刪除對應(yīng)的緩存。

7. 測試代碼

package com.hujiang.track.pageview;
import com.google.common.base.Ticker;
import com.google.common.cache.*;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
public class TestCache {
    @Test
    public void testCache() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("going to load from data, key:" + s);
                return s.matches("\\d+") ? Integer.parseInt(s) : -1;
            }
            @Override
            public Map<String, Integer> loadAll(Iterable<? extends String> keys) throws Exception {
                System.out.println("going to loadAll from data, keys:" + keys);
                Map<String, Integer> result = new LinkedHashMap<>();
                for (String s : keys) {
                    result.put(s, s.matches("\\d+") ? Integer.parseInt(s) : -1);
                }
                result.put("99", 99);
                result.put("WhatIsTheFuck", 100);
                return result;
            }
        });
        System.out.println(cache.get("10"));
        System.out.println(cache.get("20"));
        System.out.println(cache.get("a0"));
        List<String> ls = Lists.newArrayList("1", "2", "a");
        System.out.println(cache.getAll(ls));
        System.out.println(cache.get("WhatIsTheFuck"));
    }
    @Test
    public void testCallable() throws ExecutionException {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(10).build();
        final String key = "2";
        Integer value = cache.get(key, new Callable<Integer>() {
            public Integer call() throws Exception {
                System.out.println("Callable.call running, key:" + key);
                return key.matches("\\d+") ? Integer.parseInt(key) : -1;
            }
        });
        System.out.println(value);
        System.out.println(value);
    }
    @Test
    public void testPut() {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).build();
        cache.put("1", 1);
        cache.put("2", 2);
        cache.put("3", 3);
        cache.put("4", 4);
        System.out.println(cache.asMap().get("1")); // 因?yàn)樽疃嗑彺?個,get("1")數(shù)據(jù)被清除,返回null
        System.out.println(cache.asMap().get("2"));
    }
    @Test
    public void testWeight() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumWeight(10).weigher(new Weigher<String, Integer>() {
            public int weigh(String s, Integer integer) {
                return integer;
            }
        }).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading from CacheLoader, key:" + s);
                return Integer.parseInt(s);
            }
        });
        cache.get("1");
        cache.get("3");
        cache.get("5");
        cache.get("1");
        cache.get("7");
        cache.get("1");
        cache.get("3");
    }
    @Test
    public void testTimeEviction() throws InterruptedException, ExecutionException {
        System.out.println("nano:" + System.nanoTime());
        System.out.println("ms  :" + System.currentTimeMillis());
        final long start = System.nanoTime();
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).ticker(new Ticker() {
            public long read() {
                long current = System.nanoTime();
                long diff = current - start;
                System.out.println("diff:" + (diff / 1000 / 1000 / 1000));
                long time = start + (diff * 600);
                return time;
            }
        }).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading data from CacheLoader, key:" + s);
                return Integer.parseInt(s);
            }
        });
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        System.out.println(cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
        TimeUnit.SECONDS.sleep(1);
        System.out.println("time:" + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())) + "-------" + cache.get("1"));
    }
    @Test
    public void testWeakKeys() {
        CacheBuilder.newBuilder().weakKeys().weakValues().build();
    }
    @Test
    public void testRemovalListener() throws InterruptedException {
        Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).expireAfterWrite(10, TimeUnit.SECONDS).removalListener(new RemovalListener<String, Integer>() {
            public void onRemoval(RemovalNotification<String, Integer> r) {
                System.out.println("Key:" + r.getKey());
                System.out.println("Value:" + r.getValue());
                System.out.println("Cause:" + r.getCause());
            }
        }).build();
        cache.put("1", 1);
        cache.put("2", 2);
        cache.put("3", 3);
        cache.put("4", 4);
        TimeUnit.SECONDS.sleep(11);
        System.out.println("get-from-cache-2:" + cache.getIfPresent("2"));
        cache.put("2", 3);
        TimeUnit.SECONDS.sleep(11);
    }
    @Test
    public void testEvict() throws ExecutionException {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(2).removalListener(new RemovalListener<String, Integer>() {
            public void onRemoval(RemovalNotification<String, Integer> r) {
                System.out.println("Key:" + r.getKey() + ", Value:" + r.getValue() + ", Cause:" + r.getCause());
            }
        }).recordStats().build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("CacheLoader.load key:" + s);
                return Integer.parseInt(s);
            }
        });
        System.out.println(cache.get("2"));
        System.out.println(cache.get("5"));
        System.out.println(cache.get("6"));
        System.out.println(cache.get("1"));
    }
    @Test
    public void testStatistics() {
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).recordStats().build(new CacheLoader<String, Integer>() {
            public Integer load(String s) throws Exception {
                return Integer.parseInt(s);
            }
        });
        CacheStats stats = cache.stats();
        System.out.println(stats.hitRate()); // 緩存命中率
        System.out.println(stats.averageLoadPenalty()); // 平均數(shù)加載時間,單位納秒
        System.out.println(stats.evictionCount()); // 緩存過期數(shù)據(jù)數(shù)
    }
    @Test
    public void testRefresh() throws ExecutionException, InterruptedException {
        final ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
        LoadingCache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(3).refreshAfterWrite(3, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
            @Override
            public Integer load(String s) throws Exception {
                System.out.println("loading from load...s:" + s);
                return Integer.parseInt(s);
            }
            @Override
            public ListenableFuture<Integer> reload(final String key, final Integer oldValue) throws Exception {
                if (oldValue > 5) { // 立即返回舊值
                    System.out.println("loading from reload immediate...key:" + key);
                    return Futures.immediateFuture(oldValue);
                } else {
                    ListenableFutureTask<Integer> fi = ListenableFutureTask.create(new Callable<Integer>() {
                        @Override
                        public Integer call() throws Exception {
                            System.out.println("loading from reload...key:" + key);
                            return oldValue;
                        }
                    });
                    es.execute(fi);
                    return fi;
                }
            }
        });
        cache.get("5");
        cache.get("6");
        TimeUnit.SECONDS.sleep(4);
        cache.get("5");
        cache.get("6");
    }
}

到此這篇關(guān)于使用Guava Cache做緩存的文章就介紹到這了,更多相關(guān)Guava Cache緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Eclipse項(xiàng)目出現(xiàn)紅色嘆號的解決方法

    Eclipse項(xiàng)目出現(xiàn)紅色嘆號的解決方法

    eclipse工程前面出現(xiàn)紅色嘆號都是由于eclipse項(xiàng)目、eclipse工程中,缺少了一些jar包等文件引起的,這篇文章主要給大家介紹了關(guān)于Eclipse項(xiàng)目出現(xiàn)紅色嘆號的解決方法,需要的朋友可以參考下
    2023-11-11
  • SpringJPA?做分頁條件查詢的代碼實(shí)踐

    SpringJPA?做分頁條件查詢的代碼實(shí)踐

    相信小伙伴們的項(xiàng)目很多都用到SpringJPA框架的吧,對于單表的增刪改查利用jpa是很方便的,但是對于條件查詢并且分頁?是不是很多小伙伴不經(jīng)常寫到,今天給大家分享SpringJPA?做分頁條件查詢的案例代碼,感興趣的朋友一起看看吧
    2024-03-03
  • Java實(shí)現(xiàn)微信紅包分配規(guī)則

    Java實(shí)現(xiàn)微信紅包分配規(guī)則

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)仿微信紅包分配規(guī)則,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-02-02
  • Java中import導(dǎo)入的用法說明

    Java中import導(dǎo)入的用法說明

    這篇文章主要介紹了Java中import導(dǎo)入的用法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • MyBatisPlus的IService接口實(shí)現(xiàn)

    MyBatisPlus的IService接口實(shí)現(xiàn)

    MyBatisPlus是一個為MyBatis提供增強(qiáng)的工具,它通過IService接口簡化了數(shù)據(jù)庫的CRUD操作,IService接口封裝了一系列常用的數(shù)據(jù)操作方法,本文就來介紹一下,感興趣的可以了解一下
    2024-10-10
  • SpringBoot整合jasypt加密配置文件敏感信息

    SpringBoot整合jasypt加密配置文件敏感信息

    在項(xiàng)目中我們需要對配置文件的一些敏感信息進(jìn)行加密處理,比如數(shù)據(jù)庫賬戶密碼,避免直接暴露出來,這種場景常常用于生產(chǎn)環(huán)境,我們不想讓開發(fā)人員知道生產(chǎn)庫的密碼,有運(yùn)維人員統(tǒng)一管理,所以本文給大家介紹了SpringBoot整合jasypt加密配置文件敏感信息
    2024-06-06
  • 一文詳解Java屬性為什么不能是is開頭的boolean

    一文詳解Java屬性為什么不能是is開頭的boolean

    在Java實(shí)體類定義中,boolean類型的屬性命名常引發(fā)爭議,阿里巴巴Java開發(fā)手冊建議避免使用is作為布爾類型屬性的前綴,原因在于當(dāng)實(shí)體類被序列化或反序列化時,基于JavaBean規(guī)范的框架可能會移除或忽略is,導(dǎo)致不一致的字段名,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • maven異常Invalid?bound?statement(not?found)的問題解決

    maven異常Invalid?bound?statement(not?found)的問題解決

    本文詳細(xì)介紹了Maven項(xiàng)目中常見的Invalidboundstatement異常及其解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-12-12
  • SpringBoot中的6種API請求參數(shù)讀取方式總結(jié)

    SpringBoot中的6種API請求參數(shù)讀取方式總結(jié)

    使用Spring Boot開發(fā)API的時候,讀取請求參數(shù)是服務(wù)端編碼中最基本的一項(xiàng)操作,Spring Boot中也提供了多種機(jī)制來滿足不同的API設(shè)計要求,通過本文,為大家總結(jié)6種常用的請求參數(shù)讀取方式,需要的朋友可以參考下
    2024-07-07
  • SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn)

    SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn)

    這篇文章主要介紹了SpringMVC MethodArgumentResolver的作用與實(shí)現(xiàn),MethodArgumentResolver采用一種策略模式,在Handler的方法被調(diào)用前,Spring MVC會自動將HTTP請求中的參數(shù)轉(zhuǎn)換成方法參數(shù)
    2023-04-04

最新評論

河间市| 潢川县| 景宁| 遵义县| 顺义区| 蛟河市| 星子县| 连平县| 罗甸县| 裕民县| 南开区| 伊吾县| 麻阳| 察雅县| 自治县| 宜昌市| 山东省| 肇源县| 定兴县| 宣化县| 墨脱县| 三穗县| 阳山县| 咸阳市| 云龙县| 安福县| 霞浦县| 固阳县| 壤塘县| 莱州市| 沽源县| 松滋市| 霍林郭勒市| 内丘县| 遂宁市| 锡林浩特市| 罗平县| 乌兰浩特市| 新龙县| 阿克| 郁南县|