AEM 使用 Sling Dynamic Include (SDI) 来实现基于 Sling 的 SSI 机制,它可以根据 Web 服务器、CDN 或代理服务器的能力动态选择 SSI(Server-Side Includes)或 ESI(Edge-Side Includes)。
AEM 通过 SDI(Sling Dynamic Include)判断是否将组件转换为 SSI,主要依据 resource-types 和 path,两者同时满足时,SDI 才会自动将组件内容转换为 SSI 语法。
1. Sling Dynamic Include (SDI) 的作用
在 AEM Dispatcher 和 CDN 之间,SDI 允许动态内容(如 Experience Fragments、页眉页脚)被替换为 SSI 或 ESI 指令,以便 后端 Web 服务器或 CDN 进行解析,而不是直接由 AEM 进行渲染。这可以提升缓存效率,同时确保部分内容始终是动态的。
SDI 的核心逻辑
- AEM 解析页面,但不会直接渲染指定组件
- SDI 插件替换这些组件为相应的 SSI/ESI 指令
- Web 服务器(如 Apache/Nginx)或 CDN(如 Akamai)解析这些指令
- 最终用户请求页面时,服务器会动态填充这些组件
2. 解析SDI 配置
{
"include-filter.config.enabled": true,
"include-filter.config.path": "/content/experience-fragments",
"include-filter.config.resource-types": [
"lenovo-www/components/content/flashfooter",
"lenovo-www/components/content/flashheader",
"lenovo-base/components/content/flashfooter/v1/flashfooter",
"lenovo-base/components/content/flashfooter/v1/flashheader"
],
"include-filter.config.extension": "html",
"include-filter.config.include-type": "$[env:SDI_INCLUDE_TYPE;default: ESI]",
"include-filter.config.add_comment": "$[env:SDI_INCLUDE_COMMENT;default: false]",
"include-filter.config.ttl": "",
"include-filter.config.required_header": "Server-Agent=Communique-Dispatcher",
"include-filter.config.ignoreUrlParams": [],
"include-filter.config.rewrite": true
}
关键配置项解析
| 配置项 | 作用 |
|---|---|
"include-filter.config.enabled": true | 启用 SDI 插件 |
"include-filter.config.path": "/content/experience-fragments" | 只对 /content/experience-fragments 下的内容启用 SDI |
"include-filter.config.resource-types": [...] | 只对这些 组件类型 启用 SDI,例如 flashfooter 和 flashheader |
"include-filter.config.extension": "html" | 只对 HTML 页面生效 |
"include-filter.config.include-type": "$[env:SDI_INCLUDE_TYPE;default: ESI]" | 默认使用 ESI(如果 Web 服务器支持 SSI,也可以切换到 SSI) |
"include-filter.config.add_comment": "$[env:SDI_INCLUDE_COMMENT;default: false]" | 是否在 HTML 代码中插入 SDI 处理的注释(默认关闭) |
"include-filter.config.selector": "$[env:SDI_INCLUDE_SELECTOR;default: nocache]" | 默认情况下源码中是nocache |
"include-filter.config.ttl": "" | 该项为空,意味着由缓存机制控制 TTL |
"include-filter.config.required_header": "Server-Agent=Communique-Dispatcher" | Dispatcher 代理的请求才会启用 SDI(避免本地开发时影响) |
"include-filter.config.rewrite": true | 允许对 include 语句进行 URL 重写 |
3. SDI 处理的 HTML 输出
在 AEM 直接渲染 的情况下,组件的 HTML 可能是:
<div class="flash-footer">...</div>
但在 SDI 处理 后,AEM 不会直接渲染内容,而是输出 ESI/SSI 指令,例如:
如果使用 ESI(默认)
<esi:include src="/content/experience-fragments/global-footer.html"/>
这样 CDN(如 Akamai) 会解析并插入 global-footer.html 的内容。
如果使用 SSI
如果 include-type 设为 SSI,AEM 可能输出:
<!--#include virtual="/content/experience-fragments/global-footer.html" -->
这样 Web 服务器(如 Apache/Nginx) 会解析这个指令。
4. 如何修改 SDI 以支持 SSI
你的配置默认是 ESI,但如果你想改成 SSI(让 Apache/Nginx 处理),可以调整:
"include-filter.config.include-type": "SSI"
然后,在 AEM 页面 HTML 源码中,你会看到:
<!--#include virtual="/content/experience-fragments/global-footer.html" -->
这时,Dispatcher 需要启用 mod_include(前面提到的 Apache 配置)。
5. SDI 在 AEM 中的典型应用场景
- 优化 Dispatcher 缓存
- 让整个页面缓存,但让某些部分动态加载
- CDN 动态渲染
- 通过 ESI 让 CDN 直接处理 Experience Fragments
- 减少 AEM 服务器压力
- 让 Web 服务器解析 SSI,而不是让 AEM 反复生成相同内容
6. AEM SDI 判断 SSI 逻辑
SDI 主要依赖于 两个核心条件:
- 组件的
resource-type在include-filter.config.resource-types列表中 - 组件所在的路径符合
include-filter.config.path规则
当 页面渲染时,SDI 过滤器会检查组件:
- 如果
resource-type和path都匹配- SDI 拦截组件的 HTML 输出
- 将 组件的 HTML 替换为 SSI 语法
- 页面最终渲染出
<!--#include virtual="..." -->
- 如果
resource-type或path任何一个不匹配- SDI 不会处理,组件仍然正常渲染 HTML
示例:SDI 配置解析
假设你的 OSGi 配置如下(/apps/project/config/sling/dynamic-include/config.json):
{
"include-filter.config.enabled": true,
"include-filter.config.path": "/content/experience-fragments",
"include-filter.config.resource-types": [
"project/components/content/dynamic-footer",
"project/components/content/dynamic-header"
],
"include-filter.config.extension": "html",
"include-filter.config.include-type": "SSI",
"include-filter.config.add_comment": false,
"include-filter.config.selector": "nocache",
"include-filter.config.required_header": "Server-Agent=Communique-Dispatcher",
"include-filter.config.rewrite": true
}
SDI 判断逻辑
| 页面组件 | 组件 resource-type | 组件 path | 符合 SDI 规则? | 结果 |
|---|---|---|---|---|
header 组件 | project/components/content/dynamic-header | /content/experience-fragments/homepage | ✅ 是 | 转换为 SSI |
footer 组件 | project/components/content/dynamic-footer | /content/experience-fragments/homepage | ✅ 是 | 转换为 SSI |
hero-banner 组件 | project/components/content/hero-banner | /content/experience-fragments/homepage | ❌ 不是 | 正常渲染 HTML |
header 组件 | project/components/content/dynamic-header | /content/we-retail/page1 | ❌ 不是 | 正常渲染 HTML |
结论
- 只有 同时匹配
path和resource-type的组件才会触发 SDI 生成 SSI。 - 未匹配的组件 直接输出 HTML,不会被 SDI 拦截。
Dispatcher 访问时的行为
当 Dispatcher 代理请求 时,SDI 会替换组件 HTML 为 SSI 指令:
<!--#include virtual="/content/experience-fragments/footer.html" -->
而非 Dispatcher 访问时(如 AEM 直接渲染页面),组件会正常渲染完整 HTML。
优化建议
如果你想让 更多组件自动转换为 SSI,可以:
- 在 OSGi 配置中添加更多
resource-types - 扩展
path规则,支持更多页面路径 - 使用
selector限制某些页面强制使用 SDI
例如,增加对 /content/we-retail 组件的支持:
"include-filter.config.path": "/content"
这样 /content/we-retail/* 下的符合 resource-types 规则的组件也会被 SDI 转换。
总结
AEM 通过 resource-type 和 path 同时匹配,才会自动把组件转换为 SSI
符合 SDI 规则的组件,在 Dispatcher 访问时会被替换为 <!--#include virtual="..." -->
SDI 仅拦截匹配的组件,其他组件不会受到影响
这样可以 灵活控制哪些组件通过 SSI 加载,提高页面缓存效率,同时保持部分组件动态加载的能力 。