如何在EL Stream API中使用Collectors#joining?JSP遇方法未找到问题
为什么Tomcat用自身版本的Stream?
Tomcat默认使用的是Apache EL(org.apache.el包下的实现),它为了适配JSP EL的执行环境、兼容旧版规范,并没有直接复用Java标准库的java.util.stream.Stream,而是自己实现了一套简化版的Stream类(org.apache.el.stream.Stream)。这套自定义Stream只实现了标准Stream的部分方法,而且方法签名和标准库不完全一致——比如你调用的collect(Collectors.joining()),Apache EL的Stream根本没有这个重载方法,所以才会抛出MethodNotFoundException。
如何让JSP EL代码和Java类行为一致?
要解决这个问题,核心是避开Apache EL的自定义Stream,让代码使用标准Java Stream的能力,这里有几个靠谱的方案:
方案1:后端提前处理(最推荐)
不要在JSP的EL里做Stream操作,而是在后端的Java代码(比如Servlet、Controller)里提前把列表转换成逗号分隔的字符串,再传到JSP中:
// 后端代码示例(Servlet/Controller) List<YourEntity> myList = ...; // 你的列表数据 String joinedStr = myList.stream() .map(YourEntity::getStringValue) .collect(Collectors.joining(", ")); request.setAttribute("joinedMyList", joinedStr); // 存到请求属性里
然后在JSP里直接使用:
<div>${joinedMyList}</div>
这种方式完全复用标准Java Stream的能力,和你在普通Java类里的行为完全一致,而且代码更清晰,也避免了EL环境的兼容性问题。
方案2:自定义EL函数
如果一定要在JSP中处理,可以封装一个自定义EL函数,把标准Stream的逻辑包进去:
- 写一个静态工具类,实现拼接逻辑:
package com.yourpackage.el; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class ListUtils { public static <T> String join(List<T> list, Function<T, String> mapper, String delimiter) { return list.stream().map(mapper).collect(Collectors.joining(delimiter)); } }
- 在
WEB-INF下创建一个TLD文件(比如list-functions.tld),定义EL函数:
<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1"> <tlib-version>1.0</tlib-version> <short-name>listfn</short-name> <uri>http://yourdomain.com/el/list-functions</uri> <function> <name>join</name> <function-class>com.yourpackage.el.ListUtils</function-class> <function-signature>java.lang.String join(java.util.List, java.util.function.Function, java.lang.String)</function-signature> </function> </taglib>
- 在JSP顶部引入这个标签库,然后调用函数:
<%@ taglib prefix="listfn" uri="http://yourdomain.com/el/list-functions" %> <div>${listfn:join(myList, e -> e.getStringValue(), ", ")}</div>
注意:如果你的Tomcat版本对EL中的Lambda表达式支持有限,可以把工具类改成针对特定属性的方法(比如专门处理getStringValue),避免使用Lambda。
方案3:升级Tomcat版本(谨慎选择)
部分较新的Tomcat版本(比如Tomcat 9.0.x后期版本、Tomcat 10+)对Apache EL的Stream实现做了优化,更贴近标准库的API。不过这种方式存在不确定性,而且升级Tomcat可能带来其他兼容性问题,所以优先级低于前两种方案。
为什么你找到的Stack Overflow答案没用?
那个答案是基于Tomcat自定义Stream的API来实现的,它用的是Apache EL Stream特有的方法,本质还是在Tomcat的自定义逻辑里打转,并没有使用标准Java Stream,所以不符合你“和Java类中行为一致”的需求。
内容的提问来源于stack exchange,提问作者Gustavo

