從Hello?World開始理解GraphQL背后處理及執(zhí)行過程
前言
在上篇文章《初識GraphQL》中我們大致的了解了GraphQL作用,并通過簡單示例初步體驗了GraphQL的使用。下面我們從Hello World開始來進一步了解GraphQL背后的處理。
Hello World
package com.graphqljava.tutorial.bookdetails;
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;
public class HelloWorld {
public static void main(String[] args) {
// 從最簡單的schema字符串開始,省去對graphqls文件的讀取
String schema = "type Query{hello: String}";
// 用于獲得graphql schema定義,并解析放入TypeDefinitionRegistry中,以便放置在SchemaGenerator中使用
SchemaParser schemaParser = new SchemaParser();
// 解析schema定義字符串,并創(chuàng)建包含一組類型定義的TypeDefinitionRegistry
TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema);
// runtime wiring 是data fetchers、type resolves和定制標(biāo)量的規(guī)范,這些都需要連接到GraphQLSchema中
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
// 添加一個類型連接
.type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world")))
.build();
//schemaGenerator對象可以使用typeDefinitionRegistry、runtimeWiring生成工作運行時schema
SchemaGenerator schemaGenerator = new SchemaGenerator();
//graphQLSchema代表graphql引擎的組合類型系統(tǒng)。
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
//構(gòu)建GraphQL用于執(zhí)行查詢
GraphQL build = GraphQL.newGraphQL(graphQLSchema).build();
//執(zhí)行并獲得結(jié)果
ExecutionResult executionResult = build.execute("{hello}");
System.out.println(executionResult.getData().toString());
}
}
從上面的代碼注釋可以看到GraphQL大致執(zhí)行的過程:
- 根據(jù)給定的schema內(nèi)容使用SchemaParser進行解析獲得schema定義TypeDefinitionRegistry。
- 拿到了schema定義之后還需要定義RuntimeWiring用于定義不同類型的type resolves和對應(yīng)的數(shù)據(jù)提取器data fetchers。
- 使用GraphQLSchema把TypeDefinitionRegistry和RuntimeWiring組合在一起便于以后的使用。
- 使用GraphQLSchema構(gòu)建出GraphQL用于后面的QL執(zhí)行。
- 傳入QL使用GraphQL執(zhí)行并獲得結(jié)果ExecutionResult。
從外層使用代碼可以得出核心處理類為:SchemaParser、TypeDefinitionRegistry、RuntimeWiring、GraphQLSchema、GraphQL。
下面我們分配看看核心類是怎么處理的。
SchemaParser
解析schema字符串定義并生成TypeDefinitionRegistry。
public TypeDefinitionRegistry parse(String schemaInput) throws SchemaProblem {
try {
Parser parser = new Parser();
Document document = parser.parseDocument(schemaInput);
return buildRegistry(document);
} catch (ParseCancellationException e) {
throw handleParseException(e);
}
}
使用Document構(gòu)建TypeDefinitionRegistry
public TypeDefinitionRegistry buildRegistry(Document document) {
List<GraphQLError> errors = new ArrayList<>();
TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
List<Definition> definitions = document.getDefinitions();
for (Definition definition : definitions) {
if (definition instanceof SDLDefinition) {
typeRegistry.add((SDLDefinition) definition).ifPresent(errors::add);
}
}
if (errors.size() > 0) {
throw new SchemaProblem(errors);
} else {
return typeRegistry;
}
}
可以看的出來TypeDefinitionRegistry只是對Document的定義提取,重點還是在于Document的生成,我們可以先通過debugger來先看看Document的大致內(nèi)容。

可以看到就是把schema字符串解析成了方便后續(xù)使用的Document對象,我們還是詳細(xì)看看這個對象里面的屬性和大概的生成過程。
Parser#parseDocument
public Document parseDocument(String input, String sourceName) {
CharStream charStream;
if(sourceName == null) {
charStream = CharStreams.fromString(input);
} else{
charStream = CharStreams.fromString(input, sourceName);
}
GraphqlLexer lexer = new GraphqlLexer(charStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
GraphqlParser parser = new GraphqlParser(tokens);
parser.removeErrorListeners();
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
parser.setErrorHandler(new BailErrorStrategy());
//詞法分析從schema中解析出tokens(每個關(guān)鍵字、最后一個為EOF),documentContext包含children、start/stop字符等相當(dāng)于結(jié)構(gòu)。
GraphqlParser.DocumentContext documentContext = parser.document();
GraphqlAntlrToLanguage antlrToLanguage = new GraphqlAntlrToLanguage(tokens);
// 生成document
Document doc = antlrToLanguage.createDocument(documentContext);
Token stop = documentContext.getStop();
List<Token> allTokens = tokens.getTokens();
if (stop != null && allTokens != null && !allTokens.isEmpty()) {
Token last = allTokens.get(allTokens.size() - 1);
//
// do we have more tokens in the stream than we consumed in the parse?
// if yes then its invalid. We make sure its the same channel
boolean notEOF = last.getType() != Token.EOF;
boolean lastGreaterThanDocument = last.getTokenIndex() > stop.getTokenIndex();
boolean sameChannel = last.getChannel() == stop.getChannel();
if (notEOF && lastGreaterThanDocument && sameChannel) {
throw new ParseCancellationException("There are more tokens in the query that have not been consumed");
}
}
return doc;
}
tokens&documentContext

可以看到,主要是通過提取schema的關(guān)鍵字、識別結(jié)構(gòu)最后生成Document主要內(nèi)容為類型定義定義和類型定義中的字段定義。
RuntimeWiring
runtime wiring 是data fetchers、type resolves和定制標(biāo)量的規(guī)范,這些都需要連接到GraphQLSchema中。
RuntimeWiring.Builder#type
這種形式允許使用lambda作為type wiring的構(gòu)建器。
public Builder type(String typeName, UnaryOperator<TypeRuntimeWiring.Builder> builderFunction) {
TypeRuntimeWiring.Builder builder = builderFunction.apply(TypeRuntimeWiring.newTypeWiring(typeName));
return type(builder.build());
}
添加type wiring。
public Builder type(TypeRuntimeWiring typeRuntimeWiring) {
String typeName = typeRuntimeWiring.getTypeName();
Map<String, DataFetcher> typeDataFetchers = dataFetchers.computeIfAbsent(typeName, k -> new LinkedHashMap<>());
typeRuntimeWiring.getFieldDataFetchers().forEach(typeDataFetchers::put);
defaultDataFetchers.put(typeName, typeRuntimeWiring.getDefaultDataFetcher());
TypeResolver typeResolver = typeRuntimeWiring.getTypeResolver();
if (typeResolver != null) {
this.typeResolvers.put(typeName, typeResolver);
}
EnumValuesProvider enumValuesProvider = typeRuntimeWiring.getEnumValuesProvider();
if (enumValuesProvider != null) {
this.enumValuesProviders.put(typeName, enumValuesProvider);
}
return this;
}
可以看到主要就是網(wǎng)RuntimeWiring里面添加了dataFetchers、defaultDataFetchers、typeResolvers、enumValuesProviders。下面分別介紹下各屬性的含義:
- DataFetcher:負(fù)責(zé)返回給定graphql字段數(shù)據(jù)值。graphql引擎使用datafetcher將邏輯字段解析/獲取到運行時對象,該對象將作為整個graphql grapql.ExecutionResult的一部分發(fā)送回來。

GraphQLScalarType:scalar type是graphql樹類型的葉節(jié)點。該類型允許你定義新的scalar type。

- TypeResolver:這在類型解析期間被調(diào)用,以確定在運行時GraphQLInterfaceTypes和GraphQLUnionTypes應(yīng)該動態(tài)使用哪些具體的GraphQLObjectType。
- GraphQLInterfaceTypes:在graphql中,接口是一種抽象類型,它定義了一組字段,類型必須包含這些字段才能實現(xiàn)該接口。在運行時,TypeResolver用于獲取一個接口對象值,并決定哪個GraphQLObjectType表示此接口類型。關(guān)于這個概念的更多細(xì)節(jié),請參見graphql.org/learn/schem…
- GraphQLUnionTypes:聯(lián)合類型,相當(dāng)于組合。
- GraphQLObjectType:這是工作馬類型,表示一個對象,它具有一個或多個字段值,這些字段可以根據(jù)對象類型等進行自身的處理,直到到達由GraphQLScalarTypes表示的類型樹的葉節(jié)點。關(guān)于這個概念的更多細(xì)節(jié),請參見graphql.org/learn/schem…
- SchemaDirectiveWiring:SchemaDirectiveWiring負(fù)責(zé)基于schema定義語言(SDL)中放置在該元素上的指令增強運行時元素。它可以增強graphql運行時元素并添加新的行為,例如通過更改字段graphql.schema. datafetcher。
- WiringFactory:WiringFactory允許您基于IDL定義更動態(tài)的連接TypeResolvers和DataFetchers。
- EnumValuesProvider:為每個graphql Enum值提供Java運行時值。用于IDL驅(qū)動的schema創(chuàng)建。Enum值被認(rèn)為是靜態(tài)的:在創(chuàng)建schema時調(diào)用。在執(zhí)行查詢時不使用。
- GraphqlFieldVisibility:這允許您控制graphql字段的可見性。默認(rèn)情況下,graphql-java使每個定義的字段可見,但您可以實現(xiàn)此接口的實例并減少特定字段的可見性。
GraphQL
build
例子中通過傳入GraphQLSchema構(gòu)建GraphQL。
public GraphQL build() {
assertNotNull(graphQLSchema, "graphQLSchema must be non null");
assertNotNull(queryExecutionStrategy, "queryStrategy must be non null");
assertNotNull(idProvider, "idProvider must be non null");
return new GraphQL(graphQLSchema, queryExecutionStrategy, mutationExecutionStrategy, subscriptionExecutionStrategy, idProvider, instrumentation, preparsedDocumentProvider);
}
除了graphQLSchema都是默認(rèn)值,我們大概看看各個成員分別是用來干嘛的:
- queryExecutionStrategy:異步非阻塞地運行字段的標(biāo)準(zhǔn)graphql執(zhí)行策略。
- mutationExecutionStrategy:異步非阻塞執(zhí)行,但串行:當(dāng)時只有一個字段將被解析。關(guān)于每個字段的非串行(并行)執(zhí)行,請參閱AsyncExecutionStrategy。
- subscriptionExecutionStrategy:通過使用reactive-streams作為訂閱查詢的輸出結(jié)果來實現(xiàn)graphql訂閱。
- idProvider:executionid的提供者
- instrumentation:提供了檢測GraphQL查詢執(zhí)行步驟的功能。
- preparsedDocumentProvider:客戶端連接文檔緩存和/或查詢白名單的接口。
execute
下面我們還是來看看具體的執(zhí)行:
public ExecutionResult execute(ExecutionInput executionInput) {
try {
return executeAsync(executionInput).join();
} catch (CompletionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
} else {
throw e;
}
}
}
用提供的輸入對象執(zhí)行g(shù)raphql query。這將返回一個承諾(又名CompletableFuture),以提供一個ExecutionResult,這是執(zhí)行所提供查詢的結(jié)果。
public CompletableFuture<ExecutionResult> executeAsync(ExecutionInput executionInput) {
try {
log.debug("Executing request. operation name: '{}'. query: '{}'. variables '{}'", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables());
// 創(chuàng)建InstrumentationState對象,這是一個跟蹤Instrumentation全生命周期的對象
InstrumentationState instrumentationState = instrumentation.createState(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInput));
InstrumentationExecutionParameters inputInstrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState);
// 檢測輸入對象
executionInput = instrumentation.instrumentExecutionInput(executionInput, inputInstrumentationParameters);
InstrumentationExecutionParameters instrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState);
// 在執(zhí)行檢測 chain前調(diào)用
InstrumentationContext<ExecutionResult> executionInstrumentation = instrumentation.beginExecution(instrumentationParameters);
// 檢測GraphQLSchema
GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters);
// 對客戶端傳遞的query進行驗證并執(zhí)行
CompletableFuture<ExecutionResult> executionResult = parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState);
//
// finish up instrumentation
executionResult = executionResult.whenComplete(executionInstrumentation::onCompleted);
//
// allow instrumentation to tweak the result
executionResult = executionResult.thenCompose(result -> instrumentation.instrumentExecutionResult(result, instrumentationParameters));
return executionResult;
} catch (AbortExecutionException abortException) {
return CompletableFuture.completedFuture(abortException.toExecutionResult());
}
}
parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState)進行驗證并執(zhí)行,驗證我們就不看了直接看執(zhí)行:
private CompletableFuture<ExecutionResult> execute(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) {
String query = executionInput.getQuery();
String operationName = executionInput.getOperationName();
Object context = executionInput.getContext();
Execution execution = new Execution(queryStrategy, mutationStrategy, subscriptionStrategy, instrumentation);
ExecutionId executionId = idProvider.provide(query, operationName, context);
log.debug("Executing '{}'. operation name: '{}'. query: '{}'. variables '{}'", executionId, executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables());
CompletableFuture<ExecutionResult> future = execution.execute(document, graphQLSchema, executionId, executionInput, instrumentationState);
future = future.whenComplete((result, throwable) -> {
if (throwable != null) {
log.error(String.format("Execution '%s' threw exception when executing : query : '%s'. variables '%s'", executionId, executionInput.getQuery(), executionInput.getVariables()), throwable);
} else {
int errorCount = result.getErrors().size();
if (errorCount > 0) {
log.debug("Execution '{}' completed with '{}' errors", executionId, errorCount);
} else {
log.debug("Execution '{}' completed with zero errors", executionId);
}
}
});
return future;
}
這里打印日志為
Executing '9c81e267-c55a-4ebd-9f9c-3a2270b28103'. operation name: 'null'. query: '{hello}'. variables '{}'
還要繼續(xù)往下看:
Execution#execute
public CompletableFuture<ExecutionResult> execute(Document document, GraphQLSchema graphQLSchema, ExecutionId executionId, ExecutionInput executionInput, InstrumentationState instrumentationState) {
// 獲得要執(zhí)行的操作
NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, executionInput.getOperationName());
Map<String, FragmentDefinition> fragmentsByName = getOperationResult.fragmentsByName;
OperationDefinition operationDefinition = getOperationResult.operationDefinition;
ValuesResolver valuesResolver = new ValuesResolver();
// 獲得輸入的參數(shù)
Map<String, Object> inputVariables = executionInput.getVariables();
List<VariableDefinition> variableDefinitions = operationDefinition.getVariableDefinitions();
Map<String, Object> coercedVariables;
try {
coercedVariables = valuesResolver.coerceArgumentValues(graphQLSchema, variableDefinitions, inputVariables);
} catch (RuntimeException rte) {
if (rte instanceof GraphQLError) {
return completedFuture(new ExecutionResultImpl((GraphQLError) rte));
}
throw rte;
}
ExecutionContext executionContext = newExecutionContextBuilder()
.instrumentation(instrumentation)
.instrumentationState(instrumentationState)
.executionId(executionId)
.graphQLSchema(graphQLSchema)
.queryStrategy(queryStrategy)
.mutationStrategy(mutationStrategy)
.subscriptionStrategy(subscriptionStrategy)
.context(executionInput.getContext())
.root(executionInput.getRoot())
.fragmentsByName(fragmentsByName)
.variables(coercedVariables)
.document(document)
.operationDefinition(operationDefinition)
// 放入dataloder
.dataLoaderRegistry(executionInput.getDataLoaderRegistry())
.build();
InstrumentationExecutionParameters parameters = new InstrumentationExecutionParameters(
executionInput, graphQLSchema, instrumentationState
);
// 獲得執(zhí)行上下文
executionContext = instrumentation.instrumentExecutionContext(executionContext, parameters);
return executeOperation(executionContext, parameters, executionInput.getRoot(), executionContext.getOperationDefinition());
}
獲得了執(zhí)行上下文并執(zhí)行,下面繼續(xù)看executeOperation:
private CompletableFuture<ExecutionResult> executeOperation(ExecutionContext executionContext, InstrumentationExecutionParameters instrumentationExecutionParameters, Object root, OperationDefinition operationDefinition) {
// ...
ExecutionStrategyParameters parameters = newParameters()
.executionStepInfo(executionStepInfo)
.source(root)
.fields(fields)
.nonNullFieldValidator(nonNullableFieldValidator)
.path(path)
.build();
CompletableFuture<ExecutionResult> result;
try {
ExecutionStrategy executionStrategy;
if (operation == OperationDefinition.Operation.MUTATION) {
executionStrategy = mutationStrategy;
} else if (operation == SUBSCRIPTION) {
executionStrategy = subscriptionStrategy;
} else {
executionStrategy = queryStrategy;
}
log.debug("Executing '{}' query operation: '{}' using '{}' execution strategy", executionContext.getExecutionId(), operation, executionStrategy.getClass().getName());
result = executionStrategy.execute(executionContext, parameters);
} catch (NonNullableFieldWasNullException e) {
// ...
}
// ...
return deferSupport(executionContext, result);
}
日志輸出:
Executing '9c81e267-c55a-4ebd-9f9c-3a2270b28103' query operation: 'QUERY' using 'graphql.execution.AsyncExecutionStrategy' execution strategy
最終使用AsyncExecutionStrategy策略執(zhí)行,繼續(xù)往下看:
AsynExecutionStrategy#execute
public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException {
Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationExecutionStrategyParameters instrumentationParameters = new InstrumentationExecutionStrategyParameters(executionContext, parameters);
ExecutionStrategyInstrumentationContext executionStrategyCtx = instrumentation.beginExecutionStrategy(instrumentationParameters);
Map<String, List<Field>> fields = parameters.getFields();
// 字段名稱
List<String> fieldNames = new ArrayList<>(fields.keySet());
List<CompletableFuture<FieldValueInfo>> futures = new ArrayList<>();
List<String> resolvedFields = new ArrayList<>();
for (String fieldName : fieldNames) {
List<Field> currentField = fields.get(fieldName);
ExecutionPath fieldPath = parameters.getPath().segment(mkNameForPath(currentField));
ExecutionStrategyParameters newParameters = parameters
.transform(builder -> builder.field(currentField).path(fieldPath).parent(parameters));
if (isDeferred(executionContext, newParameters, currentField)) {
executionStrategyCtx.onDeferredField(currentField);
continue;
}
resolvedFields.add(fieldName);
// 處理字段,這里處理的是"hello"
CompletableFuture<FieldValueInfo> future = resolveFieldWithInfo(executionContext, newParameters);
futures.add(future);
}
CompletableFuture<ExecutionResult> overallResult = new CompletableFuture<>();
executionStrategyCtx.onDispatched(overallResult);
//并行執(zhí)行所有filed處理的futures
Async.each(futures).whenComplete((completeValueInfos, throwable) -> {
BiConsumer<List<ExecutionResult>, Throwable> handleResultsConsumer = handleResults(executionContext, resolvedFields, overallResult);
if (throwable != null) {
handleResultsConsumer.accept(null, throwable.getCause());
return;
}
List<CompletableFuture<ExecutionResult>> executionResultFuture = completeValueInfos.stream().map(FieldValueInfo::getFieldValue).collect(Collectors.toList());
executionStrategyCtx.onFieldValuesInfo(completeValueInfos);
Async.each(executionResultFuture).whenComplete(handleResultsConsumer);
}).exceptionally((ex) -> {
// if there are any issues with combining/handling the field results,
// complete the future at all costs and bubble up any thrown exception so
// the execution does not hang.
overallResult.completeExceptionally(ex);
return null;
});
overallResult.whenComplete(executionStrategyCtx::onCompleted);
return overallResult;
}
可以看到這里會遍歷所有fileds拿到每個filed future,最后并行執(zhí)行,下面具體看看:
ExecutionStrategy#resolveFieldWithInfo
調(diào)用該函數(shù)來獲取字段的值及額外的運行時信息,并根據(jù)graphql query內(nèi)容進一步處理它。
protected CompletableFuture<FieldValueInfo> resolveFieldWithInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext, parameters, parameters.getField().get(0));
Instrumentation instrumentation = executionContext.getInstrumentation();
InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginField(
new InstrumentationFieldParameters(executionContext, fieldDef, createExecutionStepInfo(executionContext, parameters, fieldDef))
);
CompletableFuture<Object> fetchFieldFuture = fetchField(executionContext, parameters);
CompletableFuture<FieldValueInfo> result = fetchFieldFuture.thenApply((fetchedValue) ->
completeField(executionContext, parameters, fetchedValue));
CompletableFuture<ExecutionResult> executionResultFuture = result.thenCompose(FieldValueInfo::getFieldValue);
fieldCtx.onDispatched(executionResultFuture);
executionResultFuture.whenComplete(fieldCtx::onCompleted);
return result;
}
調(diào)用該函數(shù)獲取filed值,使用從filed GraphQlFiledDefinition關(guān)聯(lián)的DataFetcher。
protected CompletableFuture<Object> fetchField(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
Field field = parameters.getField().get(0);
GraphQLObjectType parentType = (GraphQLObjectType) parameters.getExecutionStepInfo().getUnwrappedNonNullType();
GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field);
GraphqlFieldVisibility fieldVisibility = executionContext.getGraphQLSchema().getFieldVisibility();
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldVisibility, fieldDef.getArguments(), field.getArguments(), executionContext.getVariables());
GraphQLOutputType fieldType = fieldDef.getType();
DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, parameters.getField());
// ...
CompletableFuture<Object> fetchedValue;
// 獲得dataFetcher,這里為HelloWorld的`new StaticDataFetcher("world")`
DataFetcher dataFetcher = fieldDef.getDataFetcher();
dataFetcher = instrumentation.instrumentDataFetcher(dataFetcher, instrumentationFieldFetchParams);
ExecutionId executionId = executionContext.getExecutionId();
try {
log.debug("'{}' fetching field '{}' using data fetcher '{}'...", executionId, executionStepInfo.getPath(), dataFetcher.getClass().getName());
// 執(zhí)行dataFetcher獲取值,enviroment為上下文環(huán)境包含參數(shù)
Object fetchedValueRaw = dataFetcher.get(environment);
log.debug("'{}' field '{}' fetch returned '{}'", executionId, executionStepInfo.getPath(), fetchedValueRaw == null ? "null" : fetchedValueRaw.getClass().getName());
// 如果是具體值就返回已經(jīng)有值的CompletableFuture,如果是CompletionStage就直接返回
fetchedValue = Async.toCompletableFuture(fetchedValueRaw);
} catch (Exception e) {
log.debug(String.format("'%s', field '%s' fetch threw exception", executionId, executionStepInfo.getPath()), e);
fetchedValue = new CompletableFuture<>();
fetchedValue.completeExceptionally(e);
}
fetchCtx.onDispatched(fetchedValue);
// 對結(jié)果的后續(xù)處理
return fetchedValue
.handle((result, exception) -> {
fetchCtx.onCompleted(result, exception);
if (exception != null) {
handleFetchingException(executionContext, parameters, field, fieldDef, argumentValues, environment, exception);
return null;
} else {
return result;
}
})
.thenApply(result -> unboxPossibleDataFetcherResult(executionContext, parameters, result))
.thenApply(this::unboxPossibleOptional);
}
總體執(zhí)行過程

以上就是從Hello World開始理解GraphQL背后處理的詳細(xì)內(nèi)容,更多關(guān)于GraphQL處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Spring 攔截器流程及多個攔截器的執(zhí)行順序
這篇文章主要介紹了Spring 攔截器流程及多個攔截器的執(zhí)行順序的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Spring框架,感興趣的朋友可以了解下2021-05-05
最流行的java后臺框架spring quartz定時任務(wù)
近日項目開發(fā)中需要執(zhí)行一些定時任務(wù),比如需要在每天凌晨時候,分析一次前一天的日志信息,借此機會整理了一下定時任務(wù)的幾種實現(xiàn)方式,由于項目采用spring框架,所以我都將結(jié)合spring框架來介紹2015-12-12
PowerJob的ProcessorLoader工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的ProcessorLoader工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
SpringBoot實現(xiàn)application配置信息加密
在配置文件中,我們有開發(fā)環(huán)境配置和生產(chǎn)環(huán)境配置,而生產(chǎn)環(huán)境的配置信息是需要做好防護的,避免外泄,所以本文為大家整理了application配置信息加密的方法,需要的可以參考下2023-07-07
java計算自然數(shù)中的水仙花數(shù)的方法分享
這篇文章主要介紹了java計算自然數(shù)中的水仙花數(shù)的方法,需要的朋友可以參考下2014-03-03

