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

Spring自定义SpringTemplateEngine Bean未被Service使用,模板渲染失败

问题描述

尝试使用指定模板发送邮件时,调用templateEngine.process未抛出异常,但未对模板进行渲染处理,仅返回模板名称字符串(如activationEmail.html),最终收到的邮件内容就是该字符串。

POM依赖

<dependencies>
    <!-- Spring Boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Spring Doc -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.1.0</version>
    </dependency>

    <!-- Eureka -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- LOMBOK -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.26</version>
    </dependency>
</dependencies>

配置类

@Configuration
public class EmailConfiguration {

    @Bean
    public SpringTemplateEngine springTemplateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.addTemplateResolver(htmlTemplateResolver());
        return templateEngine;
    }

    @Bean
    public ClassLoaderTemplateResolver htmlTemplateResolver(){
        ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
        emailTemplateResolver.setPrefix("classpath:/templates/");
        emailTemplateResolver.setSuffix(".html");
        emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
        emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
        return emailTemplateResolver;
    }
}

控制器

@PostMapping("/send")
public boolean sendEmail(@RequestParam("lang") String language, @RequestBody EmailRequestDTO payload) throws NotifierEmailException {
    emailService.sendSimpleMailMessage(payload, language);
    return true;
}

服务类

@Service
public class EmailService {

    private static final String ENCODING = "UTF-8";

    private final JavaMailSender emailSender;

    public final TemplateEngine template = new SpringTemplateEngine();

    @Autowired
    public EmailService(JavaMailSender emailSender) {
        this.emailSender = emailSender;
    }

    public void sendSimpleMailMessage(EmailRequestDTO emailRequestDTO, String language) throws NotifierEmailException {
        try {
            var locale = Locale.forLanguageTag(language);
            // Prepare the evaluation context
            final Context thymeleafContext = new Context(locale);
            emailRequestDTO.getAttributes().forEach(thymeleafContext::setVariable);

            // Prepare message using a Spring helper
            final MimeMessage mimeMessage = emailSender.createMimeMessage();
            final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, ENCODING);
            message.setSubject(emailRequestDTO.getSubject());
            message.setFrom(emailRequestDTO.getEmailFrom());
            message.setTo(emailRequestDTO.getEmailTo());

            // Create the HTML body using Thymeleaf
            /* This line here returns a string with the template name that I passed! */
            final String htmlContent = template.process("activationEmail.html", thymeleafContext);
            message.setText(htmlContent, true);

            // Send mail
            emailSender.send(mimeMessage);
        } catch (Exception exception) {
            throw new NotifierEmailException(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());
        }
    }
}

补充说明

未通过application.properties配置Thymeleaf,模板文件放置在classpath:/templates/目录下,执行日志显示邮件内容为activationEmail.html,实际收到的邮件仅显示该字符串。


解决方案

问题核心是服务类中手动创建了SpringTemplateEngine实例,而非使用配置类中已配置好的Bean。你直接new SpringTemplateEngine()得到的实例没有添加任何模板解析器,Thymeleaf找不到对应模板文件,因此直接返回了模板名称。

修改步骤

  1. 删除服务类中手动创建的template字段
  2. 通过@Autowired注入配置类中定义的TemplateEngine(或SpringTemplateEngine)

修改后的服务类代码:

@Service
public class EmailService {

    private static final String ENCODING = "UTF-8";

    private final JavaMailSender emailSender;
    private final TemplateEngine templateEngine;

    @Autowired
    public EmailService(JavaMailSender emailSender, TemplateEngine templateEngine) {
        this.emailSender = emailSender;
        this.templateEngine = templateEngine;
    }

    public void sendSimpleMailMessage(EmailRequestDTO emailRequestDTO, String language) throws NotifierEmailException {
        try {
            var locale = Locale.forLanguageTag(language);
            final Context thymeleafContext = new Context(locale);
            emailRequestDTO.getAttributes().forEach(thymeleafContext::setVariable);

            final MimeMessage mimeMessage = emailSender.createMimeMessage();
            final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, ENCODING);
            message.setSubject(emailRequestDTO.getSubject());
            message.setFrom(emailRequestDTO.getEmailFrom());
            message.setTo(emailRequestDTO.getEmailTo());

            // 使用注入的模板引擎处理模板,无需加.html后缀(配置类已设置)
            final String htmlContent = templateEngine.process("activationEmail", thymeleafContext);
            message.setText(htmlContent, true);

            emailSender.send(mimeMessage);
        } catch (Exception exception) {
            throw new NotifierEmailException(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());
        }
    }
}

额外注意点

  • 调用process方法时,只需传入模板名称activationEmail即可,配置类中已设置后缀.html,无需重复添加
  • 确保classpath:/templates/activationEmail.html文件确实存在,且内容符合Thymeleaf语法规范

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 05:55:42