为何Apache Sling会忽略HTL中data-sly-use的adaptable参数?
你猜的没错!这个问题确实是因为**SlingModelsUseProvider的服务优先级高于JavaUseProvider**导致的,咱们来一步步拆解原因和解决办法:
核心原因
在Sling 11中,SlingModelsUseProvider(负责处理Sling Model作为HTL Use对象)的默认服务排名是100,而支持adaptable参数的JavaUseProvider默认排名是200——OSGi中服务排名数值越小,优先级越高。
当你在HTL中写data-sly-use.c="${'com.adobe.examples.htl.core.models.HelloWorldModel' @ adaptable=child}"时,优先级更高的SlingModelsUseProvider会先接手处理,但它完全不支持adaptable这个参数,所以直接忽略了你的配置,还是从默认的Adaptable(当前页面的资源)去适配Sling Model,这就导致你拿到的始终是页面的resourceType。
解决方案
方案1:调整JavaUseProvider的优先级(快速适配原有代码)
通过修改OSGi配置,让JavaUseProvider的优先级超过SlingModelsUseProvider,这样它就能处理你的adaptable参数了:
- 创建或修改OSGi配置文件
org.apache.sling.scripting.sightly.impl.engine.extension.use.JavaUseProvider.cfg.json,内容如下:
{ "service.ranking": 90 }
- 把这个文件放到你的Sling项目的
config目录下,或者通过Felix Console(系统配置)找到Apache Sling HTL Java Use Provider,手动将其服务排名改为90(比100小即可)。
这样修改后,JavaUseProvider会优先处理你的Use API请求,adaptable=child就能正常生效,你的原有代码不需要任何改动。
方案2:改用Sling Model参数注入(更符合最佳实践)
既然SlingModelsUseProvider不支持adaptable,咱们可以换一种Sling Model原生支持的方式——通过参数注入传递子资源:
首先修改你的HelloWorldModel,添加一个接受Resource参数的构造方法:
@Model(adaptables = {Resource.class, SlingHttpServletRequest.class}) public class HelloWorldModel { private static Logger log = LoggerFactory.getLogger(HelloWorldModel.class); @OSGiService private SlingSettingsService settings; @SlingObject private ResourceResolver resourceResolver; private String resourceType; private String message; // 注入传入的子资源 @Inject public HelloWorldModel(@Named("resource") Resource resource) { ValueMap valueMap = resource.getValueMap(); this.resourceType = valueMap.get("sling:resourceType", "No resourceType"); } @PostConstruct public void init() { log.info("Reached init of HelloWorldModel"); message = "\tHello World!\n"; message += "\tResource type is: " + resourceType + "\n"; message += "\tUser id is " + resourceResolver.getUserID() + "\n"; } public String getResourceType() { return resourceType; } public String getMessage() { log.info("Inside getMessage() method"); return message; } }
然后修改HTL代码,通过@ resource=child传递子资源:
<div data-sly-list.child="${resource.listChildren}"> <sly data-sly-use.c="${'com.adobe.examples.htl.core.models.HelloWorldModel' @ resource=child}" /> ${c.resourceType} </div>
这种方式不需要依赖adaptable参数,完全符合Sling Model的设计规范,也能完美解决你的问题。
方案3:禁用SlingModelsUseProvider(不推荐)
如果你的项目中完全不需要用Sling Model作为HTL Use对象,可以考虑在OSGi中禁用SlingModelsUseProvider。但这种方式会影响其他依赖Sling Model的代码,所以只适合非常单一的场景。
总结
你的初始怀疑完全正确,优先级问题导致adaptable参数被忽略。推荐优先选择方案2(更符合Sling生态的最佳实践),如果想快速复用原有代码,方案1也是个不错的选择。
内容的提问来源于stack exchange,提问作者Spinner

