我有一个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。