AEM内容即服务:Java模型下URL链接映射的处理方案咨询
解决AEM Java模型JSON Exporter中的自动链接映射问题
这确实是AEM Content as a Service开发里的高频痛点——总不想在每个模型的每个链接字段里都重复写ResourceResolver.map(request, path)对吧?给你几个优雅的自动化方案,彻底摆脱重复代码:
方案1:自定义Jackson序列化器(最推荐)
利用Jackson的自定义序列化能力,给所有需要映射的链接字段自动处理路径转换,不用在模型里写任何额外逻辑。
步骤1:编写自定义序列化器
创建一个继承JsonSerializer<String>的类,在序列化时自动调用当前请求的ResourceResolver.map():
import javax.servlet.http.HttpServletRequest; import java.io.IOException; import org.apache.sling.api.resource.ResourceResolver; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.apache.sling.models.annotations.injectorspecific.Self; public class MappedLinkSerializer extends JsonSerializer<String> { @Self private HttpServletRequest request; @Override public void serialize(String path, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (path == null || path.isEmpty()) { gen.writeNull(); return; } ResourceResolver resolver = request.getResourceResolver(); // 处理页面路径或资源路径 String mappedPath = resolver.map(request, path); // 若需外部化绝对URL,可结合Externalizer // Externalizer externalizer = resolver.adaptTo(Externalizer.class); // String externalUrl = externalizer.publishLink(resolver, mappedPath); gen.writeString(mappedPath); } }
步骤2:在模型字段上添加注解
在你的Java模型中,给需要映射的链接字段加上@JsonSerialize注解,指定用上面的序列化器:
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; @Model(adaptables = Resource.class, resourceType = "your-project/components/content/link-component") public class LinkModel { @ValueMapValue private String pagePath; @JsonSerialize(using = MappedLinkSerializer.class) public String getPagePath() { return pagePath; } }
这样当你访问.model.json时,pagePath字段会自动被转换成映射后的URL,完全不用在模型里手动调用map()。
方案2:扩展AbstractModelExporter做全局处理
如果你的项目里大部分模型都需要处理链接,可以扩展AEM的AbstractModelExporter,在序列化前统一扫描并处理所有符合规则的字段(比如字段名包含"link"或"path"):
import com.adobe.cq.export.json.AbstractModelExporter; import com.adobe.cq.export.json.ExporterConstants; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.models.annotations.Exporter; import org.apache.sling.models.annotations.Model; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Model(adaptables = Resource.class) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class GlobalMappedModelExporter extends AbstractModelExporter { @Override protected Map<String, Object> exportModel(Object model, HttpServletRequest request) { Map<String, Object> exportMap = super.exportModel(model, request); ResourceResolver resolver = request.getResourceResolver(); // 遍历所有字段,自动处理包含"link"或"path"的字段 for (Map.Entry<String, Object> entry : exportMap.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof String && (key.contains("link") || key.contains("path"))) { String mappedPath = resolver.map(request, (String) value); entry.setValue(mappedPath); } } return exportMap; } }
之后让你的所有模型继承这个自定义Exporter,就能全局自动处理链接映射了。
方案3:自定义注解+注入处理器
创建一个自定义注解@MappedLink,然后通过Sling Models的注解处理器,在模型初始化时就把路径转换成映射后的URL:
步骤1:定义自定义注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MappedLink { }
步骤2:编写注解处理器
import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; import org.apache.sling.models.spi.injectorspecific.AbstractInjectAnnotationProcessor2; import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessor2; import org.apache.sling.models.spi.injectorspecific.StaticInjectAnnotationProcessorFactory; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Type; public class MappedLinkInjector implements StaticInjectAnnotationProcessorFactory { @Override public InjectAnnotationProcessor2 createAnnotationProcessor(Class<?> adapterClass, Type type, java.lang.annotation.Annotation annotation, InjectionStrategy injectionStrategy) { return new AbstractInjectAnnotationProcessor2() { @Override public Object postProcess(Object value, String name, Object declaredType, InjectionStrategy injectionStrategy) { if (value instanceof String && !((String) value).isEmpty()) { // 获取当前请求的ResourceResolver HttpServletRequest request = (HttpServletRequest) org.apache.sling.api.request.RequestContext.getRequest(); ResourceResolver resolver = request.getResourceResolver(); return resolver.map(request, (String) value); } return value; } }; } }
步骤3:在模型中使用注解
import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; @Model(adaptables = Resource.class, resourceType = "your-project/components/content/button") public class ButtonModel { @ValueMapValue @MappedLink private String linkPath; public String getLinkPath() { return linkPath; // 这里已经是映射后的URL了 } }
关键注意事项
- 必须使用当前请求的ResourceResolver:绝对不能用全局的ResourceResolverFactory获取的resolver,因为不同请求(比如作者/发布、不同域名)的映射规则可能不同,必须从当前HttpServletRequest中获取。
- 外部化链接:如果需要生成发布环境的绝对URL,可以结合AEM的
Externalizer服务,在序列化或注入时调用externalizer.publishLink(resolver, mappedPath)。
内容的提问来源于stack exchange,提问作者Dr. Hans-Peter Störr
相关产品推荐
相关产品推荐

