如何为 @Query 设置 SpEL contextProvider

How to setup SpEL contextProvider for @Query

如何设置上下文提供程序以使我的 SpEL 扩展在 @Query("...") 中可用?

我们在 postgres 中使用 r2dbc。

据我所知,我需要使用 ReactiveExtensionAwareQueryMethodEvaluationContextProvider 注册我的扩展程序,但我找不到任何关于如何执行此操作的文档。是否有特定的 bean 或 @Configuration 可用于它?

我找到了this unit test。它是 monogo 而不是 r2dbc,但我认为它是相同的原理 - 但是因为它是一个单元测试,所以它没有显示如何在我的正常代码中设置上下文。

试图像这样实现它并没有在 SpEL

中提供 my() 方法
@Configuration
class MySpELExtensionConf {

  @Bean
  fun mySpELExtension() : ReactiveEvaluationContextExtension {
    return ReactiveSpelExtension()
  }

  class ReactiveSpelExtension : ReactiveEvaluationContextExtension {

    override fun getExtension(): Mono<out EvaluationContextExtension> {
      return Mono.just(SpelExtension.INSTANCE)
    }

    override fun getExtensionId(): String {
      ReactiveQueryMethodEvaluationContextProvider.DEFAULT
      return "mySpELExtension"
    }
  }

  enum class SpelExtension : EvaluationContextExtension {
    INSTANCE;

    override fun getRootObject(): Any? {
      return this
    }

    override fun getExtensionId(): String {
      return "mySpELExtension"
    }

    fun my(): String {
      return "x"
    }
  }
}

我现在在应用程序上下文中看到 mySpELExtension,但是无法在 @Query 中使用 my() 方法:

interface MyRepository : ReactiveCrudRepository<MyEntity, Long> {
  @Query("""
    ...
    :#{my()}
    ...
  """)
  fun findByMyQuery() : Flux<MyEntity>
}

结果

EL1004E: Method call: Method my() cannot be found on type java.lang.Object[]

我找到了使用 PostBeanProcessor 的解决方案。

@Configuration
class MySpELExtensionConf {

  companion object {
    // list of provided extensions
    val contextProviderWithExtensions =
      ReactiveExtensionAwareQueryMethodEvaluationContextProvider(listOf(ReactiveSpelExtension()))
  }

  /**
   * Registers the customizer to the context to make spring aware of the bean post processor.
   */
  @Bean
  fun spELContextInRepositoriesCustomizer(): AddExtensionsToRepositoryBeanPostProcessor {
    return AddExtensionsToRepositoryBeanPostProcessor()
  }

  /**
   * Sets the [contextProviderWithExtensions] for SpEL in the [R2dbcRepositoryFactoryBean]s which makes the extensions
   * usable in `@Query(...)` methods.
   */
  class AddExtensionsToRepositoryBeanPostProcessor : BeanPostProcessor {
    override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any {
      if (bean is R2dbcRepositoryFactoryBean<*, *, *>) {
        bean.addRepositoryFactoryCustomizer { it.setEvaluationContextProvider(contextProviderWithExtensions) }
      }
      return bean
    }
  }

 // ... extension implementation as in question ...
}

请注意,自定义 R2dbcRepositoryFactoryBean 适用于 CoroutineCrudRepository,您可能需要根据您使用的存储库自定义不同的工厂 bean。