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

如何直接从Thymeleaf模板访问Spring单例Bean?

问题描述

我正在学习Spring,遇到了一个困惑。我定义了一个单例Bean:

@Component
public class PuzzleUtil {
  @Bean
  @Scope("singleton")
  public PuzzleUtil puzzleUtil() {
    return new PuzzleUtil();
  }

  public ArrayList<String> getCategories() {
    // ...
  }
}

在Web应用类中,加载index页面时会将categories设置为Model属性:

@Controller
@SpringBootApplication
public class MathPuzzlesWebApplication extends SpringBootServletInitializer {
  @Autowired
  private PuzzleUtil puzzleUtil;
  
  @GetMapping("/index.html")
  public String index(Model model) {
    model.addAttribute("categories", puzzleUtil.getCategories());
    return "index";
  }

  // ...
}

现在可以在index.html模板中访问categories数组:

<table>
<th:block th:each="category: ${categories}">
<tr>
    <td th:text="${category}"></td>
</tr>
</th:block>
</table>

一切运行正常,但我觉得在Controller中将属性添加到Model是多余的步骤,因为该Bean是单例,应该可以直接从模板访问它。尝试了以下写法:

<th:block th:each="category: ${puzzleUtil.categories}">

但出现错误:

EL1007E: Property or field 'categories' cannot be found on null

请问我是否遗漏了什么,或者我的需求本身不可行?

解决方案

1. 先修正Bean定义的冗余问题

你当前的PuzzleUtil类同时使用@Component和内部的@Bean方法,会导致Spring生成两个独立的PuzzleUtil实例,这是冗余且易出错的,必须二选一:

  • 保留@Component,删除内部@Bean方法:
@Component
public class PuzzleUtil {
  public ArrayList<String> getCategories() {
    // ...
  }
}
  • 移除@Component,将@Bean方法放到专门的配置类中:
@Configuration
public class AppConfig {
  @Bean
  @Scope("singleton") // singleton是默认作用域,可省略
  public PuzzleUtil puzzleUtil() {
    return new PuzzleUtil();
  }
}

public class PuzzleUtil {
  public ArrayList<String> getCategories() {
    // ...
  }
}

2. 实现模板直接访问Spring Bean的两种方式

Thymeleaf不会自动把所有Spring Bean暴露到模板上下文,需通过以下方式实现:

方式一:全局注入到模板上下文

创建@ControllerAdvice类,将PuzzleUtil添加到所有模板的全局模型中:

@ControllerAdvice
public class GlobalControllerAdvice {
  private final PuzzleUtil puzzleUtil;

  public GlobalControllerAdvice(PuzzleUtil puzzleUtil) {
    this.puzzleUtil = puzzleUtil;
  }

  @ModelAttribute("puzzleUtil")
  public PuzzleUtil getPuzzleUtil() {
    return puzzleUtil;
  }
}

之后所有模板都能直接通过${puzzleUtil}访问Bean,调用方法:

<th:block th:each="category: ${puzzleUtil.categories}">

注:Thymeleaf会自动根据属性名categories匹配对应的getCategories() getter方法,写法合法。

方式二:使用Thymeleaf Spring Bean表达式

Thymeleaf提供专属语法直接访问Spring容器中的Bean,无需额外配置,写法为@beanName:

<th:block th:each="category: ${@puzzleUtil.getCategories()}">

这里的puzzleUtil是Bean的默认名称(类名首字母小写),直接调用方法即可。

3. 错误原因解析

你之前遇到的EL1007E错误,本质是${puzzleUtil}在模板上下文中为null——Spring并没有把该Bean暴露为模板变量,自然找不到对应的属性。

内容的提问来源于stack exchange,提问作者Neil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:10:24