【问题标题】:How are Spring Data repositories actually implemented?Spring Data 存储库是如何实际实现的?
【发布时间】:2016-11-25 09:06:46
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: java spring spring-data repository-pattern ddd-repositories


    【解决方案1】:

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

    1. 如果存储库已使用自定义实现部分进行了初始化(有关详细信息,请参阅that part of the reference documentation),并且调用的方法在该类中实现,则调用将路由到那里。
    2. 如果方法是查询方法(请参阅DefaultRepositoryInformation 了解如何确定),存储特定的查询执行机制将启动并执行确定为在启动时为该方法执行的查询。为此,存在一种解决机制,该机制试图识别在不同位置显式声明的查询(在方法上使用@Query,JPA 命名查询)最终退回到从方法名称派生的查询。查询机制检测见JpaQueryLookupStrategy。查询推导的解析逻辑可以在PartTree 中找到。可以看到商店特定于实际查询的翻译,例如在JpaQueryCreator。
    3. 如果上述方法均不适用,则执行的方法必须是由特定于商店的存储库基类(在 JPA 的情况下为 SimpleJpaRepository)实现的方法,并且调用被路由到该实例的实例中。

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

    这些代理的创建被封装到一个标准的基于 Java 的工厂模式实现中。高级代理创建可以在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 extension 的集成,以便 Spring Data 可以在普通 CDI 场景中使用。

    【讨论】:

    • 嗨 Oliver,你能详细说明一下 spring 是如何发现 @Repository 带注释的接口的吗?查看RepositoryFactorySupport#getRepository() 表明它将接口类作为参数,因此必须在其他地方发现它。我特别想弄清楚如何找到一个带注释的接口并自动生成一个实现该接口的 JDK 代理 bean,非常类似于 spring-data,但用于与存储库无关的特定于应用程序的目的。
    • 您可能想看看RepositoryComponentProvider。不会自动发生任何事情,而是对某些类型(带注释或带有注释)进行组件扫描,并为每个类型配置一个 FactoryBean。
    • 抱歉评论一个旧线程,但很好奇......存储库代理是单例对象吗?我们看到了一个问题,即我们的代码试图调用 repo 方法,但它似乎永远无法在代理上进行调用。它只是挂起。我想知道它是否在等待一个忙碌的单身人士。
    猜你喜欢
    • 2015-02-17
    • 2016-07-04
    • 2015-02-13
    • 2016-12-30
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    相关资源
    最近更新 更多