您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
GraphQL实战(2)-java和spring boot实现
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
GraphQL实战(2)-java和spring boot实现
自猿其说Tech
2021-07-05
IP归属:未知
41480浏览
计算机编程
### 1 JAVA实现及分析 到这里必须具备的知识储备:对GraphQL有简单的了解,了解Schema的常用类型。 **GraphQL实战(1)-GraphQL介绍 **:https://developer.jdcloud.com/article/2101 这里用一些demo示例来体验GraphQL的执行过程,这只是借助graphql-java实现的java版本。 首先需要引入graphql-java的依赖 ```xml <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>15.0</version> </dependency> ``` #### 1.1 Demo1 第一个demo是官网提供的 ```java import graphql.ExecutionResult; import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.schema.StaticDataFetcher; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; public class HelloWorld { public static void main(String[] args) { //定义schema文件,直接写在了代码中,包含一个hello的查询方法 String schema = "type Query{hello: String} schema{query: Query}"; SchemaParser schemaParser = new SchemaParser(); //直接加载schema,初始化GraphQL TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema); //加载一份服务端数据 RuntimeWiring runtimeWiring = new RuntimeWiring() .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world"))) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); // 构建一个GraphQL实例,执行graphql脚本 GraphQL build = GraphQL.newGraphQL(graphQLSchema).build(); ExecutionResult executionResult = build.execute("{hello}"); System.out.println(executionResult.getData().toString()); // Prints: {hello=world} } } ``` 这个demo直接加载了schema的内容,构建Graphql执行脚本。这种方式最接近于实际开发的操作。 #### 1.2 Deom2 通过这个demo来看一下一个GraphQL的服务端都做了什么。并对比一下java代码加载GraphQL对象与schema中的GraphQL的联系。 所依赖的对象User ```java /** * ClassName: User<br/> * Description: <br/> * date: 2019/6/28 10:38 AM<br/> * * @author chengluchao * @since JDK 1.8 */ @Data public class User { private int age; private long id; private String name; private Card card; public User(int age, long id, String name, Card card) { this.age = age; this.id = id; this.name = name; this.card = card; } public User(int age, long id, String name) { this.age = age; this.id = id; this.name = name; } } /** * ClassName: Card<br/> * Description: <br/> * date: 2019/6/28 3:25 PM<br/> * * @author chengluchao * @since JDK 1.8 */ @Data public class Card { private String cardNumber; private Long userId; public Card(String cardNumber, Long userId) { this.cardNumber = cardNumber; this.userId = userId; } } ``` 接下来是demo ```java /** * ClassName: GraphQLDemo<br/> * Description: <br/> * date: 2019/6/28 10:40 AM<br/> * * @author chengluchao * @since JDK 1.8 */ public class GraphQLDemo { public static void main(String[] args) { /* 定义GraphQL对象,等同于schema中定义的 type User { id:ID age:Int name:String } */ GraphQLObjectType userObjectType = GraphQLObjectType.newObject() .name("User") .field(GraphQLFieldDefinition.newFieldDefinition().name("id").type(Scalars.GraphQLLong)) .field(GraphQLFieldDefinition.newFieldDefinition().name("age").type(Scalars.GraphQLInt)) .field(GraphQLFieldDefinition.newFieldDefinition().name("name").type(Scalars.GraphQLString)) .build(); /* queryUser : User 指定对象及参数类型 等同于在GraphQL中定义一个无参方法 queryUser,返回值为User queryUser:User dataFetcher指定了响应的数据集,这个demo里使用了静态写入的方式 */ GraphQLFieldDefinition userFileldDefinition = GraphQLFieldDefinition.newFieldDefinition() .name("queryUser") .type(userObjectType) //静态数据 .dataFetcher(new StaticDataFetcher(new User(19, 2, "CLC"))) .build(); /* type UserQuery 定义查询类型 对应的graphQL为: type UserQuery { queryUser:User } */ GraphQLObjectType userQueryObjectType = GraphQLObjectType.newObject() .name("UserQuery") .field(userFileldDefinition) .build(); /* Schema 定义查询 定义了query的root类型 对应的GraphQL语法为: schema { query:UserQuery } */ GraphQLSchema qlSchema = GraphQLSchema.newSchema().query(userQueryObjectType).build(); //构建一个GraphQl对象,执行逻辑都在此处进行 GraphQL graphQL = GraphQL.newGraphQL(qlSchema).build(); //模拟客户端传入查询脚本,方法名queryUser,获取响应值为 id name age String query = "{queryUser{id name age}}"; // 执行业务操作逻辑,获取返回值 ExecutionResult result = graphQL.execute(query); System.out.println(result.toSpecification()); } } ``` 这里对应的sehema应该是这样的: ```bash #对应的User定义如下 schema { #定义查询 query: UserQuery } #定义查询类型 type UserQuery { #指定对象以及参数类型 queryUser : User } #定义对象 type User { #!表示非空 id: ID! name:String age:Int card:Card } type Card { cardNumber:String userId:ID } ``` **可以看出:** schema的结构层级是:schema > UserQuery > User schema中定义的是操作类型,UserQuery下定义的是操作方法,而User对应的是GraphQL的对象,此对象应该对应于java中一个相同的对象 **以上demo实现中有两点是实战中不可取的:** schema的加载方式,应该以读取本地配置的形式加载 dataFetcher的数据加载,demo中是静态写死的方式,实战中应该是动态加载的数据 接下来从这两点改进 #### 1.3 demo3 首先在resources目录下创建一个user.graphqls的文件 ```bash #对应的User定义如下 schema { #定义查询 query: UserQuery } #定义查询类型 type UserQuery { #指定对象以及参数类型 queryUser : User queryUserById(id:ID) : User } #定义对象 type User { #!表示非空 id: ID! name:String age:Int card:Card } type Card { cardNumber:String userId:ID } ``` ```java import clc.bean.Card; import clc.bean.User; import graphql.ExecutionResult; import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import org.apache.commons.io.IOUtils; /** * ClassName: GraphQLSDLDemo<br/> * Description: <br/> * date: 2019/6/28 11:19 AM<br/> * * @author chengluchao * @since JDK 1.8 */ public class GraphQLSDLDemo { public static void main(String[] args) throws Exception { //读取graphqls文件 String fileName = "user.graphqls"; String fileContent = IOUtils.toString(GraphQLSDLDemo.class.getClassLoader().getResource(fileName), "UTF-8"); //解析文件 TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(fileContent); RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring() .type("UserQuery", builder -> builder.dataFetcher("queryUserById", environment -> { //解析请求参数,根据业务返回结果 Long id = Long.parseLong(environment.getArgument("id")); Card card = new Card("123456", id); return new User(18, id, "user0" + id, card); }) ) .build(); GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, wiring); GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build(); String query = "{queryUserById(id:15){id,name,age,card{cardNumber,userId}}}"; ExecutionResult result = graphQL.execute(query); System.out.println("query: " + query); System.out.println(result.toSpecification()); } } ``` 项目源码:https://gitee.com/chengluchao/graphql-clc/tree/master/graphql-java/src/main/java/com/clc/demo **从demo升级成实战项目需要做的内容:** schema与java代码分离,可以通过读取文件的形式将schema加载到系统中; 动态处理GraphQL的请求参数,并随之生成对应的响应 可能大家已经发现,入参和出参都需要服务端编码实现解析。 **其实graphql-java做的事情是很有限的,主要作用如下:** 维护schema的结构 根据GraphQL的脚本需要,过滤响应结果 其他很大一部分的工作都需要自己实现的。 ### 2 spring Boot 实现 接下来分享一下在Spring Boot中对GraphQL的应用 首先创建一个Spring Boot的项目 #### 2.1 POM依赖 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.clc</groupId> <artifactId>boot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>boot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>15.0</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> ``` #### 2.2 创建schema 接下来就是创建一个schema的文件,来写graphql的定义,这里还用之前的例子。resources下创建一个user.graphqls。 ```bash #对应的User定义如下 schema { #定义查询 query: UserQuery } #定义查询类型 type UserQuery { #指定对象以及参数类型 queryUser : User queryUserById(id:ID) : User } #定义对象 type User { #!表示非空 id:ID! name:String age:Int card:Card } type Card { cardNumber:String userId:ID } ``` #### 2.3 Java实现 ##### 2.3.1 数据操作对应的java对象 ```java package com.clc.boot.bean; import lombok.Data; /** * ClassName: User<br/> * Description: <br/> * date: 2019/6/28 10:38 AM<br/> * * @author chengluchao * @since JDK 1.8 */ @Data public class User { private int age; private long id; private String name; private Card card; public User(int age, long id, String name, Card card) { this.age = age; this.id = id; this.name = name; this.card = card; } public User(int age, long id, String name) { this.age = age; this.id = id; this.name = name; } } package com.clc.boot.bean; import lombok.Data; /** * ClassName: Card<br/> * Description: <br/> * date: 2019/6/28 3:25 PM<br/> * * @author chengluchao * @since JDK 1.8 */ @Data public class Card { private String cardNumber; private Long userId; public Card(String cardNumber, Long userId) { this.cardNumber = cardNumber; this.userId = userId; } } ``` ##### 2.3.2 解析GraphQL的核心类 ```java package com.clc.boot.graphql; import com.clc.boot.bean.User; import com.clc.boot.bean.Card; import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.util.ResourceUtils; import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; /** * 实现功能:将GraphQL对象载入待Spring容器,并且完成GraphQL对象初始化的功能 * * @author chengluchao */ @Component public class GraphQLProvider { private GraphQL graphQL; @Bean public GraphQL graphQL() { return graphQL; } /** * 加载schema * * @throws IOException */ @PostConstruct public void init() throws IOException { File file = ResourceUtils.getFile("classpath:user.graphqls"); GraphQLSchema graphQLSchema = buildGraphQLSchema(file); this.graphQL = GraphQL.newGraphQL(graphQLSchema).build(); } private GraphQLSchema buildGraphQLSchema(File file) { TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(file); GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeRegistry, buildWiring()); return graphQLSchema; } private GraphQLSchema buildSchema(String sdl) { TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl); RuntimeWiring runtimeWiring = buildWiring(); SchemaGenerator schemaGenerator = new SchemaGenerator(); return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring); } /** * 业务实现,demo版 * 如果是开发实战,这一点的设计是重点,需要考虑如何根据graphql中定义的方法来执行java代码 * * @return */ private RuntimeWiring buildWiring() { RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring() .type("UserQuery", builder -> builder.dataFetcher("queryUserById", environment -> { Long id = Long.parseLong(environment.getArgument("id")); Card card = new Card("123456", id); return new User(18, id, "user0" + id, card); }) ) .build(); return wiring; } } ``` 接下来是GraphQL的入口: ```java package com.clc.boot.controller; import graphql.ExecutionResult; import graphql.GraphQL; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; /** * ClassName: GraphQLController<br/> * Description: <br/> * date: 2019/6/28 5:38 PM<br/> * * @author chengluchao * @since JDK 1.8 */ @RequestMapping("graphql") @Controller public class GraphQLController { @Autowired private GraphQL graphQL; @RequestMapping("query") @ResponseBody public Object query(@RequestParam("query") String query) { ExecutionResult result = this.graphQL.execute(query); return result.toSpecification(); } } ``` 测试: ``` 127.0.0.1:8080/graphql/query?query={ user: queryUserById(id:15){id,name,age,card{cardNumber,userId}}} ``` ```json { "data": { "user": { "id": "15", "name": "user015", "age": 18, "card": { "cardNumber": "123456", "userId": "15" } } } } ``` 源码地址:https://gitee.com/chengluchao/graphql-clc/tree/master/graphql-springboot 这里实现了graphql在spring-boot中的应用,由于解析graphql到业务执行还不够自动化,所以此例子还是一个demo级别 ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:中台技术部-基础平台组 程路超
原创文章,需联系作者,授权转载
上一篇:JAVA开发、测试问题排查利器-Arthas
下一篇:Vue-Router + Nginx解决单页应用路由问题
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号