每次GraphQL API调用重复生成Schema的技术咨询(基于graphql-spqr)
Great question—let’s dive into why your current approach might be problematic and how to fix it.
Why your current implementation isn’t ideal
Generating a GraphQLSchema on every API call is a major anti-pattern here. Schema generation with graphql-spqr involves heavy work: scanning your annotated classes via reflection, parsing annotations, building resolver metadata, and assembling the entire schema object graph. Doing this on every request wastes CPU cycles, increases memory churn, and will cripple your API’s throughput as traffic grows—especially in high-concurrency environments.
GraphQL schemas are meant to be static artifacts in most applications. Your annotated classes and their metadata (like @GraphQLQuery, @GraphQLMutation annotations) don’t change at runtime, so there’s zero reason to rebuild the schema for every call.
Optimization directions
Here’s how to fix this properly:
Initialize the schema once at application startup
Move the schema generation code to a place that runs once when your app boots. For example, if you’re using Spring, use a@Componentwith a constructor or@PostConstructmethod to create the schema once and store it as a singleton:@Component public class GraphQLConfig { private final GraphQLSchema schema; // Inject your annotated class instance if it's a Spring bean public GraphQLConfig(YourAnnotatedService annotatedService) { this.schema = new GraphQLSchemaGenerator() .withResolverBuilders(new AnnotatedResolverBuilder()) .withValueMapperFactory(new JacksonValueMapperFactory()) .withOperationsFromSingleton(annotatedService) .generate(); } public GraphQLSchema getSchema() { return schema; } }Then, inject this
GraphQLConfigwherever you handle GraphQL requests and reuse the pre-built schema.Reuse stateless components
Objects likeAnnotatedResolverBuilderandJacksonValueMapperFactoryare stateless—you don’t need to create new instances every time. Initialize them once as static constants and reuse them across schema generation (even if you ever need to regenerate the schema later):private static final AnnotatedResolverBuilder RESOLVER_BUILDER = new AnnotatedResolverBuilder(); private static final JacksonValueMapperFactory VALUE_MAPPER_FACTORY = new JacksonValueMapperFactory(); // Then use them in schema generation this.schema = new GraphQLSchemaGenerator() .withResolverBuilders(RESOLVER_BUILDER) .withValueMapperFactory(VALUE_MAPPER_FACTORY) .withOperationsFromSingleton(annotatedService) .generate();This reduces unnecessary object creation and garbage collection overhead.
Add caching if you need dynamic schema changes
If your use case requires dynamic schema updates (e.g., adding fields/operations at runtime), don’t regenerate on every call. Instead, use a cache (like Guava Cache or Caffeine) to store the schema, and only regenerate it when your underlying annotated classes change. You can trigger updates via events or a scheduled refresh, depending on your needs.
Final takeaway
Your current approach will lead to poor performance under load. The fix is straightforward: treat the GraphQLSchema as a singleton, build it once at startup, and reuse it for all incoming requests. This is the standard practice in nearly all GraphQL Java applications, including those using graphql-spqr.
内容的提问来源于stack exchange,提问作者Manav Bhanot

