标签ID
作为一个例子,我将从这里重新讨论Tag id模型第一篇博客.在该示例中,多字段项有一个名为tag ids的属性。/标签
.我把这个注入到模型中ItemTags
@ChildResource ItemTags ();
和ItemTags
实现Iterable <标记>
interface ItemTags extends Iterable{} @Model(adaptables = Resource.class, adapters = ItemTags.class) final类ItemTagsImpl实现ItemTags {private final List 标签;@Inject public ItemTagsImpl(@Self @Via("resourceResolver") final TagManager TagManager, @Self final String[] ids) {this. .tags = Arrays.stream(ids) .map(tagManager::resolve) .filter(Objects::nonNull) .collect(Collectors.toUnmodifiableList());} @覆盖公共迭代器<标记>迭代器(){返回this.tags.iterator();} @覆盖公共无效forEach(最终消费者 action) {this.tags.forEach(action);} @覆盖公共Spliterator<标记> Spliterator(){返回this.tags.spliterator();}}
Sling模型代码的代码数量尽可能少。然而,标签在AEM中无处不在。我可能需要放置另一个同样需要解析标签的模型,这种可能性很高。让我们来研究一下我们拥有的一些代码重用选项。
实用工具类
的条件反射可能会创建一个类TagUtil.java
.Lombok的@UtilityClass,这是很容易的
@UtilityClass公共类TagUtil{公共列表<标签> resolve(最终TagManager TagManager,最终字符串…返回Arrays.stream(ids) .map(tagManager::resolve) .filter(Objects::nonNull) .collect(collections . tounmodifiablelist ());}}
读了@UtilityClass文档。实用程序类是最终的,它的函数是静态的,也可能不是被实例化.因此,在单元测试中很难模拟。要隔离要测试的代码,您需要PowerMockito.这很糟糕。如果不得不求助于PowerMockito,那么就不是在编写可测试代码。你应该这样做的原因还有很多避免使用实用工具类.我像躲避瘟疫一样躲避他们。
继承的基类
我是在一个面向对象的世界里长大的。我的第二个想法可能是面向对象的方法。继承与Sling模型一起工作。
@ConsumerType接口标签{列表<标签> getTags();} @ConsumerType接口项目扩展标签{字符串getTitle();页面getPage ();} @Model(adapters = Tagged.class, adaptables = Resource.class)类TaggedImpl实现Tagged {@Getter private List标签;@ValueMapValue(name = "tags", injectionStrategy = injectionStrategy . optional) private String[] id;@Self @Via("resourceResolver") private TagManager;@PostConstruct公共无效激活(){此。tags = Arrays.stream(ArrayUtils.nullToEmpty(this.ids)) .map(this.tagManager::resolve) .filter(Objects::nonNull) .collect(Collectors.toUnmodifiableList());}} @Model(adapters = Item.class, adaptables = Resource.class) final class ItemImpl extends TaggedImpl implements Item {@Getter @ValueMapValue(name = "jcr:title") private String title;@Getter @ResourcePath私有Page页面; }
在上面的例子中,我已经放弃了只使用接口的方法因为我必须进行扩展TaggedImpl
.OOP在某些场景下可能是有意义的,比如你有一组相关的对象。但在本例中,“标记”的概念非常灵活,几乎可以应用于任何东西,比如页面、组件或资产。如果您要添加另一个概念,如“链接”,就像在组件中可以有一个链接一样,会怎样?然后项将延长标记
这将会延伸有关
.
OSGi组件
我可能是在面向对象的世界中长大的,但在过去的几年里,我一直在OSGi容器中摸索。我的第三个想法是OSGi组件。
public interface TagService {Listresolve(TagManager TagManager, String…ids);} @Component(service = TagService.class) public final class TagServiceImpl实现TagService {@Override public List resolve(final TagManager TagManager, final String…返回Arrays.stream(ids) .map(tagManager::resolve) .filter(Objects::nonNull) .collect(collections . tounmodifiablelist ());}}
您可以将接口与实现分离,使其具有可比性。这有一个恼人的缺点。它没有集成到Sling Model的注入管道中。我仍然需要编写自定义代码来调用它解决(TagManager,弦…)
函数,在模型的构造函数中
@注入公共ItemTagsImpl(@Self @Via("resourceResolver") final TagManager TagManager, @OSGiService final TagService TagService, @Self final String[] id){此。tags = tagService。解决(tagManager, ids);}
自定义注入器
MyModel model = resource.adaptTo(MyModel.class)
.
你有没有想过为什么会这样?如何注入模型中的所有这些属性?看一下ModelAdapterFactory。createObject(对象,ModelClass < ModelType >)
注入是通过滚动所有注入器直到其中一个返回一个值来实现的。你需要更明确一点。调用注入器by
的名字
或使用
injector-specific注释
.
//通过name调用注入器@Inject @Source("valuemap")//或者使用注入器特定的注释@ValueMapValue私有字符串名;
我们来创建定制的注射器.如果您喜欢上面的OSGi组件解决方案,那么这并没有什么不同。注入器只是一个OSGi组件,用于ModelAdapterFactory.
@ServiceRanking(Integer.MAX_VALUE) @Component(service = {Injector.class})公共最终类TagIdInjector实现了Injector {@Override public String getName(){返回"tag-id";} @覆盖公共对象getValue(最终对象可适应,最终字符串名称,最终类型类型,最终AnnotatedElement元素,最终DisposalCallbackRegistry callbackRegistry){最终var tags =新ArrayList<标签>();final var resource =(资源)适应性强;final var ids = resource.getValueMap() .get(name, String[].class);if (ids != null) {Optional.ofNullable(resource.getResourceResolver() .adaptTo(tagManager .class)) .ifPresent(tagManager -> Arrays.stream(ids) .map(tagManager::resolve) .filter(Objects::nonNull) .forEach(tags::add));} return Collections.unmodifiableList(tags);}}
当然,这是一个非常简单的例子。的getValue
函数是做一些假设。它假设适应性是资源
.它总是返回一个非空值<标记>列表
.这很好,因为我们的模型正在适应一个资源,我们将通过它的名字来调用它:标签id
.
下面是最终的模型代码。
@Model(adaptables = {Resource.class})公共接口MyModel {@ValueMapValue String getText();@ChildResource(injectionStrategy = injectionStrategy . optional) List- getItems();@Model(adaptables = Resource.class) interface项目{@ValueMapValue(name = "jcr:title") String getTitle();@ResourcePath Page getPage();@Inject @Source(" Tag -id") List
getTags();}}
这比原来的代码要小很多。我回到了接口,这意味着我不需要用单元测试覆盖这段代码。我唯一要讲的就是注射器。
结论
如果你看ModelAdapterFactory。createObject(对象,ModelClass < ModelType >)代码,现在你知道了。@ inject
不是魔法。实用类很糟糕。自定义注入器是创建可重用注入器的最佳选择。
我们使用注入器名称@Source(“标签id”)
.我会把它留给你们作为练习,让你们自己去做自定义注释.看看开源注入器的实现。还要留意StaticInjectAnnotationProcessorFactory
接口。