【筆記:Spring Data JPA】Spring Data JPA 如何實作我寫的 repository interface?
Chuan

  這只是個 interface 啊,它怎麼執行起來的?!


複習一下 interface

  interface(介面),像是規格書、定義出該要有哪些 method;由 implement 它的 class(實作類別)去 override 這些 method、並把內容完成。

  舉個例子,「List」是 interface、定義要有 add() 這個 method;而「ArrayList」和「LinkedList」implement 此 interface,分別用不同的實作方式實現了 add() 這個 method 所要有的功能。

  interface 是不能用 new 產生出物件實例的,它只是規格書、不是藍圖;所以,當我們在 Spring Boot 的一個 Service class 裡注入了一個 Repository,它一定有一個實作的 class 本體……對吧?


範例

  我有個 Entity class 名為 Game,並其 primary key 的型別為 Integer,因此我寫了一個 interface、繼承 Spring Data JPA 的 JpaRepository

1
2
public interface GameRepo extends JpaRepository<Game, Integer> {
}

  只有寫這樣,也沒有加上 @Repository annotation。

  在 service class,用 constructor DI 注入了這個 interface 的 Bean:

1
2
3
4
5
6
7
8
9
public class GameService {
// ...
private final GameRepo gameRepo;

public GameService(GameRepo gameRepo) {
this.gameRepo = gameRepo;
}
// ...
}

  這樣就能直接使用了 O.O ——我沒有自己寫實作 class 哦?!


Spring 怎麼辦到的?——動態代理(Dynamic Proxy)

  動態代理(Dynamic Proxy)是什麼?簡單來說,是在執行時動態地根據反射產生物件實例、而非根據寫好的 class 產生。

  當 Spring Boot Application 啟動,會掃描所有繼承了 JpaRepository 的 interface、利用動態代理產生代理物件實例,並作為 Bean 給我們使用。
  所以,我們自己沒有寫實作 class、是 Spring 幫我們做了個代理給我們用;並且,這個代理類別已經包含 @Repository 的功能,所以不用在我們自己寫的 repository interface 上面寫。

  至於動態代理是怎麼實現的,還要先瞭解「反射」……就不在此細講了,因為我也還不是很懂。

怎麼知道它是代理?

  對一個物件實例使用 getClass(),可以得到一個 Class 物件,裡面有許多方法、可以取得關於這個 class 的相關資訊,舉幾個常用的:

  • getCanonicalName():完整的 class 名稱,包含完整 package 路徑
  • getSimpleName():class 名稱,不包含 package
  • getInterfaces():這個 class implement 的所有 interface,回傳值是 Class<?>[]、可以再進一步取得這些 interface 的資訊
  • getMethods():這個 class 裡所有的 method,回傳值是 Method[]、可以透過 Method 物件進一步取得 method 名稱、參數、回傳值等資訊。

  所以,我對範例中注入進來的 gameRepo 物件實驗了一下:

1
2
3
4
// ...
result.put("canonicalName", gameRepo.getClass().getCanonicalName());
result.put("interfaces", gameRepo.getClass().getInterfaces());
// ...

  回傳的結果是:

1
2
3
4
5
6
7
8
9
10
{
"canonicalName": "jdk.proxy6.$Proxy134",
"interfaces": [
"chuan13.playground.repository.GameRepo",
"org.springframework.data.repository.Repository",
"org.springframework.transaction.interceptor.TransactionalProxy",
"org.springframework.aop.framework.Advised",
"org.springframework.core.DecoratingProxy"
]
}

  嗯,的確是個 proxy,implement 了我自己寫的 GameRepo 這個 interface、還有許多其他的…… interface 們。

  要學的還很多呢 www


延伸閱讀


參考資料

  • ChatGPT
  • 以上「延伸閱讀」資料