[轉]guava快速入門

?Guava工程包含了若干被Google的 Java項目廣泛依賴 的核心庫,例如:集合 [collections] 、緩存 [caching] 、原生類型支持 [primitives support] 、并發庫 [concurrency libraries] 、通用注解 [common annotations] 、字符串處理 [string processing] 、I/O 等等。

guava類似Apache Commons工具集

基本工具包Base

Optional

guava的Optional類似于Java 8新增的Optional類,都是用來處理null的,不過guava的是抽象類,其實現類為Absent和Present,而java.util的是final類。其中一部分方法名是相同的。

Guava用Optional表示可能為null的T類型引用。一個Optional實例可能包含非null的引用(我們稱之為引用存在),也可能什么也不包括(稱之為引用缺失)。它從不說包含的是null值,而是用存在或缺失來表示。但Optional從不會包含null值引用。


public class OptionalDemo {public static void main(String[] args) {Integer value1=null;Integer value2=10;/*創建指定引用的Optional實例,若引用為null則快速失敗返回absent()absent()創建引用缺失的Optional實例*/Optional<Integer> a=Optional.fromNullable(value1);Optional<Integer> b=Optional.of(value2); //返回包含給定的非空引用Optional實例System.out.println(sum(a,b));}private static Integer sum(Optional<Integer> a,Optional<Integer> b){//isPresent():如果Optional包含非null的引用(引用存在),返回trueSystem.out.println("First param is present: "+a.isPresent());System.out.println("Second param is present: "+b.isPresent());Integer value1=a.or(0);  //返回Optional所包含的引用,若引用缺失,返回指定的值Integer value2=b.get(); //返回所包含的實例,它必須存在,通常在調用該方法時會調用isPresent()判斷是否為nullreturn value1+value2;}
}

Preconditions

前置條件Preconditions提供靜態方法來檢查方法或構造函數,被調用是否給定適當的參數。它檢查的先決條件。其方法失敗拋出IllegalArgumentException。


public class PreconditionsDemo {public static void main(String[] args) {try {getValue(5);} catch (IndexOutOfBoundsException e){System.out.println(e.getMessage());}try {sum(4,null);} catch (NullPointerException e){System.out.println(e.getMessage());}try {sqrt(-1);} catch (IllegalArgumentException e){System.out.println(e.getMessage());}}private static double sqrt(double input){Preconditions.checkArgument(input>0.0,"Illegal Argument passed: Negative value %s.",input);return Math.sqrt(input);}private static int sum(Integer a,Integer b){a=Preconditions.checkNotNull(a,"Illegal Argument passed: First parameter is Null.");b=Preconditions.checkNotNull(b,"Illegal Argument passed: First parameter is Null.");return a+b;}private static int getValue(int input){int[] data={1,2,3,4,5};int index=Preconditions.checkElementIndex(input,data.length,"Illegal Argument passed: Invalid index.");return data[index];}
}

Joiner

Joiner 提供了各種方法來處理字符串加入操作,對象等。

Joiner的實例不可變的,因此是線程安全的。

Warning: joiner instances are always immutable; a configuration method such as { useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner instance returned by the method. This makes joiners thread-safe, and safe to store as {@code static final} constants.

   {@code // Bad! Do not do this! Joiner joiner = Joiner.on(‘,’); joiner.skipNulls(); // does nothing!分開寫跳過null就不起作用了,因為實例不可改變 return joiner.join(“wrong”, null, “wrong”);}


public class JoinerDemo {public static void main(String[] args) {/*on:制定拼接符號,如:test1-test2-test3 中的 “-“ 符號skipNulls():忽略NULL,返回一個新的Joiner實例useForNull(“Hello”):NULL的地方都用字符串”Hello”來代替*/StringBuilder sb=new StringBuilder();Joiner.on(",").skipNulls().appendTo(sb,"Hello","guava");System.out.println(sb);System.out.println(Joiner.on(",").useForNull("none").join(1,null,3));System.out.println(Joiner.on(",").skipNulls().join(Arrays.asList(1,2,3,4,null,6)));Map<String,String>map=new HashMap<>();map.put("key1","value1");map.put("key2","value2");map.put("key3","value3");System.out.println(Joiner.on(",").withKeyValueSeparator("=").join(map));}
}

Splitter

Splitter 能夠將一個字符串按照指定的分隔符拆分成可迭代遍歷的字符串集合,Iterable


public class SplitterDemo {public static void main(String[] args) {/*on():指定分隔符來分割字符串limit():當分割的子字符串達到了limit個時則停止分割fixedLength():根據長度來拆分字符串trimResults():去掉子串中的空格omitEmptyStrings():去掉空的子串withKeyValueSeparator():要分割的字符串中key和value間的分隔符,分割后的子串中key和value間的分隔符默認是=*/System.out.println(Splitter.on(",").limit(3).trimResults().split(" a,  b,  c,  d"));//[ a, b, c,d]System.out.println(Splitter.fixedLength(3).split("1 2 3"));//[1 2,  3]System.out.println(Splitter.on(" ").omitEmptyStrings().splitToList("1  2 3"));System.out.println(Splitter.on(",").omitEmptyStrings().split("1,,,,2,,,3"));//[1, 2, 3]System.out.println(Splitter.on(" ").trimResults().split("1 2 3")); //[1, 2, 3],默認的連接符是,System.out.println(Splitter.on(";").withKeyValueSeparator(":").split("a:1;b:2;c:3"));//{a=1, b=2, c=3}}
}

Objects

java7及以后的版本建議使用jdk中的Objects類

EventBus

Guava為我們提供了事件總線EventBus庫,它是事件發布-訂閱模式的實現,讓我們能在領域驅動設計(DDD)中以事件的弱引用本質對我們的模塊和領域邊界很好的解耦設計。

Guava為我們提供了同步事件EventBus和異步實現AsyncEventBus兩個事件總線,他們都不是單例的。

Guava發布的事件默認不會處理線程安全的,但我們可以標注@AllowConcurrentEvents來保證其線程安全

如果Listener A監聽Event A, 而Event A有一個子類Event B, 此時Listener A將同時接收Event A和B消息

事件


//Guava 發布-訂閱模式中傳遞的事件,是一個普通的POJO類
public class OrderEvent {  //事件private String message;public OrderEvent(String message) {this.message = message;}public String getMessage() {return message;}
}

訂閱


public class EventListener { //訂閱者//@Subscribe保證有且只有一個輸入參數,如果你需要訂閱某種類型的消息,只需要在指定的方法上加上@Subscribe注解即可@Subscribepublic void listen(OrderEvent event){System.out.println("receive message: "+event.getMessage());}/*一個subscriber也可以同時訂閱多個事件Guava會通過事件類型來和訂閱方法的形參來決定到底調用subscriber的哪個訂閱方法*/@Subscribepublic void listen(String message){System.out.println("receive message: "+message);}
}

多個訂閱者


public class MultiEventListener {@Subscribepublic void listen(OrderEvent event){System.out.println("receive msg: "+event.getMessage());}@Subscribepublic void listen(String message){System.out.println("receive msg: "+message);}
}


public class EventBusDemo {public static void main(String[] args) {EventBus eventBus=new EventBus("jack");/*如果多個subscriber訂閱了同一個事件,那么每個subscriber都將收到事件通知并且收到事件通知的順序跟注冊的順序保持一致*/eventBus.register(new EventListener()); //注冊訂閱者eventBus.register(new MultiEventListener());eventBus.post(new OrderEvent("hello")); //發布事件eventBus.post(new OrderEvent("world"));eventBus.post("!");}
}

DeadEvent

如果EventBus發送的消息都不是訂閱者關心的稱之為Dead Event。


public class DeadEventListener {boolean isDelivered=true;@Subscribepublic void listen(DeadEvent event){isDelivered=false;System.out.println(event.getSource().getClass()+"  "+event.getEvent()); //source通常是EventBus}public boolean isDelivered() {return isDelivered;}
}

Collection

不可變集合

不可變對象有很多優點,包括:

  • 當對象被不可信的庫調用時,不可變形式是安全的;
  • 不可變對象被多個線程調用時,不存在競態條件問題
  • 不可變集合不需要考慮變化,因此可以節省時間和空間。所有不可變的集合都比它們的可變形式有更好的內存利用率(分析和測試細節);

  • 不可變對象因為有固定不變,可以作為常量來安全使用。

JDK也提供了Collections.unmodifiableXXX方法把集合包裝為不可變形式,但:

  • 笨重而且累贅:不能舒適地用在所有想做防御性拷貝的場景;

  • 不安全:要保證沒人通過原集合的引用進行修改,返回的集合才是事實上不可變的;

  • 低效:包裝過的集合仍然保有可變集合的開銷,比如并發修改的檢查、散列表的額外空間,等等。

創建不可變集合方法:

  • copyOf方法,如ImmutableSet.copyOf(set);

  • of方法,如ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);

  • Builder工具


public class ImmutableDemo {public static void main(String[] args) {ImmutableSet<String> set=ImmutableSet.of("a","b","c","d");ImmutableSet<String> set1=ImmutableSet.copyOf(set);ImmutableSet<String> set2=ImmutableSet.<String>builder().addAll(set).add("e").build();System.out.println(set);ImmutableList<String> list=set.asList();}
}

新型集合類

Multiset

Multiset可統計一個詞在文檔中出現了多少次


public class MultiSetDemo {public static void main(String[] args) {Multiset<String> set=LinkedHashMultiset.create();set.add("a");set.add("a");set.add("a");set.add("a");set.setCount("a",5); //添加或刪除指定元素使其在集合中的數量是countSystem.out.println(set.count("a")); //給定元素在Multiset中的計數System.out.println(set);System.out.println(set.size()); //所有元素計數的總和,包括重復元素System.out.println(set.elementSet().size()); //所有元素計數的總和,不包括重復元素set.clear(); //清空集合System.out.println(set);}
}

Multimap

Multimap可以很容易地把一個鍵映射到多個值。換句話說,Multimap是把鍵映射到任意多個值的一般方式。


public class MultiMapDemo {public static void main(String[] args) {Multimap<String,Integer> map= HashMultimap.create(); //Multimap是把鍵映射到任意多個值的一般方式map.put("a",1); //key相同時不會覆蓋原valuemap.put("a",2);map.put("a",3);System.out.println(map); //{a=[1, 2, 3]}System.out.println(map.get("a")); //返回的是集合System.out.println(map.size()); //返回所有”鍵-單個值映射”的個數,而非不同鍵的個數System.out.println(map.keySet().size()); //返回不同key的個數Map<String,Collection<Integer>> mapView=map.asMap();}
}

BiMap

BiMap


public class BitMapDemo {public static void main(String[] args) {BiMap<String,String> biMap= HashBiMap.create();biMap.put("sina","sina.com");biMap.put("qq","qq.com");biMap.put("sina","sina.cn"); //會覆蓋原來的value/*在BiMap中,如果你想把鍵映射到已經存在的值,會拋出IllegalArgumentException異常如果對特定值,你想要強制替換它的鍵,請使用 BiMap.forcePut(key, value)*/biMap.put("tecent","qq.com"); //拋出異常biMap.forcePut("tecent","qq.com"); //強制替換keySystem.out.println(biMap);System.out.println(biMap.inverse().get("sina.com")); //通過value找keySystem.out.println(biMap.inverse().inverse()==biMap); //true}
}

Table

Table它有兩個支持所有類型的鍵:”行”和”列”。


public class TableDemo {public static void main(String[] args) {//記錄學生在某門課上的成績Table<String,String,Integer> table= HashBasedTable.create();table.put("jack","java",100);table.put("jack","c",90);table.put("mike","java",93);table.put("mike","c",100);Set<Table.Cell<String,String,Integer>> cells=table.cellSet();for (Table.Cell<String,String,Integer> cell : cells) {System.out.println(cell.getRowKey()+" "+cell.getColumnKey()+" "+cell.getValue());}System.out.println(table.row("jack"));System.out.println(table);System.out.println(table.rowKeySet());System.out.println(table.columnKeySet());System.out.println(table.values());}
}

Collections2

filter():只保留集合中滿足特定要求的元素


public class FilterDemo {public static void main(String[] args) {List<String> list= Lists.newArrayList("moon","dad","refer","son");Collection<String> palindromeList= Collections2.filter(list, input -> {return new StringBuilder(input).reverse().toString().equals(input); //找回文串});System.out.println(palindromeList);}
}

transform():類型轉換


public class TransformDemo {public static void main(String[] args) {Set<Long> times= Sets.newHashSet();times.add(91299990701L);times.add(9320001010L);times.add(9920170621L);Collection<String> timeStrCol= Collections2.transform(times, new Function<Long, String>() {@Nullable@Overridepublic String apply(@Nullable Long input) {return new SimpleDateFormat("yyyy-MM-dd").format(input);}});System.out.println(timeStrCol);}
}

多個Function組合


public class TransformDemo {public static void main(String[] args) {List<String> list= Lists.newArrayList("abcde","good","happiness");//確保容器中的字符串長度不超過5Function<String,String> f1=new Function<String, String>() {@Nullable@Overridepublic String apply(@Nullable String input) {return input.length()>5?input.substring(0,5):input;}};//轉成大寫Function<String,String> f2=new Function<String, String>() {@Nullable@Overridepublic String apply(@Nullable String input) {return input.toUpperCase();}};Function<String,String> function=Functions.compose(f1,f2);Collection<String> results=Collections2.transform(list,function);System.out.println(results);}
}

集合操作:交集、差集、并集


public class CollectionsDemo {public static void main(String[] args) {Set<Integer> set1= Sets.newHashSet(1,2,3,4,5);Set<Integer> set2=Sets.newHashSet(3,4,5,6);Sets.SetView<Integer> inter=Sets.intersection(set1,set2); //交集System.out.println(inter);Sets.SetView<Integer> diff=Sets.difference(set1,set2); //差集,在A中不在B中System.out.println(diff);Sets.SetView<Integer> union=Sets.union(set1,set2); //并集System.out.println(union);}
}

Cache

緩存在很多場景下都是相當有用的。例如,計算或檢索一個值的代價很高,并且對同樣的輸入需要不止一次獲取值的時候,就應當考慮使用緩存。

Guava Cache與ConcurrentMap很相似,但也不完全一樣。最基本的區別是ConcurrentMap會一直保存所有添加的元素,直到顯式地移除。相對地,Guava Cache為了限制內存占用,通常都設定為自動回收元素。在某些場景下,盡管LoadingCache 不回收元素,它也是很有用的,因為它會自動加載緩存。

Guava Cache是一個全內存的本地緩存實現,它提供了線程安全的實現機制。

通常來說,Guava Cache適用于:

  • 你愿意消耗一些內存空間來提升速度。

  • 你預料到某些鍵會被查詢一次以上。

  • 緩存中存放的數據總量不會超出內存容量。(Guava Cache是單個應用運行時的本地緩存。它不把數據存放到文件或外部服務器。

如果這不符合你的需求,請嘗試Memcached這類工具)

Guava Cache有兩種創建方式:

  • cacheLoader
  • callable callback

LoadingCache是附帶CacheLoader構建而成的緩存實現


public class LoadingCacheDemo {public static void main(String[] args) {LoadingCache<String,String> cache= CacheBuilder.newBuilder().maximumSize(100) //最大緩存數目.expireAfterAccess(1, TimeUnit.SECONDS) //緩存1秒后過期.build(new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {return key;}});cache.put("j","java");cache.put("c","cpp");cache.put("s","scala");cache.put("g","go");try {System.out.println(cache.get("j"));TimeUnit.SECONDS.sleep(2);System.out.println(cache.get("s")); //輸出s} catch (ExecutionException | InterruptedException e) {e.printStackTrace();}}
}


public class CallbackDemo {public static void main(String[] args) {Cache<String,String> cache= CacheBuilder.newBuilder().maximumSize(100).expireAfterAccess(1, TimeUnit.SECONDS).build();try {String result=cache.get("java", () -> "hello java");System.out.println(result);} catch (ExecutionException e) {e.printStackTrace();}}
}

refresh機制:
- LoadingCache.refresh(K) 在生成新的value的時候,舊的value依然會被使用。
- CacheLoader.reload(K, V) 生成新的value過程中允許使用舊的value
- CacheBuilder.refreshAfterWrite(long, TimeUnit) 自動刷新cache

并發

ListenableFuture

傳統JDK中的Future通過異步的方式計算返回結果:在多線程運算中可能或者可能在沒有結束返回結果,Future是運行中的多線程的一個引用句柄,確保在服務執行返回一個Result。

ListenableFuture可以允許你注冊回調方法(callbacks),在運算(多線程執行)完成的時候進行調用, 或者在運算(多線程執行)完成后立即執行。這樣簡單的改進,使得可以明顯的支持更多的操作,這樣的功能在JDK concurrent中的Future是不支持的。


public class ListenableFutureDemo {public static void main(String[] args) {//將ExecutorService裝飾成ListeningExecutorServiceListeningExecutorService service= MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());//通過異步的方式計算返回結果ListenableFuture<String> future=service.submit(() -> {System.out.println("call execute..");return "task success!";});//有兩種方法可以執行此Future并執行Future完成之后的回調函數future.addListener(() -> {  //該方法會在多線程運算完的時候,指定的Runnable參數傳入的對象會被指定的Executor執行try {System.out.println("result: "+future.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}},service);Futures.addCallback(future, new FutureCallback<String>() {@Overridepublic void onSuccess(@Nullable String result) {System.out.println("callback result: "+result);}@Overridepublic void onFailure(Throwable t) {System.out.println(t.getMessage());}},service);}
}

IO


public class FileDemo {public static void main(String[] args) {File file=new File(System.getProperty("user.dir"));}//寫文件private void writeFile(String content,File file) throws IOException {if (!file.exists()){file.createNewFile();}Files.write(content.getBytes(Charsets.UTF_8),file);}//讀文件private List<String> readFile(File file) throws IOException {if (!file.exists()){return ImmutableList.of(); //避免返回null}return Files.readLines(file,Charsets.UTF_8);}//文件復制private void copyFile(File from,File to) throws IOException {if (!from.exists()){return;}if (!to.exists()){to.createNewFile();}Files.copy(from,to);}}

Google Guava官方教程(中文版)
guava-importnew


---------------------
作者:dgeek
來源:CSDN
原文:https://blog.csdn.net/dgeek/article/details/76221746
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/283907.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/283907.shtml
英文地址,請注明出處:http://en.pswp.cn/news/283907.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據庫編程1 Oracle 過濾 函數 分組 外連接 自連接

【本文謝絕轉載原文來自http://990487026.blog.51cto.com】<大綱>數據庫編程1 Oracle 過濾 函數 分組 外連接 自連接本文實驗基于的數據表:winsows安裝好Oracle11g之后,開始實驗SQLplus 登陸 ORaclesqlplus 退出的方式查看用戶之下有什么表查看表的所有記錄&#xff0c;不…

【.NET 6】開發minimal api以及依賴注入的實現和代碼演示

前言&#xff1a;.net 6 LTS版本發布已經有一段時間了。此處做一個關于使用.net 6 開發精簡版webapi&#xff08;minimal api&#xff09;的入門教程演示。1、新建一個項目。此處就命名為 SomeExample:2、選擇 .net6版本&#xff0c;并且此處先去掉HTTPS配置以及去掉使用控制器…

(轉載)VS2010/MFC編程入門之四(MFC應用程序框架分析)

上一講雞啄米講的是VS2010應用程序工程中文件的組成結構&#xff0c;可能大家對工程的運行原理還是很模糊&#xff0c;理不出頭緒&#xff0c;畢竟跟C編程入門系列中的例程差別太大。這一節雞啄米就為大家分析下MFC應用程序框架的運行流程。 一.SDK應用程序與MFC應用程序運行過…

個人博客開發-開篇

邁出第一步&#xff1a; 很久以前就有這個想法&#xff0c;自己動手開發一套個人博客系統&#xff0c;終于&#xff0c;現在開始邁出了第一步。做這件事一點是做一個有個人風格的博客系統&#xff0c;第二點是對做這件事所使用的技術棧進行學習&#xff0c;所謂最好的學習就是實…

2022年中國中小學教育信息化行業研究報告

教育信息化丨研究報告 核心摘要&#xff1a; 背景篇 目前&#xff0c;我國中小學教育主要呈現信息時代教育的特征&#xff0c;智能時代教育特征初露端倪&#xff1b;中小學教育信息化正從量變邁向質變&#xff0c;創新引領與生態變革成為行業縱深的主旋律&#xff1b; 2021年…

使用curl指令發起websocket請求

昨日的文章沒指出websocket請求協商切換的精髓&#xff0c;刪除重發。前文相關&#xff1a;? .NET WebSockets 核心原理初體驗[1]? SignalR 從開發到生產部署避坑指南[2]tag&#xff1a;瀏覽器--->nginx--> server其中提到nginx默認不會為客戶端轉發Upgrade、Connectio…

Yii 2 的安裝 之 踩坑歷程

由于剛接觸yii2 ,決定先裝個試試&#xff1b;可是這一路安裝差點整吐血&#xff0c;可能還是水平有限吧&#xff0c; 但還是想把這個過程分享出來&#xff0c;讓遇到同樣問題的同學有個小小的參考&#xff0c;好了言歸正傳&#xff01;&#xff01; <(~.~)> 下面是安裝流…

設計模式之代理模式(上) 靜態代理與JDK動態代理

2019獨角獸企業重金招聘Python工程師標準>>> 代理模式 給某一個對象提供一個代理&#xff0c;并由代理對象控制對原對象的引用。靜態代理 靜態代理是由我們編寫好的類&#xff0c;在程序運行之前就已經編譯好的的類&#xff0c;此時就叫靜態代理。 說理論還是比較懵…

mysql 分頁查詢

使用limit函數 limit關鍵字的用法&#xff1a; LIMIT [offset,] rows offset指定要返回的第一行的偏移量&#xff0c;rows第二個指定返回行的最大數目。初始行的偏移量是0(不是1)。轉載于:https://www.cnblogs.com/xping/p/6703986.html

WPF 實現更換主題色

WPF 實現更換主題色WPF 使用 WPFDevelopers.Minimal 如何更換主題色作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&a…

vue3與vue2的區別

先來說說當下市場開發使用的問題&#xff0c;目前2021年使用vue3開發的企業還是少&#xff0c;基本上都還是以vue2的形式進行開發&#xff0c;vue3的開發模式跟react很像&#xff0c;這時候有人就會想那我學vue3有用么&#xff0c;淦&#xff0c;他喵的&#xff0c;先別激動&am…

Spring Data REST API集成Springfox、Swagger

原文: Documenting a Spring Data REST API with Springfox and Swagger 使用Spring Date REST&#xff0c;你可以迅速為Spring Date repositories的創建REST API&#xff0c;并提供CRUD和更多功能。然而&#xff0c;在嚴謹的API開發過成功&#xff0c;您還希望擁有自動生成的最…

【系統設計】S3 對象存儲

在本文中&#xff0c;我們設計了一個類似于 Amazon Simple Storage Service (S3) 的對象存儲服務。S3 是 Amazon Web Services (AWS) 提供的一項服務&#xff0c; 它通過基于 RESTful API 的接口提供對象存儲。根據亞馬遜的報告&#xff0c;到 2021 年&#xff0c;有超過 100 萬…

轉: telnet命令學習

1.每天一個linux命令&#xff08;58&#xff09;&#xff1a;telnet命令 轉自&#xff1a; http://www.cnblogs.com/peida/archive/2013/03/13/2956992.html telnet命令通常用來遠程登錄。telnet程序是基于TELNET協議的遠程登錄客戶端程序。Telnet協議是TCP/IP協議族中的一員&a…

禪道、碼云、coding、redmine、jira、teambition幾大敏捷開發項目管理系統試用對比體驗

作為一個軟件公司的管理人員&#xff0c;在項目和人員多起來后&#xff0c;就需要通過系統來對項目和人員進行管理。 我們是典型的軟件外包公司&#xff0c;專為客戶定制軟件&#xff0c;所以我們的業務都是項目型的。因此&#xff0c;在管理模式上&#xff0c;我們就要用所謂…

Dubbo中的SPI機制

Dubbo中的SPI機制 概述 Service Provider Interface 即 SPI&#xff0c;是JDK內置的一種服務提供發現機制&#xff0c;可以用來啟用框架擴展和替換組件。可以讓不同的廠商針對統一接口編寫不同的實現 SPI實際上是“接口策略模式配置文件”實現的動態加載機制。在系統設計中&…

JWT:擁有我,即擁有權力

Hi&#xff0c;這里是桑小榆。上篇文章中&#xff0c;我們一起探討了 OAuth 協議的原理以及授權認證流程&#xff0c;本次我們一起探討 jwt 令牌作為授權協議的傳輸介質。OAuth協議規范了幾個參與角色的授權標準&#xff0c;安全可控的授予第三方應用&#xff0c;第三方應用獲取…

雙十一到來之前,阿里AI設計師“魯班”1天能做4000萬張海報

相比較去年&#xff0c;“魯班”的設計技藝有所提升。 人工智能很大程度上便利了我們的生活&#xff0c;現在他們甚至還能取代了一些設計師的工作&#xff0c;在雙十一正式到來之前&#xff0c;淘寶的宣傳已經鋪天蓋地&#xff0c;然而很多人都沒想到&#xff0c;我們打開淘寶…

Appium移動自動化測試之獲取appPackage和appActivity

方法一&#xff1a;直接打開Appium,點擊左上角機器人圖標 選擇apk所在位置&#xff0c;如圖所示&#xff0c;這里以ContactManager.apk為例 方法二&#xff1a;利用dex2jar和jd-gui這兩個工具反編譯apk文件 這里仍以ContactManager.apk為例 (1)重命名ContactManager.apk為Conta…