Spring 数据存储库实际是如何实现的?
How are Spring Data repositories actually implemented?
我在项目中使用 Spring Data JPA 存储库已有一段时间,我知道以下几点:
- 在存储库接口中,我们可以添加像
findByCustomerNameAndPhone()
这样的方法(假设customerName
和phone
是域对象中的字段)。
- 然后,Spring通过在运行时间(在申请运行期间)实现上述存储库接口方法来提供实现。
我对它的编码方式很感兴趣,我查看了 Spring JPA 源代码和 API,但我找不到以下问题的答案:
- 如何在 运行 时间生成存储库实现 class 并实现和注入方法?
- SpringData JPA 是否使用 CGlib 或任何字节码操作库来实现方法并动态注入?
能否请您帮助解决上述问题并提供任何支持的文档?
首先,没有代码生成,这意味着:没有 CGLib,根本没有字节代码生成。基本方法是使用 Spring 的 ProxyFactory
API 以编程方式创建 JDK 代理实例以支持接口,并且 MethodInterceptor
拦截对实例并将方法路由到适当的位置:
- 如果存储库已使用自定义实现部分进行初始化(有关详细信息,请参阅 that part of the reference documentation),并且调用的方法是在该 class 中实现的,调用将路由到那里。
- 如果方法是查询方法(参见
DefaultRepositoryInformation
for 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 @Query
on 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
。
- 如果上面的 none 应用执行的方法必须是由商店特定的存储库基础 class (
SimpleJpaRepository
在 JPA 的情况下)实现的方法并且调用是路由到那个实例。
实现该路由逻辑的方法拦截器是QueryExecutorMethodInterceptor
,可以找到高级路由逻辑here。
这些代理的创建被封装到基于标准 Java 的工厂模式实现中。高级代理创建可以在 RepositoryFactorySupport
中找到。然后,特定于商店的实现添加必要的基础结构组件,以便您可以继续为 JPA 编写如下代码:
EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);
我明确提到这一点的原因是,应该清楚的是,在其核心中,该代码首先不需要 Spring 容器到 运行。它需要 Spring 作为 class 路径上的库(因为我们不想重新发明轮子),但通常与容器无关。
为了简化与 DI 容器的集成,我们当然已经构建了与 Spring Java 配置、XML 命名空间以及 CDI extension 的集成,从而Spring数据可以用于普通CDI场景
我在项目中使用 Spring Data JPA 存储库已有一段时间,我知道以下几点:
- 在存储库接口中,我们可以添加像
findByCustomerNameAndPhone()
这样的方法(假设customerName
和phone
是域对象中的字段)。 - 然后,Spring通过在运行时间(在申请运行期间)实现上述存储库接口方法来提供实现。
我对它的编码方式很感兴趣,我查看了 Spring JPA 源代码和 API,但我找不到以下问题的答案:
- 如何在 运行 时间生成存储库实现 class 并实现和注入方法?
- SpringData JPA 是否使用 CGlib 或任何字节码操作库来实现方法并动态注入?
能否请您帮助解决上述问题并提供任何支持的文档?
首先,没有代码生成,这意味着:没有 CGLib,根本没有字节代码生成。基本方法是使用 Spring 的 ProxyFactory
API 以编程方式创建 JDK 代理实例以支持接口,并且 MethodInterceptor
拦截对实例并将方法路由到适当的位置:
- 如果存储库已使用自定义实现部分进行初始化(有关详细信息,请参阅 that part of the reference documentation),并且调用的方法是在该 class 中实现的,调用将路由到那里。
- 如果方法是查询方法(参见
DefaultRepositoryInformation
for 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@Query
on the method, JPA named queries) eventually falling back to query derivation from the method name. For the query mechanism detection, seeJpaQueryLookupStrategy
. The parsing logic for the query derivation can be found inPartTree
. The store specific translation into an actual query can be seen e.g. inJpaQueryCreator
。 - 如果上面的 none 应用执行的方法必须是由商店特定的存储库基础 class (
SimpleJpaRepository
在 JPA 的情况下)实现的方法并且调用是路由到那个实例。
实现该路由逻辑的方法拦截器是QueryExecutorMethodInterceptor
,可以找到高级路由逻辑here。
这些代理的创建被封装到基于标准 Java 的工厂模式实现中。高级代理创建可以在 RepositoryFactorySupport
中找到。然后,特定于商店的实现添加必要的基础结构组件,以便您可以继续为 JPA 编写如下代码:
EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);
我明确提到这一点的原因是,应该清楚的是,在其核心中,该代码首先不需要 Spring 容器到 运行。它需要 Spring 作为 class 路径上的库(因为我们不想重新发明轮子),但通常与容器无关。
为了简化与 DI 容器的集成,我们当然已经构建了与 Spring Java 配置、XML 命名空间以及 CDI extension 的集成,从而Spring数据可以用于普通CDI场景