0%

SpringDataJPA运行原理分析

前几天把整合SpringDataJPA和Spring讲了一下 , 现在空出了点时间 , 也对SPringDataJPA的运行有点好奇 , 毕竟 , 只要编写一个接口并继承两个JPAReposity相关的接口就能自动完成单表基础操作. 于是写了个单表的CRUD来DEBUG看看SpringDataJPA的实际运行原理 .

运行环境

  • JDK 1.8
  • IDEA 19.01
  • Maven 3.6
  • Mysql 5.17

运行测试

项目直接套用之前使用整合的那个 , 编写一个测试类进行单测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/**
* Unit test for simple App.
*/
@ContextConfiguration(classes = {config.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class AppTest {

@Autowired
private Customerdao customerdao;

/**
* 查询所有
*/
@Test
public void testFindAll(){
List<Customer> list = customerdao.findAll();
for (Customer customer : list) {
System.out.println(customer);
}
}
}

给查询所有方法调用打上断点然后debug运行:
debug
运行到这里 , 程序正常停住 , 我们按F8往下执行一步 , 可以发现 我们 进入了一个叫做JDKDynamicAOPProxy的类中的invoke()方法 , 从这里可以看出 , JPA在运行时其实是创建了一个动态代理对象来执行方法 , 这里也就能解释为什么 , 只要继承JPA接口即可完成CRUD功能.

再往下一步一步执行 , 我们可以发现下面的变量监控中出现了一个代理对象proxy , 类型是SimpleJPAReposity , 并且执行的方法也是我们调用的Findall方法.
proxyvar

但是我们并不确定 , 实际执行的对象就是这个类型的 .

继续往下运行 , 可以看到有个target对象(代理执行的真正对象在这里了) , 这里可以看到初始化为null , 我们继续往下执行 , 可以看到在这里 , 有了对target的实际赋值操作 :
targetvar

这个时候我们返回去再看看target初始化的地方:
proxyvar
可以看到target有了真实的引用地址了 , 这个指向的地址类型可以发现 , 其实也就是最开始我们推断的SimpleJPAReposity .

点进去看看 , 可以发现这个类实现了之前我们编写的持久层继承的两个JPA的接口:
proxyvar

于是在这里我便往下翻 , 很快就找到了我们要调用的FindAll方法:
proxyvar

可以看到 , 这里的返回它调用了当前类中的getQuery方法 , 我们点过去看一下:
proxyvar

在这个getQuery方法里 , 返回的是this.em.createQuery()方法 , 有没有对这个createQuery()方法有点熟悉? 这个方法就是java 的 JPA规范实现的创建创建查询的方法 , 那么这个em的类型我们其实已经可以知道了 , 我们点过去看一下:
proxyvar

和我想象的一样 , 其实就是一个EntityManager类型的对象.

到这里 , SpringDataJPA的方法实现原理已经基本上清楚了

总结

其实SPringDataJPA就是对java的JPA规范API进行了一个二次封装 , 更加贴近于面向对象的操作 , 简化了我们对JPA的配置.

其内部还是EntityManager管理器在对持久化单元进行的操作.