在JSTL循环中调用带参数的方法



我有一个JSP需要打印一些文本,这些文本是通过接受循环迭代器并将其提供给另一个对象(Spring bean)产生的,类似于:

<c:forEach var="myVar" items="${myVars}">
   <c:out value="anotherObject.getFoo(myVar)"/>
</c:forEach>

显然上面的代码是无效的,因为JSTL .操作符只允许无参数调用。我可以看到以下解决方案:

1) scriptlet

<c:forEach var="myVar" items="${myVars}">
  <%
    SomeType myVar = (SomeType) pageContext.getAttribute("myVar");
    SomeOtherType anotherObject = (SomeOtherType) pageContext.getAttribute("anotherObject");
    YetAnotherType result = anotherObject.getFoo(myVar);
    pageContext.setAttribute("result", result);
  %>
  <c:out value="${result}"/>
</c:forEach>

这里明显的缺点是JSP代码污染和总体丑陋。

2)写一个标签,做任何在scriptlet内完成。过度工程的典型例子,恶心!

3)分解myVars的集合,将每个myVar替换为一个动态代理,其中InvocationHandler将增加额外的无参数方法,使所有getFoo()调用都通过anotherObject。所有这些都将在控制器中完成,因此JSP将保持干净,myVar保持不变。但代价是什么呢?

我不能将.getFoo()方法添加到myVar中,因为它不适合那里,并且会破坏关注点分离。

看起来在JSP/EL 2.2中可以传递参数,但我使用的是Tomcat 6.0.29,它只绑定了EL 2.1 API。

问题:对于这种情况,谁能建议最干净的方法?

一个简单的java"技巧修复",也适用于旧的JSTL版本,并且不需要额外的标签库/配置/依赖/框架等。是"包装"你想从JSTL调用的函数在从Map类扩展的类中,并覆盖其get()方法。

作为一个最小的例子,如果你想从JSTL调用Math.sin()函数,你可以定义一个类:

public class Sine extends HashMap<Double, Double> {
    private static final long serialVersionUID = 1L; // Avoids compiler-warning
    @Override
    public Double get(Object arg) {
        Double x = (Double) arg;
        return Math.sin(x);
    }
}

然后在Action execute()方法中,你做:

...
request.setAttribute("sine", new Sine());
...

在jsp中你可以这样写:

  ${sine[0.75]}

计算Math.sin(0.75)

JSTL将把变量sin视为Map,但是您可以从get()方法中计算并返回任何您喜欢的内容。

我想如果你的函数有多个参数它会变得更复杂,但是也应该有解决方法:)

最后我是这样做的。

不是传递SomeType实例的集合,而是传递一个映射。映射键是相同的SomeType,值是特定于控制器的内部类的实例,我们称之为SomeTypeSupplement

SomeTypeSupplement添加了必要的无参数getter并将所有内容连接在一起。JSP现在遍历映射条目,并且能够通过JSTL检索数据。

这样我避免了Proxy魔术,不必要的tld,保持jsp整洁和合理的类型安全。

如果您不能忍受scriptlet(您的备选方案1),我会为它创建一个自定义标记(您的备选方案2)或自定义EL函数。我不同意这是"过度工程",这是使用可用的工具来达到预期的目的。

但是,我不喜欢你的选择。如果有的话,那就是过度工程,而且它会使您的系统变得不必要的复杂,并且使其他人难以遵循。坚持你工作的规范和标准的意图。不要为了让事情变得更困难或更复杂。

为什么不在后端Java代码中编写对象列表,然后使用JSP显示它呢?

想给Rop的最后一个(按时间)答案添加评论,但缺少"reputation",所以我在这里给出一个答案。

我很快就对这样一个映射有了同样的想法,但试图使它更普遍可用,有一个"DynamicMap",DynamicMapCalculator接口,让你把任何方法调用包装到这样一个映射(不需要每次都做一个新的映射实现,只是使用匿名类实例化)。

如果你感兴趣,这将是主题:一种风格:动态映射JSTL hack来解决缺少参数函数调用

我对那里的意见很感兴趣:你做这件事会不会良心不安?

另一个选择是使用Velocity。

最新更新