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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38509882/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 20:22:46  来源:igfitidea点击:

How are Spring Data repositories actually implemented?

javaspringspring-datarepository-patternddd-repositories

提问by developer

I have been working with Spring Data JPA repository in my project for some time and I know the below points:

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

  • In the repository interfaces, we can add the methods like findByCustomerNameAndPhone()(assuming customerNameand phoneare fields in the domain object).
  • Then, Spring provides the implementation by implementing the above repository interface methods at runtime (during the application run).
  • 在存储库接口中,我们可以添加诸如findByCustomerNameAndPhone()(假设customerNamephone是域对象中的字段)之类的方法。
  • 然后,Spring 通过在运行时(在应用程序运行期间)实现上述存储库接口方法来提供实现。

I am interested on how this has been coded and I have looked at the Spring JPA source code & APIs, but I could not find answers to the questions below:

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

  1. How is the repository implementation class generated at runtime & methods being implemented and injected?
  2. Does Spring Data JPA use CGlib or any bytecode manipulation libraries to implement the methods and inject dynamically?
  1. 在运行时生成的存储库实现类和方法是如何实现和注入的?
  2. Spring Data JPA 是否使用 CGlib 或任何字节码操作库来实现方法并动态注入?

Could you please help with the above queries and also provide any supported documentation ?

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

采纳答案by Oliver Drotbohm

First of all, there's no code generation going on, which means: no CGLib, no byte-code generation at all. The fundamental approach is that a JDK proxy instance is created programmatically using Spring's ProxyFactoryAPI to back the interface and a MethodInterceptorintercepts all calls to the instance and routes the method into the appropriate places:

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

  1. If the repository has been initialized with a custom implementation part (see that part of the reference documentationfor details), and the method invoked is implemented in that class, the call is routed there.
  2. If the method is a query method (see DefaultRepositoryInformationfor how that is determined), the store specific query execution mechanism kicks in and executes the query determined to be executed for that method at startup. For that a resolution mechanism is in place that tries to identify explicitly declared queries in various places (using @Queryon the method, JPA named queries) eventually falling back to query derivation from the method name. For the query mechanism detection, see JpaQueryLookupStrategy. The parsing logic for the query derivation can be found in PartTree. The store specific translation into an actual query can be seen e.g. in JpaQueryCreator.
  3. If none of the above apply the method executed has to be one implemented by a store-specific repository base class (SimpleJpaRepositoryin case of JPA) and the call is routed into an instance of that.
  1. 如果存储库已使用自定义实现部分初始化(有关详细信息,请参阅参考文档的该部分),并且调用的方法在该类中实现,则调用将路由到那里。
  2. 如果该方法是查询方法(请参阅DefaultRepositoryInformation有关如何确定的信息),则特定于商店的查询执行机制将启动并执行确定要在启动时为该方法执行的查询。为此,存在一种解析机制,该机制试图在不同的地方(@Query在方法上使用,JPA 命名查询)识别显式声明的查询,最终回退到从方法名称派生的查询。查询机制检测参见JpaQueryLookupStrategy。查询派生的解析逻辑可以在 中找到PartTree。可以在例如 中看到商店特定转换为实际查询JpaQueryCreator
  3. 如果以上都不适用,则执行的方法必须是由特定SimpleJpaRepository于商店的存储库基类(在 JPA 的情况下)实现的方法,并且调用将路由到该实例的实例中。

The method interceptor implementing that routing logic is QueryExecutorMethodInterceptor, the high level routing logic can be found here.

实现该路由逻辑的方法拦截器是QueryExecutorMethodInterceptor,可以在此处找到高级路由逻辑。

The creation of those proxies is encapsulated into a standard Java based Factory pattern implementation. The high-level proxy creation can be found in RepositoryFactorySupport. The store-specific implementations then add the necessary infrastructure components so that for JPA you can go ahead and just write code like this:

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

EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);

The reason I mention that explicitly is that it should become clear that, in its core, nothing of that code requires a Spring container to run in the first place. It needs Spring as a library on the classpath (because we prefer to not reinvent the wheel), but is container agnostic in general.

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

To ease the integration with DI containers we've of course then built integration with Spring Java configuration, an XML namespace, but also a CDI extension, so that Spring Data can be used in plain CDI scenarios.

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