Spring Data JPA源码

导读:

什么是Spring Data JPA?
要解释这个问题,我们先将Spring Data JPA拆成两个部分,即Sping Data和JPA。
从这两个部分来解释。
Spring Data是什么?
摘自: https://spring.io/projects/spring-data

Spring Data’s mission is to provide a familiar and consistent, Spring-based programming model for data access while still retaining the special traits of the underlying data store.It makes it easy to use data access technologies, relational and non-relational databases, map-reduce frameworks, and cloud-based data services. This is an umbrella project which contains many subprojects that are specific to a given database. The projects are developed by working together with many of the companies and developers that are behind these exciting technologies.
翻译:
Spring Data的使命/任务就是提供一个通用的/熟悉的和一致的,基于Spring的数据访问编程模型,同时仍然保留了底层数据存储的特性.
它使使用数据访问技术、关系和非关系数据库、map-reduce框架和基于云的数据服务变得容易。这是一个总的项目,包含许多特定于给定数据库的子项目。这些项目是通过与这些令人兴奋的技术背后的许多公司和开发人员合作开发的。

JPA是什么?

JPA全称:Java Persistence API
是一个基于ORM(对象关系映射)的标准规范,既然是规范,那自然不是具体的实现.
Hibernate就是其中的一个实现

JPA与JDBC的关系

JPA是Java持久化的API, JDBC是Java数据库连接.
JPA是JDBC上层的抽象,提供一种ORM即对象关系映射的方式来操作数据库。
JPA是基于JDBC的,就是说JPA的各种实现包括Hibernate,JPA是基于JDBC的更高级的数据库访问技术。
JDBC是java语言中用于连接和操作数据库的API,提供了一套标准接口和方法。然后基于各个数据库的的驱动来与数据库进行交互。

在图中可以反映JPA与JDBC的关系

源码

以一个简单的方法作为切入口了解一下Spring Data Jpa的源码
这里是基于SpringBoot 2.3.12.RELEASE版本来研究源码的。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
所以在maven的pom.xml中引用jpa.
根据spring-boot-autoconfigure的jar包中会有spring-autoconfigure-metadata.properties文件,
这里面配置了匹配jpa自动配置的条件。如下
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnBean=javax.sql.DataSource
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnClass=org.springframework.data.jpa.repository.JpaRepository
通过上述配置可知:要有数据源,要有JpaRepository以及HibernateJpaAutoConfiguration等
因为JPA是基于Hibernate来的,所以需要先有Hibernate所以也就必须先进行
HibernateJpaAutoConfiguration自动配置

整体流程

整体分析源码的流程是,围绕三个问题展开的,问题如下:
1.为什么我自己定义的AccountRepository接口的类型为SimpleJpaRepository,这是从哪里来的?
2.代理SimpleJpaRepositiry是在什么时候创建的执行的?
3.为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象?

整体流程如下:

在这里插入图片描述

具体源码分析如下

关于Spring Data Jpa的源码, 我们可以通过一个简单的例子来作为切入口,例子如下
// 写一个单元测试的类
@RunWith(SpringRunner.class)
@SpringBootTest
public class AccountRepositoryTest {@Autowiredprivate AccountRepository accountRepository;@Testpublic void test() {Optional<Account> accountOptional = accountRepository.findById(1L);if (accountOptional.isPresent()) {Account account = accountOptional.get();System.out.println(account);}}
}

1. 运行单元测试方法发现accountRepository是一个SimpleJapRepository类型的代理对象

在这里插入图片描述
至此我们需要搞明白两个事情:1、为什么是SimpleJpaRepository,这是从哪里来的。2、代理对象是什么时候创建的
带着这两个问题我们去看源码。
熟悉Spring生命周期的应该知道AccountRepository接口上的@Repository注解会Spring扫描处理,并实例化。定位到具体代码

2. 进入Spring生命周期代码中查看对象的创建过程

// AbstractApplicationContext类的refresh() --> finishBeanFactoryInitialization(beanFactory)
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();

在这里插入图片描述
可以看到在这一步accountRepository已经是一个FactoryBean,所以if的这个分支会进入,在这里会对FactoryBean的对象调用getBean(String name)方法,从而进入Spring创建对象的生命周期中。
再看这个循环里面的beanNames来自于beanDefinitionNames即是存放beanDefinition对象集合的名称集合。进入isFactoryBean(String name)方法内容如下:
在这里插入图片描述
发现获取到的单例对象类型为JpaRepositoryFactoryBean,这里又增加了一个新的问题,即3、为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象。
由于当代码执行到这一步的时候,说明BeanDifinition集合已经存在了,即Spring已经将类构造成BeanDifinition对象放入集合中,后续会根据BeanDifinition集合来创建对象。所以我们需要找到初始化BeanDifinition对象的地方。
根据beanDefinitionNames找到调用的添加操作的地方
在这里插入图片描述
找到了registerBeanDefinition方法,但是这个时候BeanDefinition已经是JapRepositoryFactoryBean了,还得往前找。
在这里插入图片描述

3.根据调用链找到了RepositoryConfigurationDelegate类的registerRepositoriesIn方法中调用了上面的registerBeanDefinition方法,该方法将AccountRepositoryBeanDefinition中注册为JpaRepositoryFactoryBean

在这里插入图片描述
先看builder.build(configuration)方法,在build方法中会获取到JapRepositoryFactoryBean类型并注册
在这里插入图片描述
build方法执行结束后,会使用返回的definitionBuilder获取beanDefinition对象,并将beanDefinition传入registerBeanDefinition方法
在这里插入图片描述
到这里就解释了,为什么accountRepository对应的BeanDefinition对象类型为JapRepositoryFactoryBean类型。在Spring中FactoryBean的作用就是生产某一种类型的对象,核心方法为T getObject() throws Exception;至此解释了一个问题:为什么根据accountRepository获取对象获取到的是JpaRepositoryFactoryBean这个类型的对象。

4. 查看JpaRepositoryFactoryBeangetObject()方法

接下来重点看下JpaRepositoryFactoryBeangetObject()方法
在这里插入图片描述
这里方法很简单,直接从this.respository中获取对象即可,但是this.repository是何时初始化的呢?查看JpaRepositoryFactoryBean的类图,发现实现了InitializingBean接口
在这里插入图片描述

5. getObject()方法中的this.repository的初始化

找到InitializingBean接口的实现方法,发现在afterPropertiesSet()方法中对this.respository进行初始化了。
在这里插入图片描述

6. SimpleJpaRepository的由来

进入getRepository(Class<T> repositoryInterface, RepositoryFragments fragments)方法进一步查看代码
在这里插入图片描述
再看这个方法getRepositoryInformation(metadata, composition) --> getRepositoryBaseClass(metadata)
在这里插入图片描述在这里插入图片描述
在初始化this.repository的时候,就固定将repository固定设置为SimpleJpaRepository类型了。
到这里就解释了为什么是SimpleJpaRepository类型了,回答了最开始提出的第一个问题。

7. AccountRepository代理对象的创建

此时回到RepositoryFactorySupport.getRepository(Class<T> repositoryInterface, RepositoryFragments fragments)方法
在这里插入图片描述
可以从中看到当设置了repositoryBaseClassSimpleJpaRepository类型之后,开始通过动态代理的方式来创建对象了。

Object target = getTargetRepository(information);中通过反射来创建SimpleJpaRepository对象,SimpleJpaRepository是实现了JpaRepository接口的,与我们自己定义的AccountRepository接口一样都实现了JpaRepository即可,这满足了JDK动态代理的条件,即需要代理的对象与代理对象都实现了相同的接口。

接下来使用ProxyFactory来创建代理对象的,从最开始的结果可以知道使用的是JDK的动态代理,所以需要传递需要代理的接口,
设置需要代理的接口,和实现类。通过最后的T repository = (T) result.getProxy(classLoader);来创建并获取代理对象。

	/*** Create a new proxy according to the settings in this factory.* <p>Can be called repeatedly. Effect will vary if we've added* or removed interfaces. Can add and remove interceptors.* <p>Uses the given class loader (if necessary for proxy creation).* @param classLoader the class loader to create the proxy with* (or {@code null} for the low-level proxy facility's default)* @return the proxy object*/public Object getProxy(@Nullable ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}

最终得到了accountRepository的代理对象,然后调用findById(ID id)

	@Overridepublic Optional<T> findById(ID id) {Assert.notNull(id, ID_MUST_NOT_BE_NULL);Class<T> domainType = getDomainClass();if (metadata == null) {return Optional.ofNullable(em.find(domainType, id));}LockModeType type = metadata.getLockModeType();Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();// 最终调用SessionImpl.find()方法查询返回结果return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/55517.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round #890 (Div. 2)

A.Tales of a Sort 题目大意 Alphen has an array of positive integers a a a of length n. Alphen can perform the following operation: For all i i i from 1 to n, replace a i a_i ai​ with max ⁡ ( 0 , a i − 1 ) \max(0,a_i−1) max(0,ai​−1) . Alphen …

【C++学习】STL容器——list

目录 一、list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list 迭代器失效 二、list的模拟实现 2.1 模拟实现list 三、list和vector的对比…

Redis数据一致性问题的三种解决方案

Redis数据一致性问题的三种解决方案 1、首先redis是什么 Redis&#xff08;Remote Dictionary Server )&#xff0c;是一个高性能的基于Key-Value结构存储的NoSQL开源数据库。大部分公司采用Redis来实现分布式缓存&#xff0c;用来提高数据查询效率。 2、为什么会选Redis 在…

浅析 C 语言的共用体、枚举和位域

前言 最近在尝试阅读一些系统库的源码&#xff0c;但是其中存在很多让我感到既熟悉又陌生的语法。经过资料查阅&#xff0c;发现是 C 语言中的共用体和位域。于是&#xff0c;趁着课本还没有扔掉&#xff0c;将一些相关的知识点记录在本文。 文章目录 前言共用体 (union)枚举…

MySQL建表和增添改查

1.创建一个名为mydb的数据库 mysql> show database mydb; 查询 mysql> show database mydb; 2.创建一个学生信息表 mysql> create table mydb.student_informtion( -> student_id int UNSIGNED NOT NULL PRIMARY KEY, //非空&#xff08;不允许为空&#xff0…

图像 检测 - DETR: End-to-End Object Detection with Transformers (arXiv 2020)

图像 检测 - DETR: End-to-End Object Detection with Transformers - 端到端目标检测的Transformers&#xff08;arXiv 2020&#xff09; 摘要1. 引言2. 相关工作2.1 集预测2.2 Transformers和并行解码2.3 目标检测 3. DETR模型References 声明&#xff1a;此翻译仅为个人学习…

【数学】3、动态规划

文章目录 一、原理1.1 如何想到dp 二、案例2.1 编辑距离2.1.1 状态转移2.1.2 状态转移方程和编程实现 2.2 钱币组合 一、原理 接着文本搜索的话题&#xff0c;来聊聊查询推荐&#xff08;Query Suggestion&#xff09;的实现过程&#xff0c;以及它所使用的数学思想&#xff0…

【Java】Springboot脚手架生成初始化项目代码

Springboot配置生成初始化项目代码可以通过mvn的mvn archetype:generate 和阿里云原生应用脚手架&#xff08;地址&#xff09;、spring官方提供的start初始化生成页面(地址&#xff09;。 1、mvn archetype:generate 通过mvn选择对应的脚手架可以快速生成初始化代码&#xf…

LangChain+ChatGLM整合LLaMa模型(二)

开源大模型语言LLaMa LLaMa模型GitHub地址添加LLaMa模型配置启用LLaMa模型 LangChainChatGLM大模型应用落地实践&#xff08;一&#xff09; LLaMa模型GitHub地址 git lfs clone https://huggingface.co/huggyllama/llama-7b添加LLaMa模型配置 在Langchain-ChatGLM/configs/m…

无涯教程-Lua - 调试语句

Lua提供了一个调试库&#xff0c;该库提供了所有原始函数供无涯教程创建自己的调试器。即使没有内置的Lua调试器&#xff0c;也有许多针对Lua的调试器&#xff0c;这些调试器由各种开发人员创建&#xff0c;其中许多开源。 下表列出了Lua调试库中可用的函数及其用法。 Sr.No.…

Gogs Git windos服务搭建指南

Gogs Git服务器搭建指南 背景&#xff1a; 近期在Linux 麒麟 v10 系统上开发&#xff1b;为了团队协同编程&#xff1b;选用了Git服务器&#xff1b;之前在windos开始时候使用的visualSVN server; visualSVN server从4.x.x.x开始收费&#xff1b;限制15个开发者用户&#xff…

【100天精通python】Day23:正则表达式,基本语法与re模块详解示例

目录 专栏导读 1 正则表达式概述 2 正则表达式语法 2.1 正则表达式语法元素 2.2 正则表达式的分组操作 3 re 模块详解与示例 4 正则表达式修饰符 专栏导读 专栏订阅地址&#xff1a;https://blog.csdn.net/qq_35831906/category_12375510.html 1 正则表达式概述 python 的…