在 Wicket 6 中覆盖内置映射器的正确方法

A correct way to override a built-in mapper in Wicket 6

Wicket 6 有一个默认的复合请求映射器:SystemMapper。它包含 BookmarkableMapper。我需要覆盖它,即使用我自己的 FancyBookmarkableMapper 代替。

我尝试了什么:

  1. SystemMapperclass作为一个整体复制并更改以下行

add(new BookmarkableMapper());

add(new FancyBookmarkableMapper());

但这种方式在升级方面非常丑陋和脆弱(尽管它似乎可行)。我在这里提到它只是为了完整起见。

  1. 利用 SystemMapperICompoundMapper 的事实,使用 add()remove()iterator() 方法替换映射器:

    private SystemMapper customizeSystemMapper() {
        final SystemMapper rootRequestMapper = (SystemMapper) getRootRequestMapper();
    
        IRequestMapper originalBookmarkableMapper = null;
        boolean afterBookmarkable = false;
        List<IRequestMapper> mappersAfterBookmarkable = new ArrayList<>();
        for (IRequestMapper mapper : rootRequestMapper) {
            if (mapper.getClass() == BookmarkableMapper.class) {
                if (originalBookmarkableMapper != null) {
                    throw new IllegalStateException("There are two BookmarkableMapper instances in the initial mappers list");
                }
                originalBookmarkableMapper = mapper;
                afterBookmarkable = true;
            } else {
                if (afterBookmarkable) {
                    mappersAfterBookmarkable.add(mapper);
                }
            }
        }
        if (originalBookmarkableMapper == null) {
            throw new IllegalStateException("There is no BookmarkableMapper in the initial mappers list");
        }
    
        for (IRequestMapper mapperToRemove : mappersAfterBookmarkable) {
            rootRequestMapper.remove(mapperToRemove);
        }
        rootRequestMapper.remove(originalBookmarkableMapper);
        rootRequestMapper.add(new FancyBookmarkableMapper());
        for (IRequestMapper mapperToAdd : mappersAfterBookmarkable) {
            rootRequestMapper.add(mapperToAdd);
        }
        return rootRequestMapper;
    }
    

虽然效果也不错,但也不是很好。

  1. 构建 ICompoundMapper 的实现并用它修饰 SystemMapper 实例。替代内置映射器的唯一方法是尝试使用 iterator() 方法:

    @Override
    public Iterator<IRequestMapper> iterator() {
        return new Iterator<IRequestMapper>() {
            private Iterator<IRequestMapper> originalIterator = delegate.iterator();
    
            @Override
            public boolean hasNext() {
                return originalIterator.hasNext();
            }
    
            @Override
            public IRequestMapper next() {
                IRequestMapper nextMapper = originalIterator.next();
                if (nextMapper != null && nextMapper.getClass() == BookmarkableMapper.class) {
                    nextMapper = bookmarkableMapperReplacement;
                }
                return nextMapper;
            }
    
            @Override
            public void remove() {
                originalIterator.remove();
            }
        };
    }
    

但是,这不起作用,因为 SystemMapper#mapRequest()(实际上在 CompoundRequestMapper 中定义)直接使用 mappers 字段而不是通过 iterator() 方法。

  1. 最简单的方法:

    private SystemMapper customizeSystemMapper2() {
        final SystemMapper rootRequestMapper = (SystemMapper) getRootRequestMapper();
        rootRequestMapper.add(new FancyBookmarkableMapper());
        return rootRequestMapper;
    }
    

这里我们将映射器添加到列表的开头。它扩展了 BookmarkableMapper 并继承了它的 getCompatibilityScore(),因此它与 BookmarkableMapper 具有相同的分数,并且它在列表中较早,因此它具有优先权。

第 4 项确实有效。唯一让我问这个问题的是,实际上,对于这种方法,两个映射器都在 SystemMapper 的内部列表中。是否保证我的映射器(后来添加并具有相同分数)优先(包括未来的 Wicket 版本)?

最简单(也是官方)的方法是在 YourApplication#init() 方法中使用 WebApplication#mount(new FancyBookmarkableMapper ()

我明天会在 3) 中检查您的问题! 更新:用https://git1-us-west.apache.org/repos/asf?p=wicket.git;a=commitdiff;h=0eb63480;hp=5821157738ac43a09232a2aeb0fa2ff808340f4d改进了它 如果您看到更多改进,请告诉我们!谢谢!