导读:

什么是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,提供了一套标准接口和方法。然后基于各个数据库的的驱动来与数据库进行交互。

源码

以一个简单的方法作为切入口了解一下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.TaskExecutionAutoConfigurationorg.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnBean=javax.sql.DataSourceorg.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration.ConditionalOnClass=org.springframework.data.jpa.repository.JpaRepository
通过上述配置可知:要有数据源,要有JpaRepository以及HibernateJpaAutoConfiguration等因为JPA是基于Hibernate来的,所以需要先有Hibernate所以也就必须先进行HibernateJpaAutoConfiguration自动配置

整体流程

整体分析源码的流程是,围绕三个问题展开的,问题如下:
1.为什么我自己定义的AccountRepository接口的类型为SimpleJpaRepository,这是从哪里来的” />整体流程如下:

具体源码分析如下

关于Spring Data Jpa的源码, 我们可以通过一个简单的例子来作为切入口,例子如下
// 写一个单元测试的类@RunWith(SpringRunner.class)@SpringBootTestpublic 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 repositoryInterface, RepositoryFragments fragments)方法进一步查看代码

再看这个方法getRepositoryInformation(metadata, composition) --> getRepositoryBaseClass(metadata)

在初始化this.repository的时候,就固定将repository固定设置为SimpleJpaRepository类型了。
到这里就解释了为什么是SimpleJpaRepository类型了,回答了最开始提出的第一个问题。

7. AccountRepository代理对象的创建

此时回到RepositoryFactorySupport.getRepository(Class 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. * 

Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. *

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 " />.find(domainType, id, hints) : em.find(domainType, id, type, hints));}