内容简介:在在官方的案例中,我们需要实例化一个GraphQL实例:
在 GraphQL(一):GraphQL介绍 中讲到目前已经有很多平台完成了GraphQL实现,这里以 Java 平台为例,介绍GraphQL服务的搭建。
graphql-java + graphql-java-spring
graphql-java 是GraphQL的Java实现,它实现了GraphQL的执行,但是没有任何关于HTTP或者JSON的处理,因此在接入SpringBoot时还需要 graphql-java-spring 的支持。官方的案例就是使用这两个jar包完成的。
在官方的案例中,我们需要实例化一个GraphQL实例:
@Component public class GraphQLProvider { @Autowired GraphQLDataFetchers graphQLDataFetchers; private GraphQL graphQL; @PostConstruct public void init() throws IOException { URL url = Resources.getResource("schema.graphqls"); String sdl = Resources.toString(url, Charsets.UTF_8); GraphQLSchema graphQLSchema = buildSchema(sdl); this.graphQL = GraphQL.newGraphQL(graphQLSchema).build(); } private GraphQLSchema buildSchema(String sdl) { TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl); RuntimeWiring runtimeWiring = buildWiring(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); } private RuntimeWiring buildWiring() { return RuntimeWiring.newRuntimeWiring() .type(newTypeWiring("Query") .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher())) .type(newTypeWiring("Book") .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher())) .build(); } @Bean public GraphQL graphQL() { return graphQL; } } 复制代码
这样的实现需要我们了解较多graphql-java的底层细节,比如:TypeDefinitionRegistry、RuntimeWiring、SchemaGenerator等,同时还需要硬编码字符串。
同样,在实现数据注入时,也需要硬编码:
public DataFetcher getBookByIdDataFetcher() { return dataFetchingEnvironment -> { String bookId = dataFetchingEnvironment.getArgument("id"); return books .stream() .filter(book -> book.get("id").equals(bookId)) .findFirst() .orElse(null); }; } public DataFetcher getAuthorDataFetcher() { return dataFetchingEnvironment -> { Map<String, String> book = dataFetchingEnvironment.getSource(); String authorId = book.get("authorId"); return authors .stream() .filter(author -> author.get("id").equals(authorId)) .findFirst() .orElse(null); }; } 复制代码
于是就有了 graphql-spring-boot-starter + graphql-java-tools 搭建GraphQL的方案。
graphql-spring-boot-starter + graphql-java-tools
graphql-java-tools
graphql-java-tools 能够从GraphQL的模式定义*.graphqls文件构建出对应的Java的POJO类型对象(graphql-java-tools将读取classpath下所有以*.graphqls为后缀名的文件,创建GraphQLSchema对象。),同时为我们屏蔽了graphql-java的底层细节,它本身依赖graphql-java。
graphql-spring-boot-starter
graphql-spring-boot-starter 是辅助SpringBoot接入GraphQL的库,它本身依赖graphql-java和 graphql-java-servlet (将GraphQL服务发布为通过HTTP可访问的Web服务,封装了一个GraphQLServlet接收GraphQL请求,并提供 Servlet Listeners 功能)。
接下来我们将实现一个基于 graphql-spring-boot-starter + graphql-java-tools 搭建GraphQL服务的Demo。
Demo
基于 SpringBoot集成MyBatis 提供GraphQL服务
1. 在pom中增加以下依赖
<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-tools</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphiql-spring-boot-starter</artifactId> <version>3.6.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-spring-boot-starter</artifactId> <version>3.6.0</version> </dependency> 复制代码
对应的SpringBoot版本是1.5.6
2. 增加Teacher实体
@Serialization data class Teacher( val id: String = "commonId", var teacherId: String = "", var teacherName: String = "", var teacherPhone: String = "", var schoolId: String = "" ) { override fun toString(): String { return JSON.toJSONString(this) } } 复制代码
以及对应的Dao、Service、teacher.xml等
3. 在classpath下新建schema.graphqls
type School { id: ID! schoolId: String schoolName: String schoolAge: Int schoolAddress: String teachers: [Teacher] master: String } type Teacher{ teacherId: String teacherName: String teacherPhone: String schoolId: String } input TeacherInput{ teacherId: String teacherName: String teacherPhone: String schoolId: String } 复制代码
这里的模型最好和Java Bean一致,如果Java bean中有多余的字段,将被忽略,不会抛出异常。
4. 在classpath下新建root.graphqls
这是公开API的地方,按照GraphQL的规范,Query、Mutation、Subscription三种查询类型需要放在各自的节点下(这里暂时不考虑订阅):
type Query{ # 根据学校Id查询学校,schoolId不能为空,返回的School不能为空 school(schoolId:String!):School! } type Mutation { insertSchool(schoolId: String!,schoolName:String!,schoolAge:Int!,schoolAddress:String!) : School! insertTeacher(teacher:TeacherInput!):Teacher! } 复制代码
5. 实现Resolver
graphql-java-tools为我们屏蔽了底层细节,我们只需要继承以下几个类完成数据注入即可:
- GraphQLQueryResolver
- GraphQLMutationResolver
- GraphQLSubscriptionResolver
Resolver完成的是数据的注入,也就是对*.graphqls文件中的type的字段的数据进行注入,注入需要满足以下规则:
1. <field> 2. is<field> – only if the field is of type Boolean 3. get<field> 复制代码
比如我们我们根据学校Id查询学校的API:
@Component class SchoolQueryResolver : GraphQLQueryResolver { @Autowired private lateinit var schoolService: SchoolService fun school(schoolId: String): School { return schoolService.getSchoolBySchoolId(schoolId) } //或者 fun getSchool(schoolId: String): School { return schoolService.getSchoolBySchoolId(schoolId) } } 复制代码
我们在schema.graphqls中定义的类型有与之对应的Java Bean,这些Java Bean都提供了getField方法,因此不需要额外实现Resolver,有时候,在type中定义的类型的某个字段数据的获取比较麻烦,不是简单的getField可以解决的,此时可以为此类型实现专门的字段值获取的Resolver,假设School中的master字段逻辑获取逻辑很复杂:
public class SchoolResolver implements GraphQLResolver<School> { private SchoolDao schoolDao; public School getMaster(School school) { return schoolDao.getMasterById(school.getMasterId()); } } 复制代码
泛型中需要指定类型,字段数据获取的方法名称规则和常规接口的规则一致,只是需要把该类型作为参数传递到方法内,值得注意的是,如果客户端没有请求Master字段,那么getMaster方法将不会被执行。
实际上针对type中的每个Field都需要有getField,使得Graphql能够获取到数据注入到返回的结果中,如果针对此Field已经实现了Resolver,那么会优先使用Resolver来注入数据,此时可以省略掉getField(直接去掉School Bean中的master字段)不过还是建议将Java Bean和type中的Field一一对应,便于维护。
以上是针对Query的Demo,关于Mutation请查看文本的源码,这里需要说明的是我们的insertSchool和insertTeacher有些不同:
insertSchool(schoolId: String!,schoolName:String!,schoolAge:Int!,schoolAddress:String!) : School! insertTeacher(teacher:TeacherInput!):Teacher! 复制代码
insertTeacher引入了一个新类型TeacherInput,将需要传递到服务端的数据封装起来,GraphQL的返回类型(Teacher)和输入类型(TeacherInput)是不能共用的,所以加上Input后缀加以区分,同样的,针对TeacherInput也需要有对应的Java Bean。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 服务器完整搭建jupyter 科学环境服务
- Web服务器搭建
- 搭建 etcd discovery 服务
- 如何搭建HTTP/HTTPS服务
- Linux下Memcache服务搭建
- jenkins搭建一个持续集成服务
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Implementing Responsive Design
Tim Kadlec / New Riders / 2012-7-31 / GBP 27.99
New devices and platforms emerge daily. Browsers iterate at a remarkable pace. Faced with this volatile landscape we can either struggle for control or we can embrace the inherent flexibility of the w......一起来看看 《Implementing Responsive Design》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
html转js在线工具
html转js在线工具