You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:15:40