【问题标题】:Calling a method with parameter inside JSTL loop在 JSTL 循环内调用带参数的方法
【发布时间】:2011-07-20 10:37:48
【问题描述】:

我有一个 JSP 需要打印一些文本,这些文本是通过使用循环迭代器并将其提供给另一个对象(Spring bean)而产生的,例如:

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

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

1) 小脚本

<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 替换为dynamic proxyInvocationHandler,其中将添加额外的无参数方法以使所有getFoo() 调用都通过anotherObject。所有这些都将在控制器中完成,因此 JSP 将保持干净并且myVar 保持不变。但价格是多少?

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

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

问题:任何人都可以针对这种情况提出最干净的方法吗?

【问题讨论】:

    标签: java jsp jstl


    【解决方案1】:

    一个简单的仅限 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 会将变量正弦视为Map,但您可以通过 get() 方法计算并返回任何您喜欢的值。

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

    【讨论】:

    • 非常好用又棘手,非常感谢。对于那些不熟悉这些东西的人 modelAndView.addObject("sine", new Sine());相当于spring MVC版本的request.setAttribute("sine", new Sine());
    【解决方案2】:

    这是我最后的做法。

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

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

    这样我可以避免 Proxy 魔法、不必要的 TLD,保持 JSP 整洁和合理的类型安全。

    【讨论】:

      【解决方案3】:

      如果您不能遵守 scriptlet(您的替代方案 1),我会为它创建一个自定义标签(您的替代方案 2)或自定义 EL 函数。我不同意这是“过度设计”,而是将可用工具用于其预期目的。

      不过,我不喜欢你的替代方案 3。如果有的话,那就是过度设计,而且它会使您的系统变得不必要地复杂,并且其他人更难以遵循。遵守您所使用的规范和标准的意图。不要为了它而使事情变得更难或更复杂。

      【讨论】:

      • 如果为每个单独的方法调用写一个JSP标签不是过度工程,那我不知道什么是过度工程。我不是说选项#3 是好的/首选,但也不是#2。
      • 好吧,如果您有很多实例必须在 JSP:s 中显式调用方法,那么我建议您改进 MVC 模式并努力创建一个更简洁的模型层保存数据。然后将任何逻辑移动到您的控制器层。
      • myVar 对象是您所谓的模型(仅保存数据的对象 - 在本例中为持久性数据)的精确示例。然而,我需要可视化的数据来自完全不同的来源(“anotherObject”)并且需要这个模型类。您将逻辑移动到控制器层的建议接近我建议的选项#3,您说这是一个坏主意。
      • 我指的是您关于动态代理以实现一些类似 AOP 的行为的想法,我仍然认为它过于复杂并且随着时间的推移将难以维护。我会重构您的支持代码(控制器)以从“anotherObject”实例中提取所需信息并将其存储为更清晰的模型对象。
      【解决方案4】:

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

      【讨论】:

      • 我认为您没有理解我所面临的问题。 ${myVars} 已经引用了一个对象列表。
      【解决方案5】:

      想为 Rop 的最后(按时间)答案添加评论,但缺少“声誉”,所以我在这里回答。

      我很快就对这样的地图产生了相同的想法,但试图使其更普遍可用,具有“DynamicMap”、DynamicMapCalculator 接口并允许您将任何方法调用包装到这样的地图中(无需制作新的每次都映射实现,只是使用匿名类实例化)。

      如果您有兴趣,这将是主题: A qu. of style: dynamic map JSTL hack to work around missing parameter function calls

      我会对那里的意见感兴趣:这是你可以不问良心的事情吗?

      【讨论】:

        【解决方案6】:

        另一种选择是使用Velocity。比jstl好多了。

        【讨论】:

        • 投票支持创意替代方案(对我个人而言也是首选),但我们不能总是选择允许我们提供意见的方式
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-01
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 2022-06-23
        相关资源
        最近更新 更多