我有一个JSP需要打印一些文本,这些文本是通过获取循环迭代器并将其提供给另一个对象(Spring bean)生成的,类似于:
<c:forEach var="myVar" items="${myVars}">
<c:out value="anotherObject.getFoo(myVar)"/>
</c:forEach>
显然上面的代码无效,因为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>
这里显而易见的是JSP代码污染和普遍的丑陋。
2)编写一个标记来执行在scriptlet中完成的任何操作。过度工程的典型例子,哎呀!
3)分解myVars
的集合,并用dynamic proxy myVar
替换每个InvocationHandler
,其中getFoo()
将添加额外的无参数方法以使所有{{1}通过anotherObject
调用。所有这些都将在控制器中完成,因此JSP将保持干净并且myVar
保持不变。但是以什么价格?
我无法将.getFoo()
方法添加到myVar
,因为它不适合那里,会打破关注点的分离。
看起来在JSP / EL 2.2中可以传递参数,但我使用的是仅捆绑EL 2.1 API的Tomcat 6.0.29。
问题:有人能为这种情况建议最干净的方法吗?
答案 0 :(得分:7)
一个简单的仅限Java的“技巧修复”,也适用于较旧的JSTL版本,
并且不需要额外的taglibs / config / dependencies / frameworks等。
是“包装”您要从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会将变量sine视为Map
,但您可以从get()方法计算并返回任何您喜欢的内容。
如果你的函数有多个参数,我想它会更复杂一些, 但也应该有解决办法:)
答案 1 :(得分:2)
这就是我最后如何做到的。
我没有传递SomeType
个实例的集合,而是传递了一张地图。映射键是相同的SomeType
,值是控制器特定内部类的实例,我们称之为SomeTypeSupplement
。
SomeTypeSupplement
添加必要的无参数getter并将所有内容连接在一起。 JSP现在迭代映射条目,并能够通过JSTL检索数据。
这样我就可以避免Proxy
魔法,不必要的TLD,保持JSP整洁,合理安全。
答案 2 :(得分:1)
如果您不能遵守scriptlet(替代方案1),我会为它(您的备选方案2)或自定义EL函数创建自定义标记。我不同意它是“过度工程”,它使用可用的工具用于其预期目的。
你的另类3,但我不喜欢。如果有的话,那么过度工程加上它将使你的系统变得不必要地复杂,并且让其他人更难以遵循。坚持您正在使用的规范和标准的意图。不要为了它而使事情变得更难或更复杂。
答案 3 :(得分:1)
为什么不在后端Java代码中编写对象列表,然后只使用JSP来显示它?
答案 4 :(得分:1)
想要在Rop的最后(按时间)回答中添加评论,但缺少“声望”,所以我在这里得到答案。
我很快就对这样的地图有了同样的想法,但试图让它更普遍可用,有一个“DynamicMap”,DynamicMapCalculator接口,让你将任何方法调用包装到这样的地图中(无需制作新的每次都执行map实现,只使用匿名类实例化。)
如果您有兴趣,这将成为主题: A qu. of style: dynamic map JSTL hack to work around missing parameter function calls
我会对那里的意见感兴趣:这是否可以在没有良心的情况下做到?
答案 5 :(得分:0)
另一种选择是使用Velocity。它比jstl好得多。