Spring Data JPA 源碼分析
一、前言
Spring Data JPA
源碼很少有人去分析,原因如下:
1、Spring Data JPA
地位沒有之前學習的框架高,大家習慣把它當成一個工具來用了,不願意對它進行源碼層次的解讀。
2、開發Dao
接口(ResumeDao
),接口的實現對象肯定是通過動態代理來完成的(增強),代理對象的產生過程追源碼很難追,特別特別講究技巧。
這裏,我來和大家一起分析下源碼,Spring Data JPA
源碼剖析的主要的過程,就是代理對象產生的過程。
我們發現resumeDao
是一個代理對象,這個代理對象的類型是SimpleJapRepository
。代理對象在h
裏產生。
二、這個代理對象是怎麼產生,過程是怎樣的?
以往:如果要給一個對象產生代理對象,我們知道是在AbstractApplicationContext
的refresh
方法中, 那麼能不能在這個方法中找到什麼我們當前場景的線索?
進入該方法以後,斷點到這一行,輸入beanName.equals("resumeDao")
新的疑問又來了?
問題1: 爲什麼會給它指定爲一個JpaRespositoryFactoryBean
#getObject
方法返回具體的對象?
問題2:指定這個FactoryBean
是在什麼時候發生的?
首先解決問題2:
傳入一個resumeDao
就返回了一個已經指定class
爲JpaRepositoryFactoryBean
的BeanDefinition
對象了,那麼應該在上圖中的get
時候就有了,所以斷點進入:
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap(256);
問題來了,什麼時候put
到map
中去的?我們定位到了一個方法在做這件事:
mergedBeanDefinitions.put
Find Usages
反調找到該方法:
我們發現,傳入該方法的時候,BeanDefintion
中的class
就已經被指定爲FactoryBean
了,那麼觀察該方法的調用棧。
繼續跟進,發現 BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName());
這行代碼已經產生了JpaRepositoryFactoryBean
。
通過上述追蹤我們發現,<jpa:repository basePackage
,掃描到的接口,在進行BeanDefintion
註冊時候,class
會被固定的指定爲JpaRepositoryFacotryBean
。
至此,問題2 追蹤完畢。
那麼接下來,我們再來追蹤問題1 JpaRespositoryFactoryBean
是一個什麼樣的類,它是一個FactoryBean
,我們重點關注FactoryBean
的getObject
方法。
由此可⻅,JdkDynamicAopProxy
會生成一個代理對象類型爲SimpleJpaRespository
,而該對象的增強邏輯就在JdkDynamicAopProxy
類的invoke
方法中。
至此,問題1追蹤完畢。
三、這個代理對象類型SimpleJpaRepository有什麼特別的?
原來SimpleJpaRepository
類實現了JpaRepository
接口和JpaSpecificationExecutor
接口。
Spring Data JPA
級別的封裝也就到這了,剩下的是JPA
和hibernate
的源碼了,這也印證了我們剛開始講的Spring Data JPA
是對JPA
的高級封裝。