前言

接上一篇 : JpaRepository 查询方法没有实现却可以正常使用

这是一个 我最开始看着 都挺奇怪的一个问题, 呵呵 存在一些 XXRepository的一些方法, 没有具体的方法实现, 但是 依然可以调用该方法 处理业务, 并且实现了 TaskCustomRepository 接口中的一部分自定义的接口 

呵呵 比如 如下方法, 可以再业务代码中直接 @Resource 注入 TaskRepository, 然后也可以正常的调用他的 existsByTopic 方法, 但是 这个方法 却没有具体的实现, 一时之间很让人迷惑 

TaskCustomRepository 这部分接口的行为 又是怎么约束的呢 ? 

这一切的处理 Spring 又是怎么帮助我们处理的呢? 

public interface TaskRepository extends JpaRepository<Task, String>, TaskCustomRepository {
    boolean existsByTopic(String topic);
}

关于 JpaRepositoriesAutoConfiguration

这部分的 TaskRepository 的实例来自于 JpaRepositoriesAutoConfiguration 的配置 

71 Spring 对于这部分 JpaRepository 的处理_configuration

来看一下 是什么加载了我们关注的这部分 Repository, 是 JpaRepositoriesAutoConfiguration @Import 的这个 JpaRepositoriesRegistrar 来处理的 

71 Spring 对于这部分 JpaRepository 的处理_jpa_02

extension 进行扫描寻找目标Repository, 可以看到看到这里 找到了 51 个 Repository, 然后依次 封装 BeanDefinition 注册到 registry 

71 Spring 对于这部分 JpaRepository 的处理_封装_03

搜索一下我们系统里面的 继承自 AbstractRepository[继承了 JpaRepository] 的实现, 刚好是 51 个 

71 Spring 对于这部分 JpaRepository 的处理_spring_04

extension 怎么加载的这部分目标 Repository 

呵呵 我们上面 虽然看了一下 系统中继承自 AbstractRepository 的实现有 51 个 刚好对应上 上面的 extension 加载的目标 Repository 的数量, 但是 这个毕竟只是推测, 呵呵 我们还需要看下 extension 的加载规则, 来确认这个事情 

extension 获取 candidates, 扫描的是配置的 basePackage 下面的所有的 class, filter 为 继承自 Repository 或者打上 @RepositoryDefinition 的并且没有打上 @NoRepositoryBean 的类型 

71 Spring 对于这部分 JpaRepository 的处理_加载_05

basePackage 的值来自哪里 ?

basePackage 来自于 SpringBootApplication 的启动类的配置, 这里的 metadata wrap的为 SpringBoot 的启动类型 

71 Spring 对于这部分 JpaRepository 的处理_jpa_06

以为我们是没有配置 basePackage的, 所以我们看到的是一个默认的行为, 那么这个默认行为是什么呢? 启动类所在的 backage 

71 Spring 对于这部分 JpaRepository 的处理_jpa_07

之所以会走上面的配置 basePackage 的这些行为, 其最初还是来自于 ConfigClass 的处理 

DcamsApplication 上面有 @SpringBootApplication, @SpringBootApplication 上面包含了 @EnableAutoConfiguration, @EnableAutoConfiguration 上面包含了 @AutoConfigurationPackage, @AutoConfigurationPackage 上面有一个  @Import(AutoConfigurationPackages.Registrar.class) 

71 Spring 对于这部分 JpaRepository 的处理_封装_08

扫描 candidates, 注册到 registry 

查询一下 Repository 的子类, 在 dcams 中其实也就那 51个 AbstractJpaRepository 的实现 + AbstractJpaRepository, AbstractJpaRepository 上面标记有 @NoRepositoryBean 

scanner 合计扫描出来的 candidates 就是这 51个类型  

拿到了这些东西之后, 上面一层 还有一层校验, 使用 ConfigurationInspectionClassLoader 加载 repositoryInterface, 以及下面的 isStrictRepositoryCandidate 的校验  

71 Spring 对于这部分 JpaRepository 的处理_jpa_09

我们来看一下 isStrictRepositoryCandidate, 是需要继承自  JpaRepository 或者打上 @Entity, @MappedSuperclass, 我们这里的 51个 candidates 显然都是满足条件的  

71 Spring 对于这部分 JpaRepository 的处理_封装_10

我们再回到最上面 创建 BeanDefinition, 注册到 registry 的地方, 呵呵 看一下, 这里的 BeanDefinition 的 beanClassName 为 JpaRepositoryBean 

71 Spring 对于这部分 JpaRepository 的处理_spring_11

我们这里 bean 明明是 taskRepostiroy 为什么 beanClassName 为 JpaRepositoryBean, 这个 JpaRepositoryBean 是从哪里配置进来的呢 

呵呵来自于 EnableJpaRepositoriesConfiguration 上面的注解, EnableJpaRepositories 基本山都取得是默认值, beanClassName 为 JpaRepositoryBean  

71 Spring 对于这部分 JpaRepository 的处理_configuration_12

TaskRepository 的实例的创建

容器创建实例的时候发现一部分 Service 依赖于 TaskRepository, 因此开始创建 TaskRepository 的实例, 这里是根据 JpaRepositoryFactoryBean 来创建 TaskRepository 的实例 

71 Spring 对于这部分 JpaRepository 的处理_configuration_13

最终 TaskRepository 的实例的创建是在 RepositoryFactorySupport, 它相当于是 RepositoryFactoryBean 的 beanFactory, 用于最终创建实例 

71 Spring 对于这部分 JpaRepository 的处理_configuration_14

以上就是关于我们的这个 TaskRepository 实例是如何创建的整个流程了 

关于 TaskCustomRepository 

另外一点 我们关注的是 TaskCustomRepository, 从代码中可以直接看到的是 TaskRepository 实现了 TaskCustomRepository 

那么业务代码中调用 TaskRepository 的 TaskCustomRepository 这部分的方法, 从我们的经验可以知道, 它是桥接到了 TaskCustomRepository 的具体实现, TaskRepositoryImpl,  他具体是怎么实现的呢? 

我们出发一个 TaskCustomRepository 的方法的调用来看看, 看下下面这张图  

最下面的是 业务代码 Service 里面调用 taskRepository.search, 经过了下面的 ImplementationMethodExecutionInterceptor 尝试在 repositoryComposition 中找到 search 方法的实现的对象 

这里找到的是 TaskRepositoryImpl, 并且调用了 TaskRepositoryImpl 实例的 search 方法, 然后返回 业务数据 

查询 TaskRepositoryImpl 是来自于 ImplementationMethodExecutionInterceptor 下面的 composite 下面的 fragments, 那么这个 fragments 又是来自于哪里呢? 

71 Spring 对于这部分 JpaRepository 的处理_configuration_15

再看 TaskRepository 的 BeanDefinition 的创建 

RepositoryBeanDefinitionBuilder 构建 BeanDefinition 的时候, 获取了一部分的 customImplementation, 主要是通过 这里获取的 

configuration.toLookupConfiguration 获取到一个 lookup, 表示的是具体的实现类, 查询一下 regsitry, 发现有这个 taskRepositoryImpl 的实现, 把他放到 beanDefinition 的 customImplementation 里面 

71 Spring 对于这部分 JpaRepository 的处理_jpa_16

我们再看看这个 lookup 的生成的方式 

beanName 来自于拼接, 相当于是约定 xxRepository + 配置的后缀[默认为 Impl] 

71 Spring 对于这部分 JpaRepository 的处理_封装_17

再看 TaskRepository 的实例的创建 

呵呵 这里创建了一个 ImplementationMethodExecutionInterceptor, 传入的 composition 封装了 TaskRepositoryImpl 实例 

71 Spring 对于这部分 JpaRepository 的处理_jpa_18

我们上面看到的 fragments 来自于上一个层级的 JpaRepositoryFactoryBean 里面的 customImplementation, 自然也就来自于前面的 beanDefinition 的 customImplementation

71 Spring 对于这部分 JpaRepository 的处理_spring_19

完