Я пытаюсь создать приложение GraphQL Spring Boot для создания слоя GraphQL поверх существующего REST Web API, и у меня возникают проблемы при обработке HashMap в схеме.
Класс Timeline
имеет поле с именем dataReference
, которое является HashMap. Я попытался определить его в схеме graphql как список типа DataReference
, который является парой ключ / значение, но я получаю следующую ошибку:
Exception in thread "main" org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:155)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:540)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.foo.bar.graphql.AppLauncher.main(AppLauncher.java:43)
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:125)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:86)
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:414)
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:174)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152)
... 8 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'servletRegistrationBean' defined in com.foo.bar.graphql.AppLauncher: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'servletRegistrationBean' threw exception; nested exception is com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:625)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:236)
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:224)
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:100)
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:88)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:250)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:237)
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:54)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5098)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1429)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:944)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:839)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1429)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:944)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:261)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:422)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:770)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:370)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:106)
... 13 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'servletRegistrationBean' threw exception; nested exception is com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:620)
... 55 more
Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Unable to match type definition (ListType{type=TypeName{name='DataReference'}}) with java type (java.util.Map<java.lang.String, java.lang.String>): Java class is not a List or generic type information was lost: java.util.Map<java.lang.String, java.lang.String>
at com.coxautodev.graphql.tools.TypeClassMatcher.error(TypeClassMatcher.kt:22)
at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:65)
at com.coxautodev.graphql.tools.TypeClassMatcher.match(TypeClassMatcher.kt:28)
at com.coxautodev.graphql.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:215)
at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueueItemForPotentialMatches(SchemaClassScanner.kt:206)
at com.coxautodev.graphql.tools.SchemaClassScanner.scanQueue(SchemaClassScanner.kt:103)
at com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:81)
at com.coxautodev.graphql.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:149)
at com.coxautodev.graphql.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:155)
at com.foo.bar.graphql.AppLauncher.servletRegistrationBean(AppLauncher.java:55)
at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725.CGLIB$servletRegistrationBean$0(<generated>)
at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725$$FastClassBySpringCGLIB$$c7dabbfc.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.foo.bar.graphql.AppLauncher$$EnhancerBySpringCGLIB$$1ac15725.servletRegistrationBean(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 56 more
Это мой файл схемы:
extend type Query {
getFoo: Timeline
}
type Timeline {
dataReference: [DataReference]
}
type DataReference {
key: String
value: String
}
И мой модельный класс:
package com.foo.bar.graphql.model;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
@Getter @Setter
public class Timeline {
private Map<String, String> dataReference = new HashMap<>();
}
Я нашел эту тему Вернуть HashMap из GraphQL-Java , касающийся проблем с определением Maps в схеме. В принятом ответе вариант 1) не подходит для меня, поскольку модели взяты из существующего веб-API и не подлежат изменению. Моя попытка варианта 2 указана выше и не работает. Я также попробовал вариант 3 (используя скаляр) со следующим:
Схема:
extend type Query {
getFoo: Timeline
}
scalar Object
type Timeline {
dataReference: Object
}
Регистрация сервлета:
@Bean
public ServletRegistrationBean servletRegistrationBean() {
GraphQLSchema schema = SchemaParser.newParser()
.resolvers(fooResolver)
.file("graphql/foo.graphqls")
.scalars(ExtendedScalars.Object)
.build()
.makeExecutableSchema();
ExecutionStrategy executionStrategy = new AsyncExecutionStrategy();
GraphQLServlet servlet = new SimpleGraphQLServlet(schema, executionStrategy);
ServletRegistrationBean bean = new ServletRegistrationBean(servlet, "/graphql");
return bean;
}
pom.xml:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-extended-scalars</artifactId>
<version>1.0</version>
</dependency>
Но я получаю эту ошибку: Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Expected a user-defined GraphQL scalar type with name 'Object' but found none!
Я бы предпочел метод включения пары ключ / значение в схему, если кто-то может помочь мне в этом.