表达式语言中的#{flash.keep.message} 中的方法链是如何工作的?

How does method chaining work in #{flash.keep.message} in Expression Language?

我有这个示例代码:

<h:form>
    <h:commandButton action="#{fooBar.foo()}" value="Submit"/>
</h:form>

在 bean 中:

@ManagedBean
@ApplicationScoped
public class FooBar {
    public String foo() {
        final Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
        flash.put("message", "Hello World");
        return "hello?faces-redirect=true";
    }
}

最后在 hello.xhtml

<h:body>
    #{flash.keep.message}
</h:body>

所以我转到 index.xhtml,点击提交,我按预期被重定向到 hello.xhtml。当我刷新页面时,我仍然看到消息,因为 flash.keep 行为很棒。

现在我想了解发生了什么,所以我打开 documentation

这个class中有一个keep()方法,但是它的return类型是void并且需要一个String参数。那么#{flash.keep.message}是在调用keep()方法时带message参数吗?我真的不这么认为,据我所知应该是#{flash.keep(message)},不是吗?

那么这里发生了什么?

EL 解析可以自定义 ELResolver implementations. There are two EL resolvers involved in evaluating #{flash.keep.message}. The first one, the JSF-builtin FlashELResolver#{flash} 上执行。正如您在其源代码中所见(行号匹配 Mojarra 2.2.12),

216        // and the property argument is "keep"...
217        if (property.toString().equals(FLASH_KEEP_VARIABLE_NAME))
218        {
219            elContext.setPropertyResolved(true);
220          
221            // then this is a request to promote the value
222            // "property", which is assumed to have been previously
223            // stored in request scope via the "flash.now"
224            // expression, to flash scope.
225            result = base;
226            // Set a flag so the flash itself can look in the request
227            // and promote the value to the next request
228            FlashFactory ff = (FlashFactory) 
229                    FactoryFinder.getFactory(FactoryFinder.FLASH_FACTORY);
230            ff.getFlash(true);
231            ELFlash.setKeepFlag(facesContext);
232        }

FlashELResolver 将在计算 #{flash.keep} 表达式时调用 ELFlash.setKeepFlag(facesContext)(第 231 行)。它还将 属性 设置为已解决(第 219 行),以便 EL 上下文可以使用下一个 属性 前进,并将 base#{flash})设置为已评估结果(第 225 行),如此有效 #{flash.keep} returns 完全相同的 #{flash} 对象。

然后,当 message 属性 要根据 #{flash.keep} 的结果进行评估时,它基本上仍然是 #{flash},但 "keep" 标志集,EL 内置 MapELResolver is executed. This is because #{flash} is essentially a Map, see also the javadoc(强调我的)。

public abstract class Flash
extends Object
implements Map<String,Object>

这会调用 Map#get() 方法,即 customized in Flash class 如下(行号匹配 Mojarra 2.2.12):

384     public Object get(Object key) {
385         Object result = null;
386 
387         FacesContext context = FacesContext.getCurrentInstance();
388         if (null != key) {
389             if (key.equals("keepMessages")) {
390                 result = this.isKeepMessages();
391             } else if (key.equals("redirect")) {
392                 result = this.isRedirect();
393             } else {
394                 if (isKeepFlagSet(context)) {
395                     result = getPhaseMapForReading().get(key);
396                     keep(key.toString());
397                     clearKeepFlag(context);
398                     return result;
399                 }
400 
401             }
402 
403         }

正如您在第 396 行看到的,它会在设置标志时调用 keep(key),就像 FlashELResolver 所做的那样。