欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用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可以簡單到只實現(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實例。

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)方法,這個方法默認實現(xiàn)是拋出UnsupportedLoadingOperationException,LocalCache默認優(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的實現(xiàn)類都支持get(K, Callable<V>)方法, 返回K對應(yīng)的緩存,或者使用Callable<V>計算新值并存入緩存,實現(xiàn)get-if-absent-compute
相同的Key如果有多個調(diào)用同時進入,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")); // 因為最多緩存3個,get("1")數(shù)據(jù)被清除,返回null
System.out.println(cache.asMap().get("2"));

3. 緩存清除

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

3.1 基于maximumSize的清除

用于控制緩存的大小,通過CacheBuilder.maximumSize(long),當(dāng)緩存的數(shù)據(jù)項解決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")); // 因為最多緩存3個,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);
    }
});

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

下面這個實例通過自定義Ticker,讓1s變成10分鐘(*600),緩存一個小時的數(shù)據(jù),實際過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)來引用實際的Key、Value數(shù)據(jù)。

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

3.5 顯示的移除緩存

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

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

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

不是太實用,并不是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. 緩存的清除時機

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

這樣做的目的好處是不用額外維護一個線程做緩存管理動作,如果想要定期清除,開發(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)用后實際會調(diào)用CacheLoader.reload(K,V)方法,這個方法的第2個入?yún)嶋H是當(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中。
默認的concurrencyLevel是4,相當(dāng)于默認情況下Segment數(shù)量最大就是4。

LocalLoadingCache指定Capacity,默認是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)部還維護著writeQueue、accessQueue、recencyQueue每一次讀寫操作都會更新對應(yīng)隊列,后續(xù)expireAfterWrite、expireAfterAccess只需要順著隊列找即可,因為隊列的順序就是操作的順序, writeQueue、accessQueue是特制的隊列,只用簡單的鏈表實現(xiàn),從鏈表移除插入都很高效。

Segement還維護了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")); // 因為最多緩存3個,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)文章

  • SpringBoot2底層注解@Configuration配置類詳解

    SpringBoot2底層注解@Configuration配置類詳解

    這篇文章主要為大家介紹了SpringBoot2底層注解@Configuration配置類詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • 解析Spring中的靜態(tài)代理和動態(tài)代理

    解析Spring中的靜態(tài)代理和動態(tài)代理

    學(xué)習(xí) Spring 的過程中,不可避免要掌握代理模式。這篇文章總結(jié)一下代理模式。顧名思義,代理,就是你委托別人幫你辦事,所以代理模式也有人稱作委托模式的。比如領(lǐng)導(dǎo)要做什么事,可以委托他的秘書去幫忙做,這時就可以把秘書看做領(lǐng)導(dǎo)的代理
    2021-06-06
  • 查看java對象所占內(nèi)存大小的方法

    查看java對象所占內(nèi)存大小的方法

    這篇文章主要為大家介紹了如何查看java對象所占內(nèi)存大小的方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • JAVA查詢MongoDB的幾種方法小結(jié)

    JAVA查詢MongoDB的幾種方法小結(jié)

    本文主要介紹了JAVA查詢MongoDB的幾種方法小結(jié),通過閱讀本文,讀者可以了解如何使用Java查詢MongoDB,并在實際應(yīng)用中應(yīng)用這些技能,具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • SpringBoot3-WebClient配置與使用詳解

    SpringBoot3-WebClient配置與使用詳解

    WebClient是Spring 5引入的響應(yīng)式Web客戶端,用于執(zhí)行HTTP請求,相比傳統(tǒng)的RestTemplate,WebClient提供了非阻塞、響應(yīng)式的方式來處理HTTP請求,是Spring推薦的新一代HTTP客戶端工具,本文將詳細介紹如何在SpringBoot 3.x中配置和使用WebClient,一起看看吧
    2024-12-12
  • Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之隊列

    Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之隊列

    這篇文章主要介紹了Java隊列數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),隊列是一種特殊的線性表,只允許在表的隊頭進行刪除操作,在表的后端進行插入操作,隊列是一個有序表先進先出,想了解更多相關(guān)資料的小伙伴可以參考下面文章的詳細內(nèi)容
    2022-02-02
  • Java中圖片的常用操作代碼總結(jié)

    Java中圖片的常用操作代碼總結(jié)

    這篇文章主要為大家詳細介紹了Java中對圖片進行常用操作處理的代碼,例如生成自定義圖片、獲取圖片格式、圖片的裁剪與壓縮等,感興趣的小伙伴可以了解一下
    2022-11-11
  • java短路邏輯運算符實例用法詳解

    java短路邏輯運算符實例用法詳解

    在本篇文章里小編給大家分享的是一篇關(guān)于java短路邏輯運算符實例用法內(nèi)容,有需要的朋友們可以學(xué)習(xí)參考下。
    2021-04-04
  • Java如何獲取當(dāng)前進程ID以及所有Java進程的進程ID

    Java如何獲取當(dāng)前進程ID以及所有Java進程的進程ID

    本篇文章主要介紹了Java如何獲取當(dāng)前進程ID以及所有Java進程的進程ID,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • SpringBoot集成Redis向量數(shù)據(jù)庫實現(xiàn)相似性搜索功能

    SpringBoot集成Redis向量數(shù)據(jù)庫實現(xiàn)相似性搜索功能

    Redis?是一個開源(BSD?許可)的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲,用作數(shù)據(jù)庫、緩存、消息代理和流式處理引擎,向量檢索的核心原理是通過將文本或數(shù)據(jù)表示為高維向量,并在查詢時根據(jù)向量的相似度進行搜索,本文給大家介紹了SpringBoot集成Redis向量數(shù)據(jù)庫實現(xiàn)相似性搜索功能
    2024-09-09

最新評論