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


111

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

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

我对它的编码方式很感兴趣,并查看了Spring JPA源代码和API,但是找不到以下问题的答案:

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

您能否为上述查询提供帮助,并提供任何受支持的文档?

Answers:


144

首先,没有代码生成正在进行,这意味着:没有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);

我明确提到的原因是,应该清楚的是,从根本上讲,该代码中的任何内容都不需要Spring容器来运行。它需要Spring作为类路径上的库(因为我们不想重塑轮子),但是通常与容器无关。

为了简化与DI容器的集成,我们当然要与Spring Java配置,XML名称空间以及CDI扩展进行集成,以便可以在普通CDI场景中使用Spring Data。


3
嗨,奥利弗(Oliver),您能详细说明一下spring如何首先发现带@Repository注释的接口吗?看一下RepositoryFactorySupport#getRepository()它以接口类作为参数,因此必须在其他地方发现它。我特别想弄清楚如何找到带注释的接口,并自动生成一个实现该接口的JDK代理bean,非常类似于spring-data,但出于与存储库无关的特定于应用程序的目的。
克里斯·赖斯

1
您可能想看看RepositoryComponentProvider。没有自动的事情发生,而是对某些类型(带注释或带有注释)的组件进行扫描,并FactoryBean为每种类型进行配置。
奥利弗·德罗博姆

2
抱歉在一个旧线程上发表评论,但很好奇...存储库代理是单例对象吗?我们看到一个问题,我们的代码试图调用repo方法,但似乎永远无法在代理上进行调用。它只是挂了。我想知道它是否正在等待一个单身人士。
iu.david
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.