今後のプロジェクトでGraphQLをテストしたい。プロジェクトでは、Spring Boot、Spring Security、GraphQLを使用します。そこで、Spring Initializrのビルドを使用して、IntelliJで新しいSpring Boot Appを作成しました。もちろん、Spring Bootバージョンは最新(2.0.3.RELEASE)です。今度は、GraphQLとGraphiQLの依存関係を追加します。
<dependency>
<groupId>com.graphql-Java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.graphql-Java</groupId>
<artifactId>graphql-Java-tools</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.graphql-Java</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
これをテストするために、アプリケーションを実行しましたが、/graphql
終点。この場合は、application.properties
問題なし:
# GraphQL
graphql.servlet.mapping=/graphql
graphql.servlet.enabled=true
graphql.servlet.corsEnabled=true
# GraphiQL
graphiql.mapping=/graphiql
graphiql.endpoint=/graphql
graphiql.enabled=true
graphiql.cdn.enabled=true
graphiql.cdn.version=0.11.11
もう一度テストした後、エンドポイントはそこにあるので、スキーマやリゾルバーなどを書くことができました。これは私が実装したものです:
スキーマ:
schema {
query: Query
mutation: Mutation
}
type Greeting {
id: ID!
message: String!
}
type Query {
greetingsAll: [Greeting]
}
type Mutation {
greeting(message: String!): Greeting
}
モデル:
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@ToString
@Entity
public class Greeting {
@Id
@GeneratedValue
private Long id;
private String message;
}
リポジトリ
@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
}
QueryResolver
@Component
public class QueryResolver implements GraphQLQueryResolver {
@Autowired
private GreetingRepository greetingRepository;
public Greeting greeting(Long id) {
return greetingRepository.getOne(id);
}
public Iterable<Greeting> getGreetingsAll() {
return greetingRepository.findAll();
}
}
MutationResolver
@Component
public class MutationResolver implements GraphQLMutationResolver {
@Autowired
private GreetingRepository greetingRepository;
public Greeting newGreeting(String message) {
Greeting greeting = new Greeting();
greeting.setMessage(message);
return greetingRepository.save(greeting);
}
}
SpringBootのBeans
@SpringBootApplication
public class GraphqlApplication {
public static void main(String[] args) {
SpringApplication.run(GraphqlApplication.class, args);
}
@Bean
ApplicationRunner init(GreetingRepository greetingRepository) {
return args -> {
Stream.of("Hallo", "Guten Tag", "Moin").forEach(greeting -> greetingRepository.save(Greeting.builder().message(greeting).build()));
greetingRepository.findAll().forEach(System.out::println);
};
}
@Bean
public QueryResolver query() {
return new QueryResolver();
}
@Bean
public MutationResolver mutation() {
return new MutationResolver();
}
}
アプリケーションを再度テストして、GraphiQLを使用してクエリを実行できるかどうかを確認しようとしても、アプリケーションが起動しません。
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) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.Java:544) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.Java:140) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.Java:759) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.Java:395) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.Java:327) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.Java:1255) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.Java:1243) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at de.eno.prototyp.graphql.GraphqlApplication.main(GraphqlApplication.Java:14) [classes/:na]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.embedded.Tomcat.TomcatWebServer.initialize(TomcatWebServer.Java:126) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.web.embedded.Tomcat.TomcatWebServer.<init>(TomcatWebServer.Java:86) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.web.embedded.Tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.Java:413) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.web.embedded.Tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.Java:174) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.Java:179) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.Java:152) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
... 8 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServletRegistrationBean' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServletRegistrationBean' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServlet' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServlet' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.Java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.Java:1256)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServlet' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServlet' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.Java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.Java:1256) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 24 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.Java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.Java:1256) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 38 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.Java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:474)
... 52 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:590) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.Java:1256)
... 66 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.Java:185) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.Java:582) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
... 79 common frames omitted
Caused by: com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public Java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
at com.coxautodev.graphql.tools.FieldResolverScanner.findFieldResolver(FieldResolverScanner.kt:39) ~[graphql-Java-tools-5.2.0.jar:na]
at com.coxautodev.graphql.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:227) ~[graphql-Java-tools-5.2.0.jar:na]
at com.coxautodev.graphql.tools.SchemaClassScanner.handleRootType(SchemaClassScanner.kt:122) ~[graphql-Java-tools-5.2.0.jar:na]
at com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:80) ~[graphql-Java-tools-5.2.0.jar:na]
at com.coxautodev.graphql.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:150) ~[graphql-Java-tools-5.2.0.jar:na]
at com.coxautodev.graphql.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:156) ~[graphql-Java-tools-5.2.0.jar:na]
at com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration.schemaParser(GraphQLJavaToolsAutoConfiguration.Java:65) ~[graphql-spring-boot-autoconfigure-4.3.0.jar:na]
何が起こったのかわかりませんが、GraphQLはQuery
タイプをQueryResolver
にマップできないと思います。しかし、なぜ?
インターネットをチェックしたところ、Spring Boot Versionが理由だと読みました。そのため、Spring Boot 1.5.8.RELEASEでテストしましたが、結果はありませんでした。
ただし、Springブート1.5.8で古いGraphQLバージョン(4.3.0および5.2.0)を使用すると、正常に動作するように見えました。それで、これらの古いGraphQLバージョンを新しいSpringブートバージョンで使用しようとしましたが、別の例外が発生します。
2018-07-13 14:45:57.666 INFO 12872 --- [io-8080-exec-10] graphql.servlet.GraphQLServlet : Bad POST request: parsing failed
Java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
at org.Apache.catalina.connector.Request.parseParts(Request.Java:2826) ~[Tomcat-embed-core-8.5.31.jar:8.5.31]
at org.Apache.catalina.connector.Request.getParts(Request.Java:2793) ~[Tomcat-embed-core-8.5.31.jar:8.5.31]
at org.Apache.catalina.connector.RequestFacade.getParts(RequestFacade.Java:1084) ~[Tomcat-embed-core-8.5.31.jar:8.5.31]
at graphql.servlet.GraphQLServlet.lambda$new$2(GraphQLServlet.Java:129) ~[graphql-Java-servlet-5.1.0.jar:na]
at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.Java:260) ~[graphql-Java-servlet-5.1.0.jar:na]
at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.Java:278) ~[graphql-Java-servlet-5.1.0.jar:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:661) ~[Tomcat-embed-core-8.5.31.jar:8.5.31]
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:742) ~[Tomcat-embed-core-8.5.31.jar:8.5.31]
この非互換性のために、今私は本当にめちゃくちゃになっています。すべてのライブラリの新しいバージョンを使用したい。誰かが解決策を持っていますか?
リゾルバーBeanを2回作成しています。 MutationResolver
とQueryResolver
の両方に@Component
の注釈が付けられます。これは、Springがインスタンスを作成して登録することを意味します。ただし、GraphqlApplication
内では、@Bean
アノテーションを使用して両方のリゾルバー用のBeanも作成しました。
GraphQLライブラリはgreetingsAll
の適切なリゾルバーを探すため、これにより問題が発生しますが、Beanが2回マッピングされているため、2つのリゾルバーが見つかります。
解決策は、@Component
アノテーションを削除するか、次のBean構成を削除することです。
@Bean
public QueryResolver query() {
return new QueryResolver();
}
@Bean
public MutationResolver mutation() {
return new MutationResolver();
}