Thymeleaf预处理调用实体get方法传参为null问题求助
Thymeleaf预处理传参为null问题
问题现象
在Thymeleaf中使用预处理功能,根据用户是否完成挑战为<i>标签追加对应class。定义了接收用户名字符串参数的isSolvedBy方法,用于校验用户是否完成对应挑战。模板中通过调用返回当前登录用户用户名的getUsername方法获取入参,但调用isSolvedBy方法时,接收到的参数值为null,而单独调用user.getUsername()可以正常获取到真实用户名,整体流程无报错。
相关代码
Controller代码片段
model.addAttribute("user", user.get()); model.addAttribute("score", user.get().getScore()); model.addAttribute("rank", user.get().getUserRank()); model.addAttribute("challenges", challengeList); String challengesNumber = Integer.toString(challengeList.size()); model.addAttribute("challengesNumber", challengesNumber); return "challenges";
challenges视图代码片段
<a th:each="challenge: ${challenges}" th:href="@{/challenge(id=${challenge.challengeId})}" class="challenge-card white-text" style="text-decoration: none"> <h3 style="margin-top: 5px;" th:text="${challenge.challengeTitle}" class="golden-text"></h3> <i th:classappend="${challenge.isSolvedBy(__${user.getUsername()}__)}"></i> <!-- 预处理写法位置 --> <div> <h4 class="card-info">Points: <span class="golden-text" th:text="${challenge.challengeScore}"></span></h4> <h4 class="card-info">Level: <span class="" th:text="${challenge.level}"></span></h4> </div> </a>
challenge.isSolvedBy方法代码
public String isSolvedBy(String username) { System.out.println(username); // 控制台打印null return "solved-challenge"; // 临时返回值 }
user.getUsername()方法代码
public String getUsername() { System.out.println(username); // 被Thymeleaf调用时可正常打印真实用户名 return username; }
运行日志
2022-07-11 01:09:07.365 INFO 19416 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-07-11 01:09:07.365 INFO 19416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-07-11 01:09:07.366 INFO 19416 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms yousef [getUsername方法打印] null [isSolvedBy方法打印] yousef [getUsername方法打印] null [isSolvedBy方法打印]
问题根因
问题出在预处理语法的执行逻辑上:__${}__是Thymeleaf的预处理标记,会在执行外层${}表达式之前,先计算内部表达式的值,直接将结果作为纯文本拼接到外层表达式中,之后再执行拼接完成的外层表达式。
当user.getUsername()返回字符串yousef时,预处理完成后外层表达式会被拼接为:
${challenge.isSolvedBy(yousef)}
此时外层SpringEL表达式会把yousef识别为上下文中的变量名进行解析,而非字符串字面量。由于上下文里不存在名为yousef的变量,解析结果为null,最终传给isSolvedBy的参数就是null。该逻辑和日志现象完全匹配:预处理阶段调用getUsername打印了正确的用户名,外层表达式执行时因找不到对应变量传入null,循环遍历挑战列表时就会重复出现该打印顺序。
修复方案
两种写法均可解决问题,按需选择即可:
- 方案1(推荐,无需使用预处理):直接在外层表达式中传递方法调用结果,SpringEL会自动先计算参数值再调用目标方法,不需要额外预处理:
<i th:classappend="${challenge.isSolvedBy(user.getUsername())}"></i>
- 方案2(保留预处理写法):给预处理占位符外层包裹单引号,明确标记拼接后的内容是字符串字面量,避免被当成变量解析:
<i th:classappend="${challenge.isSolvedBy('__${user.getUsername()}__')}"></i>
内容的提问来源于stack exchange,提问作者Yousef Ali
相关产品推荐
相关产品推荐

