这次的学习主要是从graphql-java文档上入手,对语言的文档有初步的认识;
GraphQL简介
在 2015 React 欧洲大会上,Lee Byron 介绍了 Facebook 的 GraphQL ,包含 GraphQL 背后的故事,查询语句的示例,还有核心的概念。GraphQL 非常易懂,直接看查询语句就能知道查询出来的数据是什么样的。你可以把 GraphQL 的查询语句想成是没有值,只有属性的对象,返回的结果就是对应的属性还有对应值的对象。
故事
从 2011 开始,Facebook 开始越来越重视移动端,一支很小的团队开始去做 Android 与 iOS 应用。Facebook 的强项是 Web,也非常的了解 Web ,而且在这方面储备了大量的技术。当年 Facebook 的主要平台就是传统的 浏览器 Web 服务器 数据服务 的组合,Web 服务器响应浏览器的请求,到数据服务那里提供出数据,然后再交给浏览器去显示。
他们打算尽可能的使用现有的代码去实施移动端的应用,所以一开始 Facebook 的移动应用就是一个浏览器,加上了一个本地的壳,内容基本上就是简单的定制以后的移动 Web 网站。这样的好处就是可以使用所有的现有的 Web 平台上的东西。这样工程师们也可以使用平时创建东西的方法。这种方法在短时间内也得到了很大的成功,并且让公司把重点放在移动端上。
一开始都还好,不过在移动应用上添加越来越多的功能以后,就有点吃力了,移动浏览器经常会消耗掉所有的内存,让应用崩溃。另一面,在 Web 上,Facebook 仍然快速的生成,而移动端有点跟不上脚步了。这让他们决定要去做真正的本地的移动应用。
2012 年开始,Facebook 要开始开发真正的本地应用。 这跟 Web 很不一样,所以开始重新思考应用的平台。Web 就是请求一个 URL ,返回一堆 HTML。而本地移动应用,为了给应用提供需要的数据,填充数据模型 ,显示视图,要想的问题是,怎么去请求,准备,传递这些数据。而当时 Facebook 现有的服务器主要功能还是只提供 HTML。
工程师们试了一些方法,比如 RESTful API,对于 Facebook 这种复杂的应用,可能需要定义很多的端点,不同的端点返回来的数据只是略有不同,造成了资源浪费,而且还需要大量的逻辑去处理这些数据。后来他们又试了 FQL, 这是 Facebook 的公共接口,应该是一种查询语言。功能很强大,而且返回来的数据也有很好的结构。不好的地方是,查询用的语言非常难理解,比如多个 JOIN ,主键什么的,所以经常会出错。
除了这些表面上遇到的问题,工程师们也非常不喜欢这些方法表达数据的形式,比如我们平时想像的数据并不是一大堆查询语言,LEFT JOIN,RIGHT JOIN .. 也不是资源的地址。而对象的形式非常适合表达数据,一个对象,里面有一些属性,不同的属性对应不同的值。几个工程师开始了现在的 GraphQL,一种用对象,属性,关系的,有点像图形的方式来表达想要的数据。
三年前,Facebook 用了 GraphQL 做了第一款真正的本地移动应用,现在,应用每天会接受 260 亿的请求。
关于GraphQL的简介主要是从https://ninghao.net/blog/2857上复制过来的。
GraphQL-java文档相关
//以下全是个人的理解,目前只是初学,如果有错误欢迎指正。
jdk1.8以上才支持graphql-java
1、maven拉取所需要的graphql-java包,现在已经更新到3.0.0版本了
<dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>3.0.0</version></dependency>
2、这是官网给出的,也是很多博客网站给出的graphql最经典的java例子:
import graphql.GraphQL;import graphql.schema.GraphQLObjectType;import graphql.schema.GraphQLSchema;import java.util.Map;import static graphql.Scalars.GraphQLString;import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;import static graphql.schema.GraphQLObjectType.newObject;public class HelloWorld { public static void main(String[] args) { GraphQLObjectType queryType = newObject() .name("helloWorldQuery") .field(newFieldDefinition() .type(GraphQLString) .name("hello") .staticValue("world")) .build(); GraphQLSchema schema = GraphQLSchema.newSchema() .query(queryType) .build(); GraphQL graphQL = GraphQL.newGraphQL(schema).build(); Map<String, Object> result = graphQL.execute("{hello}").getData(); System.out.println(result); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
3、graphql-java定义了两种schema语言,一种是标准的java语言的,一种是IDL,类似json格式的语言;
下面是两种的示例:
IDL(目前个人的理解是用于前端的)
type Foo { bar: String}
Java(目前个人理解是用于后台)
GraphQLObjectType fooType = newObject() .name("Foo") .field(newFieldDefinition() .name("bar") .type(GraphQLString)) .build();
比较一下两种语言还是很好理解,这里需要说一下,newFieldDefinition和newObject都是静态方法,需要引入或者声明变量调用,感觉graphql中都是用引入的方法:
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;import static graphql.schema.GraphQLObjectType.newObject;
方法属于哪个包中,在eclipse中可以通过ctrl+H查找;idea中还没有试过;
4、DataFetcher和TypeResolver
DataFetcher有点不是很理解,每一个搜索域可以指定DataFetcher,如果没有指定的话,就是用默认的PropertyDataFetcher, PropertyDataFetcher主要是Map或者JavaBeans中获取数据,与Map中的Key对应,和JavaBeans中的变量名对应;
TypeResolver就是数据的类型
官网给出的IDL示例
schema { query: QueryType}type QueryType { hero(episode: Episode): Character human(id : String) : Human droid(id: ID!): Droid}enum Episode { NEWHOPE EMPIRE JEDI}interface Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]!}type Human implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! homePlanet: String}type Droid implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! primaryFunction: String}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
Java代码
目前这里面的内容不懂的部分还是很多的
①loadSchema这个方法是在哪里定义的至今没有找到;
②starWarsSchema.graphqls这个文件怎么定义的?里面的内容是什么;
③StarWarsData理解上应该是一个entity类,但是为什么没有申明变量就可以直接使用里面getter和setter,还是需要申明了,但是文档觉得没有必要给出这部分代码;
④CustomScalar定义在哪里?
SchemaParser schemaParser = new SchemaParser();SchemaGenerator schemaGenerator = new SchemaGenerator();File schemaFile = loadSchema("starWarsSchema.graphqls");TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile);RuntimeWiring wiring = buildRuntimeWiring();GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);RuntimeWiring buildRuntimeWiring() { return RuntimeWiring.newRuntimeWiring() .scalar(CustomScalar) .type("QueryType", typeWiring -> typeWiring .dataFetcher("hero", new StaticDataFetcher(StarWarsData.getArtoo())) .dataFetcher("human", StarWarsData.getHumanDataFetcher()) .dataFetcher("droid", StarWarsData.getDroidDataFetcher()) ) .type("Human", typeWiring -> typeWiring .dataFetcher("friends", StarWarsData.getFriendsDataFetcher()) ) .type("Droid", typeWiring -> typeWiring .dataFetcher("friends", StarWarsData.getFriendsDataFetcher()) ) .type( newTypeWiring("Character") .typeResolver(StarWarsData.getCharacterTypeResolver()) .build() ) .build();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
官网还给出了另外一种buildDynamicRuntimeWiring,根据名字为动态的RuntimeWiring,但是里面的方法也不是很懂,有人懂得望告知:
RuntimeWiring buildDynamicRuntimeWiring() { WiringFactory dynamicWiringFactory = new WiringFactory() { @Override public boolean providesTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) { return getDirective(definition,"specialMarker") != null; } @Override public boolean providesTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) { return getDirective(definition,"specialMarker") != null; } @Override public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) { Directive directive = getDirective(definition,"specialMarker"); return createTypeResolver(definition,directive); } @Override public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) { Directive directive = getDirective(definition,"specialMarker"); return createTypeResolver(definition,directive); } @Override public boolean providesDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) { return getDirective(definition,"dataFetcher") != null; } @Override public DataFetcher getDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) { Directive directive = getDirective(definition, "dataFetcher"); return createDataFetcher(definition,directive); } }; return RuntimeWiring.newRuntimeWiring() .wiringFactory(dynamicWiringFactory).build();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
5、类型:
GraphQL提供一下类型:
- Scalar
- Object
- Interface
- Union
- InputObject
- Enum
Scalar
- GraphQLString
- GraphQLBoolean
- GraphQLInt
- GraphQLFloat
- GraphQLID
- GraphQLLong
- GraphQLShort
- GraphQLByte
- GraphQLFloat
- GraphQLBigDecimal
- GraphQLBigInteger
Object
type SimpsonCharacter { name: String mainCharacter: Boolean}
GraphQLObjectType simpsonCharacter = newObject().name("SimpsonCharacter").description("A Simpson character").field(newFieldDefinition() .name("name") .description("The name of the character.") .type(GraphQLString)).field(newFieldDefinition() .name("mainCharacter") .description("One of the main Simpson characters?") .type(GraphQLBoolean)).build();
Interface
interface ComicCharacter { name: String;}
GraphQLInterfaceType comicCharacter = newInterface() .name("ComicCharacter") .description("A abstract comic character.") .field(newFieldDefinition() .name("name") .description("The name of the character.") .type(GraphQLString)) .build();
Union
interface ComicCharacter { name: String;}
GraphQLUnionType PetType = newUnionType() .name("Pet") .possibleType(CatType) .possibleType(DogType) .typeResolver(new TypeResolver() { @Override public GraphQLObjectType getType(TypeResolutionEnvironment env) { if (env.getObject() instanceof Cat) { return CatType; } if (env.getObject() instanceof Dog) { return DogType; } return null; } }) .build();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Enum
enum Color { RED GREEN BLUE}
GraphQLEnumType colorEnum = newEnum() .name("Color") .description("Supported colors.") .value("RED") .value("GREEN") .value("BLUE") .build();
ObjectInputType
input Character { name: String}
GraphQLInputObjectType inputObjectType = newInputObject() .name("inputObjectType") .field(newInputObjectField() .name("field") .type(GraphQLString)) .build();
6、递归类型
每个人都是有朋友的,每个人的朋友都是人,然后每个人又都有朋友,朋友又会有朋友的朋友,就是一直递归下去,graphql-java不会出现这种情况
GraphQLObjectType person = newObject() .name("Person") .field(newFieldDefinition() .name("friends") .type(new GraphQLList(new GraphQLTypeReference("Person")))) .build()
7、对于查询的类可进行扩展
java代码上是这样是实现的
SchemaParser schemaParser = new SchemaCompiler();SchemaGenerator schemaGenerator = new SchemaGenerator();File schemaFile1 = loadSchema("starWarsSchemaPart1.graphqls");File schemaFile2 = loadSchema("starWarsSchemaPart2.graphqls");File schemaFile3 = loadSchema("starWarsSchemaPart3.graphqls");TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();typeRegistry.merge(schemaParser.compile(schemaFile1));typeRegistry.merge(schemaParser.compile(schemaFile2));typeRegistry.merge(schemaParser.compile(schemaFile3));GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, buildRuntimeWiring());
在IDL中大概就是下面这种情况
type Human { id: ID! name: String!}#Another part of your system can extend this type to add more shape to it.extend type Human implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]!}#You can have as many extensions as you think sensible. extend type Human { homePlanet: String}#With all these type extensions in place the Human type now looks like this at runtime.type Human implements Character { id: ID! name: String! friends: [Character] appearsIn: [Episode]! homePlanet: String}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
今天的工作暂时就这么多,明天研究研究graphql-java源代码,再看看IDL语言,看看能不能对graphq-java有更深的了解,目前了解的情况还无法根本无法用来写代码。