web-dev-qa-db-ja.com

GraphQLおよびSpring Boot 2.0.3

今後のプロジェクトで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]

この非互換性のために、今私は本当にめちゃくちゃになっています。すべてのライブラリの新しいバージョンを使用したい。誰かが解決策を持っていますか?

6
Jonas Wolff

リゾルバーBeanを2回作成しています。 MutationResolverQueryResolverの両方に@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();
}
5
g00glen00b