Guava地址:https://github.com/google/guavahtml
第一次接觸我是在16年春github上,當時在找單機查緩存方法,google guava當初取名是由於JAVA的類庫很差用,因此谷歌工程師本身開發一套,想着google出品必屬精品理念,咱們一塊兒來了解一下。java
guava在還沒作分佈式處理上,單機單整合上大行其道。因此guava在程序性能優化上下了很多的工夫,咱們稱其爲單塊架構的利器git
我認爲強大的有幾點:1.集合處理 2.EventBus消息總線處理 3.guava cache 單機緩存處理 4.併發listenableFutrue回調處理,如下是全部的功能:github
讓使用Java語言變得更溫馨web
1.1 使用和避免null:null是模棱兩可的,會引發使人困惑的錯誤,有些時候它讓人很不舒服。不少Guava工具類用快速失敗拒絕null值,而不是盲目地接受spring
1.2 前置條件: 讓方法中的條件檢查更簡單sql
1.3 常見Object方法: 簡化Object方法實現,如hashCode()和toString()編程
1.4 排序: Guava強大的」流暢風格比較器」設計模式
1.5 Throwables:簡化了異常和錯誤的傳播與檢查api
Guava對JDK集合的擴展,這是Guava最成熟和爲人所知的部分
2.1 不可變集合: 用不變的集合進行防護性編程和性能提高。
2.2 新集合類型: multisets, multimaps, tables, bidirectional maps等
2.3 強大的集合工具類: 提供java.util.Collections中沒有的集合工具
2.4 擴展工具類:讓實現和擴展集合類變得更容易,好比建立Collection的裝飾器,或實現迭代器
Guava Cache:本地緩存實現,支持多種緩存過時策略
Guava的函數式支持能夠顯著簡化代碼,但請謹慎使用它
強大而簡單的抽象,讓編寫正確的併發代碼更簡單
5.1 ListenableFuture:完成後觸發回調的Future
5.2 Service框架:抽象可開啓和關閉的服務,幫助你維護服務的狀態邏輯
很是有用的字符串工具,包括分割、鏈接、填充等操做
擴展 JDK 未提供的原生類型(如int、char)操做, 包括某些類型的無符號形式
可比較類型的區間API,包括連續和離散類型
簡化I/O尤爲是I/O流和文件的操做,針對Java5和6版本
提供比Object.hashCode()更復雜的散列實現,並提供布魯姆過濾器的實現
發佈-訂閱模式的組件通訊,但組件不須要顯式地註冊到其餘組件中
優化的、充分測試的數學工具類
Guava 的 Java 反射機制工具類
1.Guava EventBus探討
在設計模式中, 有一種叫作發佈/訂閱模式, 即某事件被髮布, 訂閱該事件的角色將自動更新。
那麼訂閱者和發佈者直接耦合, 也就是說在發佈者內要通知訂閱者說我這邊有東西發佈了, 你收一下。
Observable.just(1).subscribe(new Subsriber(){ @Override public void onCompleted() { System.out.println("onCompleted "); } @Override public void onError(Throwable arg0) { } @Override public void onNext(Long arg0) { System.out.println("onNext " + arg0); } }) 咱們能夠看到, 發佈者發佈一個數字1, 訂閱者訂閱了這個
而加入eventBus, 發佈者與生產者之間的耦合性就下降了。由於這時候咱們去管理eventbus就能夠, 發佈者只要向eventbus發送信息就能夠, 而不須要關心有多少訂閱者訂閱了此消息。模型以下
首先單塊架構就是在一個進程內, 在一個進程內, 咱們仍是但願模塊與模塊之間(功能與功能之間)是鬆耦合的,而在一個模塊中是高度內聚的, 如何下降必定的耦合, 使得代碼更加有結構, guava eventbus就是支持進程內通信的橋樑。
咱們但願在數據到來以後, 進行入庫, 同時可以對數據進行報警預測, 當發生報警了, 可以有如下幾個動做, 向手機端發送推送, 向web端發送推送, 向手機端發送短信。
在通常狀況下咱們能夠這樣實現: (僞代碼以下)
processData(data){ insertintoDB(data); //執行入庫操做 predictWarning(data); // 執行報警預測 } 在predictWarning(data)中{ if(data reaches warning line){ sendNotification2App(data); //向手機端發送推送 sendNotification2Web(data); // 向web端發送推送 sendSMS2APP(data); //手機端發送短信 } } 在這裏我不去講具體是如何向web端發送推送, 如何發送短信。主要用到第三方平臺
入庫和報警預測是沒有直接聯繫,或者是不分前後順序的, 一樣在報警模塊中, 向3個客戶端發送信息也應該是沒有聯繫的, 因此以上雖然能夠實現功能, 但不符合代碼的合理性。
應該是怎麼樣的邏輯呢? 以下圖
當數據事件觸發, 發佈到data EventBus 上, 入庫和預警分別訂閱這個eventBus, 就會觸發這兩個事件, 而在預警事件中, 將事件發送到warning EventBus 中, 由下列3個訂閱的客戶端進行發送消息。
先來說同步, 即訂閱者收到事件後依次執行, 下面都是僞代碼, 具體的入庫細節等我在這裏不提供。
@Component public class DataHandler{ @Subscribe public void handleDataPersisist(Data data){ daoImpl.insertData2Mysql(data); } @Subscribe public void predictWarning(Data data){ if(data is warning){ // pseudo code 若是預警 Warning warning = createWarningEvent(data); // 根據data建立一個Warning事件 postWarningEvent(warning) } } protected postWarningEvent(Warning warning){ EventBusManager.warningEventBus.post(warning);// 發佈到warning event 上 } @PostConstruct // 由spring 在初始化bean後執行 public void init(){ register2DataEventBus(); } // 將本身註冊到eventBus中 protected void register2DataEventBus(){ EventBusManager.dataEventBus.register(this); } } @Component public class WarningHandler{ @Subscribe public void sendNotification2AppClient(Warning warning){ JpushUtils.sendNotification(warning); } @Subscribe public void sendSMS(Warning warning){ SMSUtils.sendSMS(warning); } @Subscribe public void send2WebUsingWebSocket(Warning warning){ WebsocketUtils.sendWarning(warning); } @PostConstruct // 由spring 在初始化bean後執行 public void init(){ register2WarningEventBus(); } // 將本身註冊到eventBus中 protected void register2DataEventBus(){ EventBusManager.warningEventBus.register(this); } } /** * 管理全部的eventBus **/ public class EventBusManager{ public final static EventBus dataEventBus = new EventBus(); public final static EventBus warningEventBus = new EventBus(); } 簡化 // 咱們發現每個Handler都要進行註冊, public abstract class BaseEventBusHandler{ @PostConstruct public void init(){ register2EventBus(); } private void register2EventBus(){ getEventBus().register(this); } protected abstract EventBus getEventBus(); } 這樣在寫本身的eventBus只須要 @Component public class MyEventBus extends BaseEventBusHandler{ @Override protected abstract EventBus getEventBus(){ retrun EventBusManager.myEventBus; } } 在目前的應用場景下, 同步是咱們不但願的, 異步場景也很容易實現。 只須要將EventBus 改爲 AsyncEventBus warningEvent = new AsyncEventBus(Executors.newFixedThreadPool(1)) AsyncEventBus dataEventBus = new AsyncEventBus(Executors.newFixedThreadPool(3))
2.集合處理
1.optional空值比較
2.集合排序guava
Integer[] inumber={55,22,33}; System.out.println(new Ordering<Integer>(){ @Override public int compare(Integer left, Integer right) { return left-right; } }.sortedCopy(Arrays.asList(inumber)));
//java 須要自定義compare
3.guava cache 緩存觸發機制
業務場景,當某一個文件保留的有效期30分鐘後刪除;某一個文件容易超過必定限定。
public class GuavaCacheTest { public static void main(String[] args) { Cache<Integer, String> cache = CacheBuilder.newBuilder().maximumSize(2).build(); cache.put(1, "a"); cache.put(2, "b"); cache.put(3, "c"); System.out.println(cache.asMap()); System.out.println(cache.getIfPresent(2)); cache.put(4, "d"); System.out.println(cache.asMap()); } }
guava 提供兩種定時回收的方法
expireAfterAccess(long, TimeUnit):緩存項在給定時間內沒有被讀/寫訪問,則回收。請注意這種緩存的回收順序和基於大小回收同樣。
expireAfterWrite(long, TimeUnit):緩存項在給定時間內沒有被寫訪問(建立或覆蓋),則回收。若是認爲緩存數據老是在固定時候後變得陳舊不可用,這種回收方式是可取的。
public class GuavaCacheTest { public static void main(String[] args) { LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() { @Override public void onRemoval(RemovalNotification<Object, Object> notification) { System.out.println("remove key[" + notification.getKey() + "],value[" + notification.getValue() + "],remove reason[" + notification.getCause() + "]"); } }).recordStats().build( new CacheLoader<Integer, Integer>() { @Override public Integer load(Integer key) throws Exception { return 2; } } ); cache.put(1, 1); cache.put(2, 2); System.out.println(cache.asMap()); cache.invalidateAll(); System.out.println(cache.asMap()); cache.put(3, 3); try { System.out.println(cache.getUnchecked(3)); Thread.sleep(4000); System.out.println(cache.getUnchecked(3)); } catch (InterruptedException e) { e.printStackTrace(); } } }
Cache<Integer, Integer> cache = CacheBuilder.newBuilder().maximumSize(100).expireAfterAccess(3, TimeUnit.SECONDS).build();
4.ListenableFuture 異步回調通知
傳統JDK中的Future經過異步的方式計算返回結果:在多線程運算中可能或者可能在沒有結束返回結果,Future是運行中的多線程的一個引用句柄,確保在服務執行返回一個Result。
ListenableFuture能夠容許你註冊回調方法(callbacks),在運算(多線程執行)完成的時候進行調用, 或者在運算(多線程執行)完成後當即執行。這樣簡單的改進,使得能夠明顯的支持更多的操做,這樣的功能在JDK concurrent中的Future是不支持的。
ListenableFuture 中的基礎方法是addListener(Runnable, Executor), 該方法會在多線程運算完的時候,指定的Runnable參數傳入的對象會被指定的Executor執行。
多數用戶喜歡使用 Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor)的方式, 或者 另一個版本version(譯者注:addCallback(ListenableFuture<V> future,FutureCallback<? super V> callback)),默認是採用 MoreExecutors.sameThreadExecutor()線程池, 爲了簡化使用,Callback採用輕量級的設計. FutureCallback<V> 中實現了兩個方法:
對應JDK中的 ExecutorService.submit(Callable) 提交多線程異步運算的方式,Guava 提供了ListeningExecutorService 接口, 該接口返回 ListenableFuture 而相應的 ExecutorService 返回普通的 Future。將 ExecutorService 轉爲 ListeningExecutorService,可使用MoreExecutors.listeningDecorator(ExecutorService)進行裝飾。
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); ListenableFuture explosion = service.submit(new Callable() { public Explosion call() { return pushBigRedButton(); } }); Futures.addCallback(explosion, new FutureCallback() { // we want this handler to run immediately after we push the big red button! public void onSuccess(Explosion explosion) { walkAwayFrom(explosion); } public void onFailure(Throwable thrown) { battleArchNemesis(); // escaped the explosion! } });
原文出處:https://www.cnblogs.com/jay-wu/p/10244501.html