Spring 在 Groovy 中使用 ElasticSearch 启动:WebClient 不存在异常

Spring Boot with ElasticSearch in Groovy: WebClient not present Exception

我是 运行 一个小型演示 spring-boot 应用程序,我想将我的 url 路由与 elasticsearch 查询连接起来。该应用程序在这些 gradle 依赖项下启动得很好:

implementation('org.springframework.data:spring-data-elasticsearch')
implementation('org.elasticsearch:elasticsearch')
implementation('org.elasticsearch.client:elasticsearch-rest-high-level-client')
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.codehaus.groovy:groovy')

只要我添加 class ElasticsearchClientConfig(我还没有使用,刚刚加载):

package me.spring.GroovyDemo.store

import me.spring.GroovyDemo.AppConstants
import org.elasticsearch.client.RestHighLevelClient
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.elasticsearch.client.ClientConfiguration
import org.springframework.data.elasticsearch.client.RestClients
import org.springframework.data.elasticsearch.core.ElasticsearchOperations
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories

@Configuration
@EnableElasticsearchRepositories(basePackages = "me.spring.GroovyDemo")
@ComponentScan(basePackages = ["me.spring.GroovyDemo"])
class ElasticsearchClientConfig {
    @Bean
    RestHighLevelClient client() {
        ClientConfiguration clientConfiguration
                = ClientConfiguration.builder()
                .connectedTo(AppConstants.ELASTIC_SERVER)
                .build()

        return RestClients.create(clientConfiguration).rest()
    }

    @Bean
    ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(client())
    }
}

应用启动失败。 我发现了很多类似的问题。但其中 none 似乎因缺少 Webclient 而失败。我真的不明白我首先需要一个 WebClient 。 异常情况如下:

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.0)

2021-05-27 12:02:32.598  INFO 4462 --- [           main] m.s.GroovyDemo.GroovyDemoApplication     : Starting GroovyDemoApplication using Java 1.8.0_292 on vagrant-VirtualBox with PID 4462 (/home/vagrant/GroovyOpenApi/out/production/classes started by vagrant in /home/vagrant/GroovyOpenApi)
2021-05-27 12:02:32.600  INFO 4462 --- [           main] m.s.GroovyDemo.GroovyDemoApplication     : No active profile set, falling back to default profiles: default
2021-05-27 12:02:32.890  WARN 4462 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder    : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
2021-05-27 12:02:33.230  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Elasticsearch repositories in DEFAULT mode.
2021-05-27 12:02:33.240  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 6 ms. Found 0 Elasticsearch repository interfaces.
2021-05-27 12:02:33.402  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Elasticsearch repositories in DEFAULT mode.
2021-05-27 12:02:33.404  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 1 ms. Found 0 Elasticsearch repository interfaces.
2021-05-27 12:02:33.407  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Reactive Elasticsearch repositories in DEFAULT mode.
2021-05-27 12:02:33.409  INFO 4462 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 1 ms. Found 0 Reactive Elasticsearch repository interfaces.
2021-05-27 12:02:33.799  INFO 4462 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-05-27 12:02:33.807  INFO 4462 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-05-27 12:02:33.807  INFO 4462 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.46]
2021-05-27 12:02:33.861  INFO 4462 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-05-27 12:02:33.861  INFO 4462 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1065 ms
2021-05-27 12:02:34.035  WARN 4462 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client' defined in class path resource [me/spring/GroovyDemo/store/ElasticsearchClientConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.RestHighLevelClient]: Factory method 'client' threw exception; nested exception is java.lang.TypeNotPresentException: Type org.springframework.web.reactive.function.client.WebClient not present
2021-05-27 12:02:34.038  INFO 4462 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2021-05-27 12:02:34.054  INFO 4462 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-05-27 12:02:34.078 ERROR 4462 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client' defined in class path resource [me/spring/GroovyDemo/store/ElasticsearchClientConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.RestHighLevelClient]: Factory method 'client' threw exception; nested exception is java.lang.TypeNotPresentException: Type org.springframework.web.reactive.function.client.WebClient not present
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:335) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.7.jar:5.3.7]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.7.jar:5.3.7]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) [spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) [spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) [spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) [spring-boot-2.5.0.jar:2.5.0]
    at org.springframework.boot.SpringApplication$run.call(Unknown Source) [spring-boot-2.5.0.jar:2.5.0]
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148) [groovy-3.0.8.jar:3.0.8]
    at me.spring.GroovyDemo.GroovyDemoApplication.main(GroovyDemoApplication.groovy:10) [classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.RestHighLevelClient]: Factory method 'client' threw exception; nested exception is java.lang.TypeNotPresentException: Type org.springframework.web.reactive.function.client.WebClient not present
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.7.jar:5.3.7]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 23 common frames omitted
Caused by: java.lang.TypeNotPresentException: Type org.springframework.web.reactive.function.client.WebClient not present
    at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117) ~[na:1.8.0_292]
    at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125) ~[na:1.8.0_292]
    at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:1.8.0_292]
    at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68) ~[na:1.8.0_292]
    at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138) ~[na:1.8.0_292]
    at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49) ~[na:1.8.0_292]
    at sun.reflect.generics.repository.MethodRepository.getReturnType(MethodRepository.java:68) ~[na:1.8.0_292]
    at java.lang.reflect.Method.getGenericReturnType(Method.java:255) ~[na:1.8.0_292]
    at java.beans.FeatureDescriptor.getReturnType(FeatureDescriptor.java:370) ~[na:1.8.0_292]
    at java.beans.Introspector.getTargetEventInfo(Introspector.java:1052) ~[na:1.8.0_292]
    at java.beans.Introspector.getBeanInfo(Introspector.java:427) ~[na:1.8.0_292]
    at java.beans.Introspector.getBeanInfo(Introspector.java:173) ~[na:1.8.0_292]
    at groovy.lang.MetaClassImpl.lambda$addProperties(MetaClassImpl.java:3460) ~[groovy-3.0.8.jar:3.0.8]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_292]
    at groovy.lang.MetaClassImpl.addProperties(MetaClassImpl.java:3460) ~[groovy-3.0.8.jar:3.0.8]
    at groovy.lang.MetaClassImpl.reinitialize(MetaClassImpl.java:3442) ~[groovy-3.0.8.jar:3.0.8]
    at groovy.lang.MetaClassImpl.initialize(MetaClassImpl.java:3435) ~[groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:273) ~[groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:315) ~[groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:258) ~[groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:987) ~[groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallStaticSite(CallSiteArray.java:71) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallSite(CallSiteArray.java:156) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) [groovy-3.0.8.jar:3.0.8]
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130) [groovy-3.0.8.jar:3.0.8]
    at me.spring.GroovyDemo.store.ElasticsearchClientConfig.client(ElasticsearchClientConfig.groovy:21) ~[classes/:na]
    at me.spring.GroovyDemo.store.ElasticsearchClientConfig$$EnhancerBySpringCGLIB$$a214165c.CGLIB$client[=14=](<generated>) ~[classes/:na]
    at me.spring.GroovyDemo.store.ElasticsearchClientConfig$$EnhancerBySpringCGLIB$$a214165c$$FastClassBySpringCGLIB$$c29866d5.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.7.jar:5.3.7]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.7.jar:5.3.7]
    at me.spring.GroovyDemo.store.ElasticsearchClientConfig$$EnhancerBySpringCGLIB$$a214165c.client(<generated>) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_292]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_292]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_292]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_292]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.7.jar:5.3.7]
    ... 24 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.web.reactive.function.client.WebClient
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_292]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_292]
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352) ~[na:1.8.0_292]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_292]
    at java.lang.Class.forName0(Native Method) ~[na:1.8.0_292]
    at java.lang.Class.forName(Class.java:348) ~[na:1.8.0_292]
    at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114) ~[na:1.8.0_292]
    ... 60 common frames omitted


Process finished with exit code 1

不知何故我无法创建 ES 客户端 bean。 我的应用程序在包 me.spring.GroovyDemo 中,其他所有内容都在下面。 我尝试添加一些依赖项,例如 webflux 以获取 WebClient,但并没有真正帮助。

启动失败是什么原因?

从堆栈跟踪可以看出,您应该将 spring-web-reactive 缺少的依赖项添加到 Gradle 配置中:

dependencies {
    //...
    implementation 'org.springframework:spring-web-reactive:5.0.0.M4'
}

我通过添加

得到了你的例子运行
implementation('org.springframework:spring-webflux')

WebClient class 的引用从何而来? Spring 数据 Elasticsearch 构建时具有对 org.springframework:spring-webflux 可选 依赖性,因为它包含命令式和反应式设置的代码。 可选 因为我们需要它来构建库,但是当 运行 - 在命令模式下你不需要它。

现在 ClientConfiguration class 及其构建器有一个方法 withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer)。此函数编译入库

当 运行 一个命令式、非反应性应用程序在 Java 或 Kotlin 中的 class 路径中没有 webflux 时,一切都很好 - 应用程序本身从未被编译为使用此函数,因此不需要它,并且传递可选依赖项永远不会被解析。只要不使用这些可选依赖项,它们是否在加载的 class 中并不重要 - 否则编译器会抱怨。

在Groovy中这似乎有所不同。这里有一些 Java-bean inspection 运行 on the ClientConfiguration class 找到上述方法但找不到 WebClient class,因为那不在 class 路径上。

因此,通过将依赖项添加到 class 路径,此错误会因在 class 路径中具有具有反应性代码的库的成本而消失 - 现在一些 Spring 引导自动配置可能会想从那里捡点东西。

我不知道什么是更好的解决方案,或者如果 groovy 可以更好地处理可选依赖项,我不会使用 groovy。有更多见解的人可能会有更好的答案。