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

min*_*das 7 java jsp jstl

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

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

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

1)Scriptlets

<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>
Run Code Online (Sandbox Code Playgroud)

这里显而易见的是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.

问题:有人能为这种情况建议最干净的方法吗?

Rop*_*Rop 7

在旧的JSTL版本中也可以使用的简单的Java"技巧修复",并且不需要额外的taglibs/config/dependencies/frameworks等,是在一个类中"包装"你想从JSTL调用的函数.从Map类扩展,并覆盖其get()方法.

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在Action execute()方法中执行:

...
request.setAttribute("sine", new Sine());
...
Run Code Online (Sandbox Code Playgroud)

然后在jsp中你可以说:

  ${sine[0.75]}
Run Code Online (Sandbox Code Playgroud)

计算价值 Math.sin(0.75)

JSTL会将变量sine视为a Map,但您可以从get()方法计算并返回任何您喜欢的内容.

如果你的函数有多个参数,我想它会涉及更多,但是应该有相同的解决方法:)


min*_*das 2

这就是我最后的做法。

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

SomeTypeSupplement添加必要的无参数吸气剂并将所有内容连接在一起。JSP 现在迭代映射条目并能够通过 JSTL 检索数据。

通过这种方式,我可以避免Proxy神奇的、不必要的 TLD,保持 JSP 的整洁和相当的类型安全。