【发布时间】:2016-10-23 05:26:31
【问题描述】:
在我的 Sling 应用程序中,我有数据呈现文档、页面和内容节点。我们主要将这些文档作为 HTML 提供,但现在我想要一个 servlet 将这些文档作为 PDF 和 PPT 提供。
基本上,我考虑过实现工厂模式:在我的 servlet 中,取决于请求的扩展(pdf 或 ppt),我将从 DocumentBuilderFactory 中获得正确的 DocumentBuilder 实现,PdfDocumentBuilder 或 PptDocumentBuilder。
所以首先我有这个:
public class PlanExportBuilderFactory {
public PlanExportBuilder getBuilder(String type) {
PlanExportBuilder builder = null;
switch (type) {
case "pdf":
builder = new PdfPlanExportBuilder();
break;
default:
logger.error("Unsupported plan export builder, type: " + type);
}
return builder;
}
}
在 servlet 中:
@Component(metatype = false)
@Service(Servlet.class)
@Properties({
@Property(name = "sling.servlet.resourceTypes", value = "myApp/document"),
@Property(name = "sling.servlet.extensions", value = { "ppt", "pdf" }),
@Property(name = "sling.servlet.methods", value = "GET")
})
public class PlanExportServlet extends SlingSafeMethodsServlet {
@Reference
PlanExportBuilderFactory builderFactory;
@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
Resource resource = request.getResource();
PlanExportBuilder builder = builderFactory.getBuilder(request.getRequestPathInfo().getExtension());
}
}
但问题是,在构建器中我想引用其他服务来访问 Sling 资源,并且使用此解决方案,它们没有被绑定。
我查看了带有 OSGi 的服务工厂,但据我了解,您可以使用它们来配置相同的服务实现。
然后我发现你可以通过命名得到一个具体的实现,或者使用一个属性和一个过滤器。
所以我最终得到了这个:
public class PlanExportBuilderFactory {
@Reference(target = "(builderType=pdf)")
PlanExportBuilder pdfPlanExportBuilder;
public PlanExportBuilder getBuilder(String type) {
PlanExportBuilder builder = null;
switch (type) {
case "pdf":
return pdfPlanExportBuilder;
default:
logger.error("Unsupported plan export builder, type: " + type);
}
return builder;
}
}
定义“builderType”属性的构建器:
// AbstractPlanExportBuilder implements PlanExportBuilder interface
@Component
@Service(value=PlanExportBuilder.class)
public class PdfPlanExportBuilder extends AbstractPlanExportBuilder {
@Property(name="builderType", value="pdf")
public PdfPlanExportBuilder() {
planDocument = new PdfPlanDocument();
}
}
我想知道这是否是检索我的关于 OSGi 良好实践的 PDF 构建器实现的好方法。
编辑 1
根据 Peter 的回答,我尝试添加多个引用,但对于 Felix,它似乎不起作用:
@Reference(name = "planExportBuilder", cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC)
private Map<String, PlanExportBuilder> builders = new ConcurrentHashMap<String, PlanExportBuilder>();
protected final void bindPlanExportBuilder(PlanExportBuilder b, Map<String, Object> props) {
final String type = PropertiesUtil.toString(props.get("type"), null);
if (type != null) {
this.builders.put((String) props.get("type"), b);
}
}
protected final void unbindPlanExportBuilder(final PlanExportBuilder b, Map<String, Object> props) {
final String type = PropertiesUtil.toString(props.get("type"), null);
if (type != null) {
this.builders.remove(type);
}
}
我收到这些错误:
@Reference(builders) : Missing method bind for reference planExportBuilder
@Reference(builders) : Something went wrong: false - true - MANDATORY_MULTIPLE
@Reference(builders) : Missing method unbind for reference planExportBuilder
http://felix.apache.org/documentation/subprojects/apache-felix-maven-scr-plugin/scr-annotations.html#reference 此处的 Felix 文档说明了绑定方法:
默认值是通过将引用名称附加到字符串绑定而创建的名称。该方法必须声明为公共或受保护的,并采用使用服务接口类型声明的单个参数
因此,据此,我知道它不能与 Felix 一起使用,因为我正在尝试传递两个参数。但是,我在这里找到了一个示例,该示例似乎与您的建议相匹配,但我无法使其工作:https://github.com/Adobe-Consulting-Services/acs-aem-samples/blob/master/bundle/src/main/java/com/adobe/acs/samples/services/impl/SampleMultiReferenceServiceImpl.java
编辑 2 只需将引用移到类上方即可使其工作:
@References({
@Reference(
name = "planExportBuilder",
referenceInterface = PlanExportBuilder.class,
policy = ReferencePolicy.DYNAMIC,
cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE)
})
public class PlanExportServlet extends SlingSafeMethodsServlet {
【问题讨论】: