Spring Data存储库是如何实际实现的?

dev*_*per 93 java spring ddd-repositories repository-pattern spring-data

我已经在我的项目中使用Spring Data JPA存储库一段时间了,我知道以下几点:

  • 在存储库接口中,我们可以添加类似的方法findByCustomerNameAndPhone()(假设customerNamephone是域对象中的字段).
  • 然后,Spring通过在运行时(在应用程序运行期间)实现上述存储库接口方法来提供实现.

我感兴趣的是如何对它进行编码,我已经查看了Spring JPA源代码和API,但我找不到以下问题的答案:

  1. 如何在运行时和方法中生成并注入存储库实现类?
  2. Spring Data JPA是否使用CGlib或任何字节码操作库来实现方法并动态注入?

您能否帮助解决上述问题并提供任何支持的文档?

Oli*_*ohm 119

首先,没有代码生成,这意味着:没有CGLib,根本没有字节代码生成.基本方法是使用Spring的ProxyFactoryAPI以编程方式创建JDK代理实例以支持接口,并MethodInterceptor拦截对实例的所有调用并将方法路由到适当的位置:

  1. 如果已使用自定义实现部件初始化存储库(有关详细信息,请参阅参考文档的该部分),并且在该类中实现调用的方法,则将呼叫路由到那里.
  2. 如果该方法是查询方法(参见DefaultRepositoryInformation如何确定),则特定于商店的查询执行机制将启动并执行确定在启动时为该方法执行的查询.为此,尝试在各个地方(使用@Query方法,JPA命名查询)识别显式声明的查询,最终回退到方法名称的查询派生.有关查询机制检测,请参阅JpaQueryLookupStrategy.可以在中找到查询派生的解析逻辑PartTree.例如,可以在例如中看到商店特定的翻译成实际查询JpaQueryCreator.
  3. 如果以上都不适用,则执行的方法必须是由特定SimpleJpaRepository于商店的存储库基类(在JPA的情况下)实现的,并且将调用路由到该实例中.

实现该路由逻辑的方法拦截器,QueryExecutorMethodInterceptor可以在这里找到高级路由逻辑.

这些代理的创建被封装到基于标准Java的Factory模式实现中.可以在中找到高级代理创建RepositoryFactorySupport.然后,特定于商店的实现添加必要的基础架构组件,以便JPA可以继续编写如下代码:

EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);
Run Code Online (Sandbox Code Playgroud)

我明确提到的原因是,应该清楚的是,在其核心中,该代码中的任何内容都不需要Spring容器首先运行.它需要Spring作为类路径上的库(因为我们不喜欢重新发明轮子),但通常是容器不可知的.

为了简化与DI容器的集成,我们当然会构建与Spring Java配置,XML命名空间以及CDI扩展的集成,以便Spring Data可以用于普通的CDI场景.

  • 嗨,奥利弗,你能详细说明春天如何发现`@ Repository`注释界面吗?查看`RepositoryFactorySupport#getRepository()`表明它将接口类作为参数,因此必须在其他地方发现它.我特别想弄清楚如何找到一个带注释的接口并自动生成一个实现接口的JDK代理bean,非常像spring-data,但是用于与存储库无关的特定于应用程序的目的. (3认同)
  • 您可能想看看 [`RepositoryComponentProvider`](https://github.com/spring-projects/spring-data-commons/blob/0237760345faa011bdc6639ec838790be3d41e9b/src/main/java/org/springframework/data/repository/配置/RepositoryComponentProvider.java#L60-L79)。没有自动发生的事情,而是对某些类型(带注释或带有注释)的组件扫描以及为每种类型配置的“FactoryBean”。 (2认同)
  • 抱歉在一个旧线程上发表评论,但很好奇...存储库代理是单例对象吗?我们看到一个问题,我们的代码试图调用repo方法,但似乎永远无法在代理上进行调用。它只是挂了。我想知道是否正在等待一个单身人士忙。 (2认同)