Spring Data JPA 源码分析
前言
欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
一、概述
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
的高级封装。