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 {

    @Autowired
    private AccountRepository accountRepository;

    @Test
    public 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)

	@Override
	public 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));
	}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

压力测试与测试工具jmeter的介绍

目录 一、性能指标 二、jmeter &#xff08;一&#xff09;JMeter 安装 &#xff08;二&#xff09;JMeter 压测示例 1、添加线程组 2、添加 HTTP 请求 3、添加监听器 4、启动压测&查看分析结果 &#xff08;三&#xff09;JMeter Address Already in use 错误解决 压力测…

【ChatGPT 指令大全】怎么使用ChatGPT写履历和通过面试

目录 怎么使用ChatGPT写履历 寻求履历的反馈 为履历加上量化数据 把经历修精简 为不同公司客制化撰写履历 怎么使用ChatGPT通过面试 汇整面试题目 给予回馈 提供追问的问题 用 STAR 原则回答面试问题 感谢面试官的 email 总结 在职场竞争激烈的今天&#xff0c;写一…

【逗老师的PMP学习笔记】5、项目范围管理

目录 一、规划范围管理二、收集需求1、【关键工具】头脑风暴2、【关键工具】访谈3、【关键工具】问卷调查4、【关键工具】标杆对照&#xff08;对标&#xff09;5、【关键工具】亲和图和思维导图6、【关键工具】质量功能展开7、【关键工具】用户故事8、【关键工具】原型法9、【…

软件测试缺陷报告

缺陷报告是描述软件缺陷现象和重现步骤地集合。软件缺陷报告Software Bug Report&#xff08;SBR&#xff09;或软件问题报告Software Problem Report&#xff08;SPR&#xff09; 作用&#xff1a;缺陷报告是软件测试人员的工作成果之一&#xff0c;体现软件测试的价值缺陷报…

传染病学模型 | Python实现基于SIR模型分析Covid19爆发

效果一览 文章概述 传染病学模型 | Python实现基于SIR 模型分析Covid19爆发 源码设计 import jax.numpy as npimport matplotlib.pyplot

数据结构刷题训练——链表篇(一)

目录 前言 题目一&#xff1a;链表的中间节点 思路 分析 题解 题目二&#xff1a;链表中倒数第k个结点 思路 分析 题解 题目三&#xff1a;合并两个有序链表 思路 分析 题解 方法二 题解 题目四&#xff1a;链表的回文结构 思路 分析 题解 总结 前言 今天我将开…

3.netty和protobuf

1.ChannelGroup可以免遍历由netty提供,覆盖remove方法即可触发删除channel\ 2.群聊私聊 13.群聊私聊简单原理图 3.netty心跳检测机制,客户端对服务器有没有读写(读,写空闲) //IdleStateHandler(3,5,7,TimeUnite.SECONDS)是netty提供的检测状态的处理器,也加到pipeline,读,写,…

Spring IOC

◆ 传统Javaweb开发的困惑 ◆ IoC、DI和AOP思想提出 ◆ Spring框架的诞生 Spring | Home IOC控制反转&#xff1a;BeanFactory 快速入门 package com.xiaolin.service.Impl;import com.xiaolin.dao.UserDao; import com.xiaolin.service.UserService;public class UserServic…

javaWeb项目--二级评论完整思路

先来看前端需要什么吧&#xff1a; 通过博客id&#xff0c;首先需要显示所有一级评论&#xff0c;包括评论者的头像&#xff0c;昵称&#xff0c;评论时间&#xff0c;评论内容 然后要显示每个一级评论下面的二级评论&#xff0c;包括&#xff0c;评论者的头像&#xff0c;昵称…

CS 144 Lab Six -- building an IP router

CS 144 Lab Six -- building an IP router 引言路由器的实现测试 对应课程视频: 【计算机网络】 斯坦福大学CS144课程 Lab Six 对应的PDF: Lab Checkpoint 5: building an IP router 引言 在本实验中&#xff0c;你将在现有的NetworkInterface基础上实现一个IP路由器&#xf…

贝叶斯学习

贝叶斯 贝叶斯学习的背景贝叶斯定理举例 概览选择假设— MAPMAP举例 选择假设 — 极大似然 MLML 举例: 抛硬币问题 极大似然 & 最小二乘Nave Bayesian Classifier (朴素贝叶斯分类器)举例1&#xff1a;词义消歧 (Word Sense Disambiguation)举例 2: 垃圾邮件过滤 从垃圾邮件…

小程序自定义tabBar+Vant weapp

1.构建npm&#xff0c;安装Vant weapp&#xff1a; 1&#xff09;根目录下 &#xff0c;初始化生成依赖文件package.json npm init -y 2&#xff09;安装vant # 通过 npm 安装 npm i vant/weapp -S --production 3&#xff09;修改 package.json 文件 开发者工具创建的项…

使用Idea提交项目到远程仓库

使用Idea提交项目到远程仓库 1.在Idea中打开本地要推送的项目2.创建远程仓库并提交 1.在Idea中打开本地要推送的项目 tips: 首先你得有git工具&#xff0c;没有的话可以参考下面的这篇文章 git与gitee结合使用&#xff0c;提交代码&#xff0c;文件到远程仓库 从导航栏中选择 V…

阿里云ssl免费数字证书快过期 如何更换

1.登陆阿里云 找到ssl 查看快过期的证书 数字证书管理服务-ssl证书 2.创建免费的证书&#xff0c;对应过期证书的域名 3.下载新证书 pem key放在本地 此处记录本地的下载路径 /Users/dorsey/Downloads/10791167_lzzabc.cn_nginx/lzzabc.cn.pem /Users/dorsey/Downloads/1…

maven的下载与安装

文章目录 1 官网下载地址2 设置环境变量3 设置仓库地址4 添加阿里云的中央镜像 1 官网下载地址 https://maven.apache.org/ 下载 2 设置环境变量 MAVEN_HOME PATH mvn -v验证 3 设置仓库地址 仓库地址 4 添加阿里云的中央镜像 阿里云中央镜像

Python(三)

诚信像一面镜子&#xff0c;一旦打破&#xff0c;你的人格就会出现裂痕。 存在短路的情景 谢谢观看 Python(三)

Kernel Exception导致手机重启案例分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、高温触发 Kernel Exception 重启问题二、解决方案三、提高电池温度方案 一、 高温触发 Kernel Exception 重启问题 手机 电池温度 默认60度以上高温…

静态网页加速器:优化性能和交付速度的 Node.js 最佳实践

如何使用 Node.js 发布静态网页 在本文中&#xff0c;我们将介绍如何使用 Node.js 来发布静态网页。我们将创建一个简单的 Node.js 服务器&#xff0c;将 HTML 文件作为响应发送给客户端。这是一个简单而灵活的方法&#xff0c;适用于本地开发和轻量级应用。 1、创建静态网页…

mongodb-win32-x86_64-2008plus-ssl-3.6.23-signed.msi

Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation。保留所有权利。C:\Users\Administrator>cd C:\MongoDB\Server\3.6\binC:\MongoDB\Server\3.6\bin> C:\MongoDB\Server\3.6\bin> C:\MongoDB\Server\3.6\bin>mongod --dbpath C:\Mongo…

使用Golang实现一套流程可配置,适用于广告、推荐系统的业务性框架——简单应用

在诸如广告、推荐等系统中&#xff0c;我们往往会涉及过滤、召回和排序等过程。随着系统业务变得复杂&#xff0c;代码的耦合和交错会让项目跌入难以维护的深渊。于是模块化设计是复杂系统的必备基础。这篇文章介绍的业务框架脱胎于线上多人协作开发、高并发的竞价广告系统&…