java面試知識點精華提煉

2021-03-10 12:00:05

持續更新中。。。。。。

java基礎

== 和 equals 的區別?

  • equals 和 == 最大的區別是一個是方法一個是運運算元。
  • ==如果比較的物件是基本資料型別,則比較的是數值是否相等;如果比較的是參照資料型別,則比較的是物件的地址值是否相等。
  • equals():用來比較方法兩個物件的內容是否相等。
    注意:equals 方法不能用於基本資料型別的變數,如果沒有對 equals 方法進行重寫,則比較的是參照型別的變數所指向的物件的地址。

int 和 Integer 的區別

int是基本資料型別,Integer是包裝類(Integer是物件),Integer可以像物件一樣被操作

java集合

  1. Collection:Collection 是集合 List、Set、Queue 的最基本的介面。
  2. Iterator:迭代器,可以通過迭代器遍歷集合中的資料
  3. Map:是對映表的基礎介面

1、Collection 和 Map 的繼承體系:
在這裡插入圖片描述

list 集合

  • List 是有序、且可以重複的 Collection集合。List 一共三個實現類,分別是 ArrayList、Vector 和 LinkedList。
ArraList

ArrayList 內部是通過陣列實現,特點:讀取快,增刪慢。並且它是執行緒不安全的。

  • 因為陣列元素之間不能有間隔,所以從中間位置增刪元素,需要對陣列進行復制、移動、代價比較高。因此讀取快,增刪慢。
  • ArrayList 初始大小是10,每次擴容是原來的1.5倍。
  • ArrayList每次擴容都是通過Arrays.copyof(elementData,newCapacity)來實現的。
  • 在知道元素的大致數量時提前指定集合的大小,可以做到優化作用。
LinkList
  • LinkedList 是用連結串列結構儲存資料的雙向連結串列、支援序列化,特點:讀取慢,增刪快。並且它是執行緒不安全的。
  • LinkedList 提供了 List 介面中沒有定義的方法,用於操作表頭和表尾元素,可以當作堆疊、佇列和雙向佇列使用。
Vector(陣列實現、執行緒同步)
  • Vector 也是通過陣列實現,不同的是它支援執行緒的同步,所以是執行緒安全的。
  • 實現同步需要很高的花費,因此存取它比存取 ArrayList 慢。

Set 集合

  • Set 是無序、且不可以重複的 Collection集合。
  • set中是以物件的hashcode值作為判斷,判斷兩個物件是否相等。
  • 如果想要讓兩個不同的物件視為相等的,就必須覆蓋 Object 的 hashCode 方法和 equals 方法。
HashSet(Hash 表)
  • HashSet存放的是雜湊值,儲存元素的順序是按照雜湊值來存的。
  • 取資料也是按照雜湊值取得。元素的雜湊值是通過元素的hashcode 方法來獲取的。
  • HashSet 首先判斷兩個元素的雜湊值如果一樣,會接著比較equals 方法,如果equals 方法結果相等就視為同一個元素,結果不相等就視為不同元素。
  • 雜湊值相同 equals 方法不相等的元素,是在同樣的雜湊值下順延(可以認為雜湊值相同的元素放在一個雜湊桶中),也就是雜湊一樣的存一列
  • HashSet 通過 hashCode 值來確定元素在記憶體中的位置,一個 hashCode 位置上可以存放多個元素。
    如圖 1 表示 hashCode 值不相同的情況; 圖2 表示 hashCode 值相同,但 equals 不相同的情況。

在這裡插入圖片描述

  • 當new 一個HashSet範例時, 其實底層是新建立了一個HashMap範例。 HashSet中的元素實際上由HashMap的key來儲存,而HashMap的value則儲存了一個PRESENT,它是一個靜態的Object物件。
TreeSet(二元樹)
  • TreeSet 是使用二元樹的原理對 add 的物件按照指定的順序排序(升序、降序),每增加一個物件都會進行排序,將物件插入到二元樹指定的位置。
  • Integer 和 String 物件都可以進行預設的 TreeSet 排序,而自定義類的物件是不可以的,自己定義的類必須實現 Comparable 介面,並且覆寫相應的 compareTo()函數,才可以正常使用。
  • 在覆寫 compare()函數時,要返回相應的值才能使 TreeSet 按照一定的規則來排序,比較此物件與指定物件的順序。
  • 如果該物件小於、等於或大於指定物件,則分別返回負整數、零或正整數。
LinkHashSet(HashSet+LinkedHashMap)
  • LinkedHashSet 繼承與 HashSet、又基於 LinkedHashMap 來實現的。
  • LinkedHashSet 通過傳遞一個標識引數,呼叫父類別的構造器,底層構造一個 LinkedHashMap 來實現,在相關操
    作上與父類別 HashSet 的操作相同,直接呼叫父類別 HashSet 的方法即可。

Map

HashMap(陣列+連結串列+紅黑樹)
  • java7中 hashmap 使用,陣列+連結串列實現
  • java8以後 hashmap 使用,陣列+連結串列+紅黑樹實現
  • HashMap 根據鍵的 hashCode 值儲存資料,具有很快的存取速度,但遍歷順序卻是不確定的。 查詢快無序
  • HashMap 中 key 不可重複,value可以重複,且執行緒不安全。
  • HashMap 可以用 Collections 的 synchronizedMap 方法實現執行緒安全的能力,或者使用 ConcurrentHashMap。

在這裡插入圖片描述在這裡插入圖片描述

  • 大方向上,HashMap 裡面是一個陣列,然後陣列中每個元素是一個單向連結串列。
  • 上圖中每個綠色的實體是巢狀類 Entry 的範例,Entry 包含四個屬性:key, value, hash 值和用於單向連結串列的 next。
  • Java8 以後當連結串列中的元素超過了 8 個以後,會將連結串列轉換為紅黑樹,
  • 紅黑樹為了降低時間複雜度,由原來的 O(n) 降為 O(LogN),以加快檢索速度。
  1. capacity:當前陣列容量,始終保持 2^n,可以擴容,擴容後陣列大小為當前的 2 倍。
  2. loadFactor:負載因子,預設為 0.75。
  3. threshold:擴容的閾值,等於 capacity * loadFactor
ConcurrentHashMap
  • Java7 以前使用 segment(分段) + 陣列 + 連結串列 實現,使用對 segment(分段)加鎖實現執行緒安全,簡稱分段鎖。
  • Java8 以後使用 陣列 + 連結串列 + 紅黑樹 實現,使用Synchronized和CAS實現執行緒安全。

1、Java7 以前 ConcurrentHashMap 由一個個 Segment(分段) 組成。

  • Segment(分段) 通過繼承 ReentrantLock 來進行加鎖,每次加鎖的操作鎖住的是一個 segment(分段),這樣只要保證每個 Segment 是執行緒安全的,也就實現了全域性的執行緒安全。
  • Segment(分段) 數量預設是 16,初始化時可以設定為其他值,但是一旦初始化以後,它是不可以擴容的。
  • 因為是對 Segment(分段)單獨加鎖,所以理論上,最多可以同時支援 16 個執行緒並行寫,只要它們的操作分別分佈在不同的 Segment 上
    在這裡插入圖片描述

2、Java8以後 ConcurrentHashMap 和 HashMap 實現是差不多的,只是比HashMap多了Synchronized和CAS實現執行緒安全。

  • 在 put 時首先計算 key 的 hash 值,判斷 hash 值有沒有衝突,沒有衝突直接 CAS 插入。
  • 如果 hash 值存在衝突,就使用 Synchronized 加鎖,加鎖時會只鎖住單一連結串列或者紅黑樹的頭結點。
HashTable(執行緒安全)
  • Hashtable 很多的常用功能與 HashMap 類似,不同的是它繼承自 Dictionary 類,並且是執行緒安全的。
  • 並行性不如 ConcurrentHashMap,因為 ConcurrentHashMap 引入了分段鎖。
  • Hashtable 不建議使用,可以使用 HashMap 和 ConcurrentHashMap 代替。
TreeMap(可排序)
  • TreeMap 實現 SortedMap 介面,根據鍵排序,預設是按鍵值的升序排序,也可以自定義排序。
  • 如果使用到排序的功能,建議使用 TreeMap。
    在使用 TreeMap 時,key 必須實現 Comparable 介面或者在構造 TreeMap 傳入自定義的
    Comparator,否則會在執行時丟擲 java.lang.ClassCastException 型別的異常。
LinkHashMap(記錄插入順序)
  • LinkedHashMap 是 HashMap 的一個子類,儲存了元素的插入順序,在用 Iterator 遍歷
  • LinkedHashMap 時,先得到的記錄肯定是先插入的,也可以在構造時帶引數,按照存取次序排序。

反射機制

  • 反射機制是程式在執行中,獲取任意一個類的屬性和方法,並且可以呼叫。以達到動態獲取類資訊、動態呼叫物件的方法。
  • 反射將類的各個組成部分封裝成其他物件,這就是反射機制。
反射的應用場合
  • Java 物件在執行時可能會出現兩種型別:編譯時型別和執行時型別。
  • 編譯時的型別由宣告物件時用的型別來決定,執行時的型別由實際賦值給物件的型別決定 。
    如:Person p=new Student();
    其中編譯時型別為 Person,執行時型別為 Student。
    程式在執行時想要獲取 Student 物件的真實資訊,就只能依靠執行時資訊來發現該物件和類的真實資訊,此時就必須使用到反射了
反射 API
  1. Class 類:反射的核心類,可以獲取類的屬性,方法等資訊。
  2. Field 類:Java.lang.reflec 包中的類,表示類的成員變數,可以用來獲取和設定類之中的屬性值。
  3. Method 類: Java.lang.reflec 包中的類,表示類的方法,它可以用來獲取類中的方法資訊或者執行方法。
  4. Constructor 類: Java.lang.reflec 包中的類,表示類的構造方法。
  • 獲取 Class 物件的 3 種方法
// 1 呼叫某個物件的 getClass()方法
Person p=new Person();
Class clazz=p.getClass();
// 2 呼叫某個類的 class 屬性來獲取該類對應的 Class 物件
Class clazz=Person.class;
// 3 使用 Class 類中的 forName()靜態方法(最安全/效能最好/最常用)
Class clazz=Class.forName("類的全路徑"); 
  • 通過 Class 類中的方法獲取並檢視該類中的方法和屬性。
// 獲取 Person 類的 Class 物件
Class clazz=Class.forName("reflection.Person");
// 使用.newInstane 方法建立物件
Person p=(Person) clazz.newInstance();
// 獲取構造方法建立物件並設定屬性
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
Person p1=(Person) c.newInstance("李四","男",20);
//獲取 Person 類的所有方法資訊
// getMethods(),該方法是獲取本類以及父類別或者父介面中所有的公共方法(public修飾符修飾的)
// getDeclaredMethods(),該方法是獲取本類中的所有方法,包括私有的(private、protected、預設以及public)的方法。
Method[] method=clazz.getDeclaredMethods();
for(Method m:method){
 System.out.println(m.toString());
 // 呼叫方法 使方法執行
 m.invoke(p, 20);//需要兩個引數,一個是要呼叫的物件(獲取有反射),一個是實參
}
//獲取 Person 類的所有成員屬性資訊
Field[] field=clazz.getDeclaredFields();
for(Field f:field){
 System.out.println(f.toString());
}
//獲取 Person 類的所有構造方法資訊
Constructor[] constructor=clazz.getDeclaredConstructors();
for(Constructor c:constructor){
 System.out.println(c.toString());
}

序列化和反序列化

  • 序列化:將物件寫入到IO流中
  • 反序列化:從IO流中恢復物件
  • 在類中增加 writeObject 和 readObject 方法可以實現自定義序列化策略。
  • 通過 ObjectOutputStream 和 ObjectInputStream 對物件進行序列化及反序列化。
  • 意義:序列化機制允許將實現序列化的Java物件轉換為位元組序列,這些位元組序列可以儲存在磁碟上,或通過網路傳輸,以達到以後恢復成原來的物件。序列化機制使得物件可以脫離程式的執行而獨立存在。
  • 使用場景:所有可在網路上傳輸的物件都必須是可序列化的,比如RMI(remote method invoke,即遠端方法呼叫),傳入的引數或返回的物件都是可序列化的,否則會出錯;所有需要儲存到磁碟的java物件都必須是可序列化的。
  • 通常建議:程式建立的每個JavaBean類都實現Serializeable介面。並且建立序列化ID,用來判斷是否可以反序列化。
  • 序列化並不儲存靜態變數
  • 要想將父類別物件也序列化,就需要讓父類別也實現 Serializable 介面。
  • 如果不想讓某個變數被序列化,使用transient修飾,反序列化後,transient 變數的值被設為初始值,如 int 型的是 0,物件型的是 null。
  • 物件的類名、範例變數(包括基本型別,陣列,對其他物件的參照)都會被序列化;方法、類變數、transient範例變數都不會被序列化。
  • 序列化物件的參照型別成員變數,也必須是可序列化的,否則,會報錯。
  • 反序列化時必須有序列化物件的class檔案。
  • 同一物件序列化多次,只有第一次序列化為二進位制流,以後都只是儲存序列化編號,不會重複序列化。

IO和NIO

1、主要區別:

  • io是面向流、阻塞的。 Nio是面向快取、非阻塞的。
  • 傳統IO基於位元組流和字元流進行操作。
  • NIO基於Channel(通道)、Buffer(緩衝區)進行操作,資料從通道讀取到緩衝區中,或者從緩衝區讀取到通道中。
  • NIO中使用Selector(選擇區)監聽多個Channel(通道)事件,因此單個執行緒可以監聽多個資料通道。(比如:連線開啟,資料到達)

2、IO 工作流程:

  • 由於Java IO是阻塞的,所以當面對多個流的讀寫時需要多個執行緒處理。例如在網路IO中,Server端使用一個執行緒監聽一個埠,一旦某個連線被accept,建立新的執行緒來處理新建立的連線。其中 read/write 是阻塞的。

3、NIO 工作流程:

  • NIO 提供 Selector 實現單個執行緒管理多個channel的功能。select 呼叫可能是阻塞的,也可以是非阻塞的。但是read/write是非阻塞的!

4、NIO為什麼會被阻塞:

//這個方法可能會阻塞,直到有一個已註冊的事件發生,或者當一個或者更多的事件發生時
//可以設定超時時間,防止程序阻塞
selector.select(long timeout);

//使用此方法可以防止阻塞,阻塞在select()方法上的執行緒也可以立刻返回,不阻塞
selector.selectNow();

//可以喚醒阻塞狀態下的selector
selector.wakeup();

5、BIO、NIO、AIO 有什麼區別:

  • BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方 便,並行處理能力低。
  • NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,使用者端和伺服器端通過 Channel(通道) 通訊,實現了多路複用。
  • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了非同步非堵塞 IO ,非同步 IO 的 操作基於事件和回撥機制。

程式碼執行順序:父類別子類 靜態程式碼塊、構造程式碼塊、構造方法執行順序

父靜態、子靜態、父構造程式碼塊、父構造方法、子構造程式碼塊、子構造方法

java多執行緒實現

  • 建立多執行緒有4種方式,其中兩種有返回值,兩種沒有返回值。
    1.繼承Thread類,重寫run方法(其實Thread類本身也實現了Runnable介面)
    2.實現Runnable介面,重寫run方法
    3.實現Callable介面,重寫call方法(有返回值)
    4.使用執行緒池(有返回值)

四種執行緒池

  • Java 裡面執行緒池的頂級介面是 Executor,但是嚴格意義上講 Executor 並不是一個執行緒池,而只是一個執行執行緒的工具。真正的執行緒池介面是 ExecutorService。
  • newSingleThreadExecutor
    建立一個單執行緒的執行緒池。這個執行緒池只有一個執行緒在工作,也就是相當於單執行緒序列執行所有任務。如果這個唯一的執行緒因為異常結束,那麼會有一個新的執行緒來替代它。此執行緒池保證所有任務的執行順序按照任務的提交順序執行。
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
  • newFixedThreadPool
    建立固定大小的執行緒池。每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。執行緒池的大小一旦達到最大值就會保持不變,如果某個執行緒因為執行異常而結束,那麼執行緒池會補充一個新執行緒。
new ThreadPoolExecutor(int, int, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
  • newCachedThreadPool
    建立一個可快取的執行緒池。如果執行緒池的大小超過了處理任務所需要的執行緒,那麼就會回收部分空閒(60秒不執行任務)的執行緒,當任務數增加時,此執行緒池又可以智慧的新增新執行緒來處理任務。此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
  • newScheduledThreadPool
    建立一個大小無限的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。
new ScheduledThreadPoolExecutor(10);

執行緒生命週期(狀態)

  • 執行緒有五種狀態 新建(New)、就緒(Runnable)、執行(Running)、阻塞(Blocked)、死亡(Dead) 。
  • 新建(New):使用 new 建立了一個執行緒之後,該執行緒就處於新建狀態,此時僅由 JVM 為其分配記憶體,並初始化其成員變數的值
  • 就緒(Runnable):執行緒物件呼叫 start()方法之後,該執行緒處於就緒狀態。JVM 為其建立方法呼叫棧和程式計數器,等待排程執行。
  • 執行(Running):如果處於就緒狀態的執行緒獲得了 CPU,開始執行 run() 方法的執行緒執行體,則該執行緒處於執行狀態。
  • 阻塞(Blocked):
    指執行緒因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice,暫時停止執行。
    直到執行緒進入可執行(runnable)狀態,才有機會再次獲得 cpu timeslice 轉到執行(running)狀態。
  • 阻塞的情況分三種:
  1. 等待阻塞(o.wait->等待對列):執行(running)的執行緒執行 o.wait()方法,JVM會把該執行緒放入等待佇列(waitting queue)中。
  2. 同步阻塞(lock->鎖池):執行(running)的執行緒沒有獲取到同步鎖,該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池(lock pool)中。
  3. 其他阻塞(sleep/join):執行(running)的執行緒執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時,JVM 會把該執行緒置為阻塞狀態。當 sleep()狀態超時、join()等待執行緒終止或者超時、或者 I/O處理完畢時,執行緒重新轉入可執行(runnable)狀態。
  • 執行緒死亡(DEAD):執行緒會以下面三種方式結束,結束後就是死亡狀態。
  1. 正常結束,run()或 call()方法執行完成,執行緒正常結束。
  2. 異常結束,執行緒丟擲一個未捕獲的 Exception 或 Error。
  3. 呼叫 stop,直接呼叫該執行緒的 stop()方法來結束該執行緒—該方法通常容易導致死鎖,不推薦使用。

在這裡插入圖片描述

sleep 與 wait 區別

  • sleep()方法屬於 Thread 類,而 wait()方法,則是屬於Object 類中的。
  • sleep()方法是暫停執行指定的時間,讓出 cpu 給其他執行緒,但是他的監控狀態依然保持,當指定的時間到了又會自動恢復執行狀態。
  • 在呼叫 sleep()方法的過程中,執行緒不會釋放物件鎖。
  • 在呼叫 wait()方法的時候,執行緒會放棄物件鎖,並進入等待佇列,當其他執行緒呼叫notify()或者notifyAll()方法時,當前執行緒進入就緒狀態

start 與 run 區別

  • start()方法使用來啟動執行緒,真正實現了多執行緒執行。這時無需等待 run 方法體程式碼執行完畢,可以直接繼續執行下面的程式碼。
  • run()方法是執行緒體,包含了要執行的內容,直接呼叫run()方法,並不是啟動執行緒,和普通方法是一樣的。

java框架

spring框架

spring核心

  • IOC(Inverse of Control 控制反轉):Spring 通過組態檔來利用 Java 反射,範例化並控制物件的,生命週期和物件間的關係。還提供了 Bean 範例快取、生命週期管理、 Bean 範例代理、事件釋出、資源裝載等高階服務。
  • AOP(Aspect Oriented Programming 面向切面程式設計):AOP 是一種程式設計思想,是物件導向程式設計(OOP)的一種補充。物件導向程式設計將程式抽象成各個層次的物件,而面向切面程式設計是將程式抽象成各個切面。

Spring 常用模組

  • spring Beans:
  • spring context:
  • spring AOP:
  • spring DAO:
  • spring ORM:
  • spring web:

spring常用註解

  • @Controller(@RestController):
  • @RequestMapping(@GetMapping、@PostMapping):
  • @ResponseBody:
  • @RequestBody:
  • @RequestParam:
  • @PathVariable:
  • @Service:
  • @Repository:
  • @Autowired:注入物件
  • @Component:
  • @Configuration:定義設定類
  • @ModeAttribute:將引數和返回值繫結到Model中
  • @SessionAttribute:設定在Model中的屬性名稱
  • @Valid:校驗資料
  • @CookieValue:用來獲取Cokkie中的值

Spring IOC

IOC容器初始化時序圖:
在這裡插入圖片描述

Spring Bean 作用域
  • Bean的五中作用域:singleton(單例)、prototype(原型)、request(請求)、session(對談)、global session(全域性對談)
  • singleton:(預設)每一個Spring IoC容器都擁有唯一的一個範例物件,該模式在多執行緒下是不安全的。
  • prototype:每次通過 Spring 容器獲取 prototype 定義的 bean 時,容器都將建立一個新的 Bean 範例,每個 Bean 範例都有自己的屬性和狀態,而 singleton 全域性只有一個物件。根據經驗,對有狀態的bean使用prototype作用域,而對無狀態的bean使用singleton作用域。
  • request:每個HTTP請求會產生一個Bean物件,Http 請求結束,該 bean範例也將會被銷燬。只在基於web的SpringApplicationContext中可用。
  • session:限定一個Bean的作用域為HTTPsession的生命週期,session結束bean銷燬,只有基於web的Spring ApplicationContext才能使用
  • global Session:限定一個Bean的作用域為全域性HTTPSession的生命週期,只有基於web的SpringApplicationContext可用
Spring Bean 生命週期
  • Spring Bean的生命週期分為四個階段和多個擴充套件點。擴充套件點又可以分為影響多個Bean和影響單個Bean
  • 四個階段
    範例化 Instantiation:createBeanInstance()
    屬性賦值 Populate:populateBean()
    初始化 Initialization:initializeBean()
    銷燬 Destruction:至於銷燬,是在容器關閉時呼叫的,詳見ConfigurableApplicationContext#close()
  • 多個擴充套件點
    生命週期:InitializingBean、DisposableBean
    影響多個Bean:BeanPostProcessor、InstantiationAwareBeanPostProcessor
    影響單個Bean:
    Aware Group1:BeanNameAware、BeanClassLoaderAware、BeanFactoryAware
    Aware Group2:EnvironmentAware、EmbeddedValueResolverAware、ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)
Spring 依賴注入四種方式
  • 構造器注入、setter 方法注入、靜態工廠注入、範例工廠
Spring 自動裝配

Spring 裝配包括手動裝配和自動裝配,手動裝配是有基於 xml 裝配、構造方法、setter 方法等。
自動裝配有五種自動裝配的方式,可以用來指導 Spring 容器用自動裝配方式來進行依賴注入。

  1. no:預設的方式是不進行自動裝配,通過顯式設定 ref 屬性來進行裝配。
  2. byName:通過引數名自動裝配,Spring 容器在組態檔中發現 bean 的 autowire 屬性被設定成 byname,之後容器試圖匹配、裝配和該 bean 的屬性具有相同名字的 bean。
  3. byType:通過引數型別自動裝配,Spring 容器在組態檔中發現 bean 的 autowire 屬性被設定成 byType,之後容器試圖匹配、裝配和該 bean 的屬性具有相同型別的 bean。如果有多個 bean 符合條件,則丟擲錯誤。
  4. constructor:這個方式類似於 byType, 但是要提供給構造器引數,如果沒有確定的帶引數的構造器引數型別,將會丟擲異常。
  5. autodetect:首先嚐試使用 constructor 來自動裝配,如果無法運作,則使用 byType 方式。

Spring AOP

AOP核心概念
  1. 切面(aspect):類是對物體特徵的抽象,切面就是對橫切關注點的抽象
  2. 橫切關注點:對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點。
  3. 連線點(joinpoint):被攔截到的點,Spring 只支援方法型別的連線點,連線點指的是被攔截到的方法,連線點還可以是欄位或者構造器。
  4. 切入點(pointcut):對連線點進行攔截的定義
  5. 通知(advice):所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類。
  6. 目標物件:代理的目標物件
  7. 織入(weave):將切面應用到目標物件並導致代理物件建立的過程
  8. 引入(introduction):在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位。
AOP 主要應用場景有
  1. Authentication 許可權
  2. Caching 快取
  3. Context passing 內容傳遞
  4. Error handling 錯誤處理
  5. Lazy loading 懶載入
  6. Debugging 偵錯
  7. logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
  8. Performance optimization 效能優化
  9. Persistence 持久化
  10. Resource pooling 資源池
  11. Synchronization 同步
  12. Transactions 事務
AOP 兩種代理方式

Spring 提供了兩種方式來生成代理物件: JDKProxy 和 Cglib,具體使用哪種方式生成由AopProxyFactory 根據 AdvisedSupport 物件的設定來決定。預設的策略是如果目標類是介面,則使用 JDK 動態代理技術,否則使用 Cglib 來生成代理。

  • JDK 動態介面代理
    JDK 動態代理主要涉及到 java.lang.reflect 包中的兩個類:Proxy 和 InvocationHandler。InvocationHandler是一個介面,通過實現該介面定義橫切邏輯,並通過反射機制呼叫目標類的程式碼,動態將橫切邏輯和業務邏輯編制在一起。Proxy 利用 InvocationHandler 動態建立一個符合某一介面的範例,生成目標類的代理物件。
  • CGLib 動態代理
    CGLib 全稱為 Code Generation Library,是一個強大的高效能,高品質的程式碼生成類庫,可以在執行期擴充套件 Java 類與實現 Java 介面,CGLib 封裝了 asm,可以再執行期動態生成新的 class。
    和 JDK 動態代理相比較:JDK 建立代理有一個限制,就是隻能為介面建立代理範例,而對於沒有通過介面定義業務方法的類,則可以通過 CGLib 建立動態代理。