You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于GraphQL Java实现Sling Servlet:构建AEM数据查询接口

实现基于GraphQL Java的AEM Sling Servlet

以下是实现你需求的完整步骤,从Schema定义到Servlet开发,直接对接AEM数据:

1. 定义GraphQL Schema

先写出匹配你查询结构的Schema,明确查询字段、参数和返回类型:

type Query {
  content(path: String!): Content
}

type Content {
  pageTitle: String
  resourceType: String
}

2. 实现DataFetcher(从AEM拉取数据核心逻辑)

这个类负责接收GraphQL查询的path参数,通过Sling的ResourceResolver从AEM中获取对应资源,提取所需属性:

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;

import java.util.HashMap;

public class ContentDataFetcher implements DataFetcher<Object> {

    private final ResourceResolver resourceResolver;

    public ContentDataFetcher(ResourceResolver resourceResolver) {
        this.resourceResolver = resourceResolver;
    }

    @Override
    public Object get(DataFetchingEnvironment env) throws Exception {
        String path = env.getArgument("path");
        Resource targetResource = resourceResolver.getResource(path);
        
        if (targetResource == null) {
            return null;
        }

        // 提取AEM资源的属性,这里用HashMap封装返回,也可以用自定义POJO
        return new HashMap<String, Object>() {{
            put("pageTitle", targetResource.getValueMap().get("jcr:title", String.class));
            put("resourceType", targetResource.getResourceType());
        }};
    }
}

3. 编写Sling Servlet处理GraphQL请求

Servlet负责接收POST请求、解析查询、执行GraphQL逻辑并返回JSON结果:

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.service.component.annotations.Component;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

import javax.servlet.Servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component(
        service = Servlet.class,
        property = {
                "sling.servlet.methods=" + HttpConstants.METHOD_POST,
                "sling.servlet.paths=/bin/graphql/custom-content" // 自定义的Servlet访问路径
        }
)
public class CustomGraphQLServlet extends SlingSafeMethodsServlet {

    private volatile GraphQL graphQLInstance;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
        // 懒加载GraphQL实例,避免启动时的资源依赖问题
        if (graphQLInstance == null) {
            synchronized (this) {
                if (graphQLInstance == null) {
                    graphQLInstance = buildGraphQL(request.getResourceResolver());
                }
            }
        }

        // 读取请求体中的GraphQL查询语句
        StringBuilder queryBuilder = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            queryBuilder.append(line);
        }
        String graphqlQuery = queryBuilder.toString();

        // 执行查询并获取结果
        Map<String, Object> result = graphQLInstance.execute(graphqlQuery).toSpecification();

        // 返回JSON格式响应
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        objectMapper.writeValue(response.getWriter(), result);
    }

    private GraphQL buildGraphQL(ResourceResolver resourceResolver) {
        // 加载Schema定义(复杂场景建议从JCR文件或外部配置读取,这里硬编码做示例)
        String schemaDef = """
                type Query {
                  content(path: String!): Content
                }
                
                type Content {
                  pageTitle: String
                  resourceType: String
                }
                """;

        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaDef);
        RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
                .type("Query", wiring -> wiring
                        .dataFetcher("content", new ContentDataFetcher(resourceResolver)))
                .build();

        GraphQLSchema graphQLSchema = new SchemaGenerator().makeExecutableSchema(typeRegistry, runtimeWiring);
        return GraphQL.newGraphQL(graphQLSchema).build();
    }
}

4. 测试验证

发送POST请求到/bin/graphql/custom-content,请求体使用你提供的查询:

query {
  content(path: "/content/test/en") {
     pageTitle
     resourceType
 }
}

返回结果会和你期望的格式完全一致:

{
  "data": {
    "content": {
          "pageTitle": "English",
          "resourceType": "/apps/testproject/components/page"
        }
    }
}

注意事项

  • 权限控制:确保Servlet使用的ResourceResolver有目标AEM路径的访问权限,必要时配置OSGi权限映射或调整Servlet的资源解析策略。
  • 错误处理:可以在ContentDataFetcher中添加异常捕获,返回GraphQL标准错误格式,比如资源不存在时返回明确的错误提示。
  • 性能优化:对于高并发场景,建议缓存GraphQL实例,避免每次请求重复构建Schema和RuntimeWiring。

内容的提问来源于stack exchange,提问作者Prince Shivhare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 03:44:59