GraphQL源码阅读之schema解析

436 阅读2分钟

前言

《初识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为本项目中自定义的GraphQLResolver beans。

image.png

  • schemaStringProvider是用来提供schema文件内容。

image.png

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;
    }
}

image.png

入参

  • 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())
        }
}

image.png

最后创建GraphQLSchema并注入。 image.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情