前言
继《初识GraphQL》之后我们开启GraphQL源码的阅读,本篇我们来阅读下GraphQL的源码解析部分。
1.SchemaParser
解析schema需要先创建SchemaParser:
package graphql.kickstart.tools.boot;
@Configuration
@ConditionalOnClass({SchemaParser.class})
@AutoConfigureAfter({JacksonAutoConfiguration.class})
@EnableConfigurationProperties({GraphQLToolsProperties.class})
public class GraphQLJavaToolsAutoConfiguration {
@Bean
@ConditionalOnBean({GraphQLResolver.class})
@ConditionalOnMissingBean
public SchemaParser schemaParser(List<GraphQLResolver<?>> resolvers, SchemaStringProvider schemaStringProvider, Builder optionsBuilder) throws IOException {
SchemaParserBuilder builder = this.dictionary != null ? new SchemaParserBuilder(this.dictionary) : new SchemaParserBuilder();
//当前项目下所有(包含依赖的)graphql文件内容
List<String> schemaStrings = schemaStringProvider.schemaStrings();
//全部组合在builder#schemaString字段中。
schemaStrings.forEach(builder::schemaString);
if (this.scalars != null) {
builder.scalars(this.scalars);
}
builder.options(optionsBuilder.build());
if (this.directives != null) {
this.directives.forEach((it) -> {
builder.directive(it.getName(), it.getDirective());
});
}
if (this.directiveWirings != null) {
this.directiveWirings.forEach(builder::directiveWiring);
}
return builder.resolvers(resolvers).build();
}
}
入参
- resolves为本项目中自定义的
GraphQLResolverbeans。
- schemaStringProvider是用来提供schema文件内容。
2.Parser
package graphql.parser;
@Internal
public class Parser {
public Document parseDocument(Reader reader) throws InvalidSyntaxException {
final MultiSourceReader multiSourceReader;
if (reader instanceof MultiSourceReader) {
multiSourceReader = (MultiSourceReader) reader;
} else {
multiSourceReader = MultiSourceReader.newMultiSourceReader().reader(reader, (String) null).build();
}
CodePointCharStream charStream;
try {
charStream = CharStreams.fromReader(multiSourceReader);
} catch (IOException var17) {
throw new UncheckedIOException(var17);
}
// 构建词法分析器
GraphqlLexer lexer = new GraphqlLexer(charStream);
lexer.removeErrorListeners();
lexer.addErrorListener(new BaseErrorListener() {
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
SourceLocation sourceLocation = AntlrHelper.createSourceLocation(multiSourceReader, line, charPositionInLine);
String preview = AntlrHelper.createPreview(multiSourceReader, line);
throw new InvalidSyntaxException(sourceLocation, "Invalid syntax: " + msg, preview, (String) null, (Exception) null);
}
});
CommonTokenStream tokens = new CommonTokenStream(lexer);
//构建语法分析器
GraphqlParser parser = new GraphqlParser(tokens);
parser.removeErrorListeners();
((ParserATNSimulator) parser.getInterpreter()).setPredictionMode(PredictionMode.SLL);
ExtendedBailStrategy bailStrategy = new ExtendedBailStrategy(multiSourceReader);
parser.setErrorHandler(bailStrategy);
GraphqlAntlrToLanguage toLanguage = new GraphqlAntlrToLanguage(tokens, multiSourceReader);
DocumentContext documentContext = parser.document();
// 解析出单独的的Definition
Document doc = toLanguage.createDocument(documentContext);
Token stop = documentContext.getStop();
List<Token> allTokens = tokens.getTokens();
if (stop != null && allTokens != null && !allTokens.isEmpty()) {
Token last = (Token) allTokens.get(allTokens.size() - 1);
boolean notEOF = last.getType() != -1;
boolean lastGreaterThanDocument = last.getTokenIndex() > stop.getTokenIndex();
boolean sameChannel = last.getChannel() == stop.getChannel();
if (notEOF && lastGreaterThanDocument && sameChannel) {
throw bailStrategy.mkMoreTokensException(last);
}
}
return doc;
}
}
入参
- reader类型为
MultiSourceReader内容为前面SchemaParser中组合的graphql文件内容。
3.GraphlQLSchema装载
package graphql.kickstart.tools
class SchemaParser internal constructor(scanResult:ScannedSchemaObjects,private val options:SchemaParserOptions,private val runtimeWiring:RuntimeWiring){
fun parseSchemaObjects():SchemaObjects{
// Create GraphQL objects
val interfaces=interfaceDefinitions.map{createInterfaceObject(it)}
val objects=objectDefinitions.map{createObject(it,interfaces)}
val unions=unionDefinitions.map{createUnionObject(it,objects)}
val inputObjects=inputObjectDefinitions.map{createInputObject(it)}
val enums=enumDefinitions.map{createEnumObject(it)}
// Assign type resolver to interfaces now that we know all of the object types
interfaces.forEach{codeRegistryBuilder.typeResolver(it,InterfaceTypeResolver(dictionary.inverse(),it,objects))}
unions.forEach{codeRegistryBuilder.typeResolver(it,UnionTypeResolver(dictionary.inverse(),it,objects))}
// Find query type and mutation/subscription type (if mutation/subscription type exists)
val queryName=rootInfo.getQueryName()
val mutationName=rootInfo.getMutationName()
val subscriptionName=rootInfo.getSubscriptionName()
val query=objects.find{it.name==queryName}
?:throw SchemaError("Expected a Query object with name '$queryName' but found none!")
val mutation=objects.find{it.name==mutationName}
?:if(rootInfo.isMutationRequired())throw SchemaError("Expected a Mutation object with name '$mutationName' but found none!")else null
val subscription=objects.find{it.name==subscriptionName}
?:if(rootInfo.isSubscriptionRequired())throw SchemaError("Expected a Subscription object with name '$subscriptionName' but found none!")else null
val additionalObjects=objects.filter{o->o!=query&&o!=subscription&&o!=mutation}
val types=(additionalObjects.toSet()as Set<GraphQLType>)+inputObjects+enums+interfaces+unions
return SchemaObjects(query,mutation,subscription,types,codeRegistryBuilder,buildCustomDirectiveSet())
}
}
最后创建GraphQLSchema并注入。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情