Java™ API最佳实践
Adobe Experience Manager (AEM)构建在丰富的开源软件栈栈上,该栈栈公开许多Java™ API以供在开发期间使用。 本文探讨了主要API以及何时使用它们以及为什么使用它们。
AEM基于四个主要Java™ API集构建。
-
Adobe Experience Manager (AEM)
- 产品抽象,如页面、资产、工作流等。
-
Apache Sling Web框架
- REST和基于资源的抽象,如资源、值映射和HTTP请求。
-
JCR (Apache Jackrabbit Oak)
- 数据和内容抽象,如节点、属性和会话。
-
OSGi (Apache Felix)
- OSGi应用程序容器抽象,例如服务和(OSGi)组件。
Java™ API首选项“经验法则”
一般规则是按照以下顺序优先使用API/抽象:
- AEM
- Sling
- JCR
- osgi
如果API由AEM提供,则首选它而不是 Sling、 JCR和OSGi。 如果AEM不提供API,则首选 Sling 超过JCR和OSGi。
此顺序是一般规则,表示存在例外。 可以接受脱离此规则的原因包括:
-
众所周知的例外,如下所述。
-
所需功能在更高级别的API中不可用。
-
在现有代码(自定义或AEM产品代码)的上下文中操作,而现有代码本身使用不太首选的API,迁移到新API的成本是不合理的。
- 始终使用较低级别的API比创建混合更好。
AEM API
AEM API提供了特定于产品化用例的抽象和功能。
例如,AEM PageManager 和 页面 API为以下对象提供抽象: cq:Page
AEM中表示网页的节点。
虽然这些节点可通过以下方式使用 Sling API作为资源,JCR API作为节点,AEM API为常见用例提供抽象。 使用AEM API可确保AEM产品与AEM的自定义项和扩展之间的行为一致。
com.adobe.* vs com.day.* API
AEM API具有包内首选项,按首选项顺序由以下Java™包标识:
com.adobe.cq
com.adobe.granite
com.day.cq
此 com.adobe.cq
package支持产品用例,而 com.adobe.granite
支持跨产品平台用例,例如工作流或任务(跨产品:AEM Assets、Sites等)。
此 com.day.cq
包中包含“原始”API。 这些API处理在Adobe收购之前和/或收购前后存在的核心抽象和功能 Day CQ. 这些API受支持,应避免使用,除非 com.adobe.cq
或 com.adobe.granite
包不提供(较新的)替代方案。
新的抽象,例如 Content Fragments 和 Experience Fragments 构建于 com.adobe.cq
空格而不是 com.day.cq
如下所述。
查询API
AEM支持多种查询语言。 三种主要语言是 JCR-SQL2、 XPath和 AEM查询生成器.
最重要的考虑是在代码库中维护一致的查询语言,以降低复杂性和理解成本。
与一样,所有查询语言都有效地具有相同的性能配置文件 Apache Oak 将它们转换到JCR-SQL2以供最终查询执行,并且与JCR-SQL2的查询时间本身相比,转换时间可以忽略不计。
首选的API为 AEM查询生成器,这是最高级别的抽象,提供了一个用于构建、执行和检索查询结果的强大API,并提供了以下内容:
Sling API
Apache Sling 是支持AEM的RESTful Web框架。 Sling 提供HTTP请求路由,将JCR节点建模为资源,提供安全上下文等。
Sling API具有为扩展而构建的其他好处,这意味着增强使用构建的应用程序的行为通常更容易、更安全 Sling API比扩展性较低的JCR API好。
的常见用法 Sling API
-
访问JCR节点身份 Sling Resources 并通过访问其数据 值映射.
-
通过提供安全上下文 ResourceResolver.
-
通过ResourceResolver 创建/移动/复制/删除方法.
-
通过更新属性 ModifiableValueMap.
-
构建请求处理构建基块
-
异步处理构建基块
JCR API
此 JCR (Java™ Content Repository) 2.0 API 是JCR实现规范的一部分(在AEM中, Apache Jackrabbit Oak)。 所有JCR实施都必须符合并实施这些API,因此,它是与AEM内容进行交互的最低级别API。
JCR本身是一种基于分层/树的NoSQL数据存储,AEM将其用作内容存储库。 JCR具有大量受支持的API,从内容CRUD到查询内容。 尽管拥有这种强大的API,但与更高级别的AEM相比,它们很少受到青睐 Sling 抽象。
与Apache Jackrabbit Oak API相比,JCR API始终优先。 JCR API用于 交互 JCR存储库,而Oak API用于 实施 JCR存储库。
关于JCR API的常见误解
虽然JCR是AEM内容存储库,但其API不是与内容进行交互的首选方法。 相反,您更喜欢AEM API(Page、Assets、Tag等)或Sling资源API,因为它们提供了更好的抽象。
JCR API的常见用法
-
JCR观察(侦听JCR事件)
-
创建深层节点结构
OSGi API
OSGi API与更高级的API(AEM, Sling、和JCR),并且使用OSGi API的需要很少并且需要高水平的AEM开发专业知识。
OSGi与Apache Felix API
OSGi定义所有OSGi容器都必须实现并遵循的规范。 AEM OSGi实施Apache Felix也提供了若干自己的API。
- 首选OSGi API (
org.osgi
)通过Apache Felix API(org.apache.felix
)。
OSGi API的常见用法
-
用于声明OSGi服务和组件的OSGi注释。
- 首选 OSGi Declarative Services (DS) 1.2注释 超过 Felix SCR注释 声明OSGi服务和组件
-
用于动态代码内消息的OSGi API 取消/注册OSGi服务/组件.
- 当不需要条件OSGi服务/组件管理时(最常见的情况是),首选使用OSGi DS 1.2注释。
规则的例外
以下是上述定义的规则的常见例外。
OSGi API
在处理低级OSGi抽象概念(例如在OSGi组件属性中定义或读取)时,提供的较新抽象概念 org.osgi
比更高级别的Sling抽象概念更可取。 与之竞争的Sling抽象概念尚未标记为 @Deprecated
并提出建议 org.osgi
替代项。
另请注意OSGi配置节点定义首选 cfg.json
超过 sling:OsgiConfig
格式。
AEM资源API
-
首选
com.day.cq.dam.api
超过com.adobe.granite.asset.api
.- 而
com.day.cq
Assets API为AEM资产管理用例提供了更加免费的工具。 - Granite Assets API支持低级资产管理用例(版本、关系)。
- 而
查询API
- AEM QueryBuilder不支持某些查询函数,例如 建议、拼写检查和索引提示以及其他不太常见的函数。 首选使用JCR-SQL2函数进行查询。
Sling Servlet注册 sling-servlet-registration
- Sling servlet注册,首选 带有@SlingServletResourceTypes的OSGi DS 1.2注释 超过
@SlingServlet
Sling 筛选器注册 sling-filter-registration
- Sling 过滤器注册,首选 带有@SlingServletFilter的OSGi DS 1.2注释 超过
@SlingFilter
有用的代码段
下面是一些有用的Java™代码片段,它们说明了使用讨论的API的常见用例的最佳实践。 这些代码片段还说明了如何从不太首选的API迁移到更首选的API。
JCR会话至 Sling ResourceResolver
自动关闭Sling ResourceResolver
自AEM 6.2起, Sling ResourceResolver AutoClosable
在 试用资源 语句。 使用此语法,显式调用 resourceResolver .close()
不需要。
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
try (ResourceResolver resourceResolver = rrf.getResourceResolver(authInfo)) {
// Do work with the resourceResolver
} catch (LoginException e) { .. }
手动关闭的Sling ResourceResolver
必须在中手动关闭ResourceResolvers finally
块,如果以上显示的自动关闭技术不可用。
@Reference
ResourceResolverFactory rrf;
...
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, jcrSession);
ResourceResolver resourceResolver = null;
try {
resourceResolver = rrf.getResourceResolver(authInfo);
// Do work with the resourceResolver
} catch (LoginException e) {
...
} finally {
if (resourceResolver != null) { resourceResolver.close(); }
}
JCR路径 Sling Resource
Resource resource = ResourceResolver.getResource("/path/to/the/resource");
JCR节点到 Sling Resource
Resource resource = resourceResolver.getResource(node.getPath());
Sling Resource 到AEM资产
推荐的方法
此 DamUtil.resolveToAsset(..)
函数解析 dam:Asset
到Asset对象。
Asset asset = DamUtil.resolveToAsset(resource);
替代方法
将资源调整为适应资产需要资源本身 dam:Asset
节点。
Asset asset = resource.adaptTo(Asset.class);
Sling “资源到AEM”页
推荐的方法
pageManager.getContainingPage(..)
解析下的任何资源 cq:Page
到Page对象,方法是根据需要向上浏览树。
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page page = pageManager.getContainingPage(resource);
Page page2 = pageManager.getContainingPage("/content/path/to/page/jcr:content/or/component");
替代方法 alternative-approach-1
将资源调整为页面需要资源本身 cq:Page
节点。
Page page = resource.adaptTo(Page.class);
读取AEM页面属性
使用页面对象的getter获取已知属性(getTitle()
, getDescription()
,等等)和 page.getProperties()
以获取 [cq:Page]/jcr:content
用于检索其他属性的值映射。
Page page = resource.adaptTo(Page.class);
String title = page.getTitle();
Calendar value = page.getProperties().get("cq:lastModified", Calendar.getInstance());
读取AEM资源元数据属性
Asset API提供了从读取属性的简便方法 [dam:Asset]/jcr:content/metadata
节点。 这不是ValueMap,不支持第二个参数(缺省值和自动类型转换)。
Asset asset = resource.adaptTo(Asset.class);
String title = asset.getMetadataValue("dc:title");
Calendar lastModified = (Calendar) asset.getMetadata("cq:lastModified");
读取 Sling Resource 属性 read-sling-resource-properties
当资产存储在AEM API(Page、Asset)无法直接访问的位置(资产或相对资源)时, Sling 可使用资源和ValueMap获取数据。
ValueMap properties = resource.getValueMap();
String value = properties.get("jcr:title", "Default title");
String relativeResourceValue = properties.get("relative/propertyName", "Default value");
在这种情况下,可能必须将AEM对象转换为 Sling Resource 以有效地找到所需的属性或子资源。
AEM页至 Sling Resource
Resource resource = page.adaptTo(Resource.class);
将AEM资产发送至 Sling Resource
Resource resource = asset.adaptTo(Resource.class);
写入属性,使用 Sling的ModifiableValueMap
使用 Sling的 ModifiableValueMap 将属性写入节点。 这只能写入直接节点(不支持相对属性路径)。
记下对的调用 .adaptTo(ModifiableValueMap.class)
需要资源的写入权限,否则返回null。
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("newPropertyName", "new value");
properties.put("propertyNameToUpdate", "updated value");
properties.remove("propertyToRemove");
resource.getResourceResolver().commit();
创建AEM页面
始终使用PageManager创建页面,因为它需要使用“页面模板”,这是在AEM中正确定义和初始化页面所必需的。
String templatePath = "/conf/my-app/settings/wcm/templates/content-page";
boolean autoSave = true;
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
pageManager.create("/content/parent/path", "my-new-page", templatePath, "My New Page Title", autoSave);
if (!autoSave) { resourceResolver.commit(); }
创建 Sling 资源
ResourceResolver支持用于创建资源的基本操作。 在创建更高级别的抽象概念(AEM Pages、Assets、Tags等)时,请使用其各自经理提供的方法。
resourceResolver.create(parentResource, "my-node-name", new ImmutableMap.Builder<String, Object>()
.put("jcr:primaryType", "nt:unstructured")
.put("jcr:title", "Hello world")
.put("propertyName", "Other initial properties")
.build());
resourceResolver.commit();
删除 Sling 资源
ResourceResolver支持删除资源。 在创建更高级别的抽象概念(AEM Pages、Assets、Tags等)时,请使用其各自经理提供的方法。
resourceResolver.delete(resource);
resourceResolver.commit();