从百万美分电子邮件模板访问应用程序上下文bean

时间:2015-07-06 11:24:09

标签: spring thymeleaf

我已按照此Thymeleaf教程“Rich HTML email in Spring with Thymeleaf”使用Thymeleaf模板生成电子邮件。 一切都很好,但我无法访问我在应用程序中其他地方使用的ApplicationContext bean。

更具体地说,在我的电子邮件模板中,我希望有类似的内容:

<span th:text="${@myBean.doSomething()}">

其中“myBean”是@Component。 这有可能吗? 我不是在寻找像

这样的解决方法
<span th:text="${myBean.doSomething()}">

将bean作为变量添加到模板处理上下文中。

配置:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
public class MyWebConfig extends WebMvcConfigurerAdapter {
[....]
public ClassLoaderTemplateResolver emailTemplateResolver() {
    ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
    resolver.setPrefix("emailtemplates/");
    resolver.setSuffix(".html");
    resolver.setCharacterEncoding("UTF-8");
    resolver.setTemplateMode("HTML5");
    return resolver;
}

@Bean
public SpringTemplateEngine emailTemplateEngine() {
    SpringTemplateEngine engine = new SpringTemplateEngine();
    engine.addTemplateResolver(emailTemplateResolver());
    engine.addDialect(new LayoutDialect()); // thymeleaf-layout-dialect
    addSpringSecurityDialect(engine); // thymeleaf-SpringSecurity-dialect
    return engine;
}
[....]
}

电子邮件服务:

@Service
public class MyEmailService {
@Resource SpringTemplateEngine emailTemplateEngine;
[....]
public boolean sendHtmlEmail(...) {
    final Context ctx = new Context(locale);
    ctx.setVariable("someVariable", "someValue"); // Don't want to add myBean here
    final String body = this.emailTemplateEngine.process("myTemplate", ctx);
    [....]

组件:

@Component
public class MyBean {
    public String doSomething() {
        return "Something done!";
    }
}

模板:

<span th:text="${@myBean.doSomething()}">

错误:

  

EL1057E:(pos 1):在上下文中没有注册的bean解析器要解析   访问bean'myBean'

我正在使用thymeleaf-2.1.4和spring-4.1.6

编辑:

请注意,我无法使用HttpServletRequest,因为我使用@Async方法发送大部分电子邮件,但请求无法可靠地使用。这就是为什么我使用Context而不是WebContext的原因(虽然我不知道SpringWebContext - 我想如果有人通过“bean”变量创建该类来访问bean,那么使用@myBean表示法必须是不可能的)。

3 个答案:

答案 0 :(得分:4)

这令人困惑,因为我们在这里有三种不同的上下文:

  • 如果我们以预定的方式发送邮件,则不是ApplicationContext的春季WebApplicationContext
  • 在其上设置/添加变量的Thymeleaf org.thymeleaf.context.Context
  • SpEL评估上下文org.springframework.expression.EvaluationContext,用于评估SpEL表达式。对于Thymeleaf,就是ThymeleafEvaluationContext

如果您没有Spring Web上下文,则只需添加ThymeleafEvaluationContext和对应用程序上下文的引用作为Thymeleaf Context的变量:

例如(在科特林)

@Service
class TemplateService(
        private val templateEngine: TemplateEngine,
        private val applicationContext: ApplicationContext
) {

    fun processTemplate(locale: Locale, template: String, vararg variables: Pair<String, Any?>): String {
        val context = Context(locale)

        // Set the Thymeleaf evaluation context to allow access to Spring beans with @beanName in SpEL expressions
        context.setVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME,
                ThymeleafEvaluationContext(applicationContext, null))

        // Set additional variables
        variables.forEach { (key, value) -> context.setVariable(key, value) }

        return templateEngine.process(template, context)
    }
}

答案 1 :(得分:2)

实测值, 你需要在SpringWebContext中添加一个bean解析器。 Bean Resolver由ThymeleafEvaluationContext创建。

    Map<String, Object> mergedModel = new HashMap<>();
    ConversionService conversionService = (ConversionService) this.request
            .getAttribute(ConversionService.class.getName()); // might be null!
    ThymeleafEvaluationContext evaluationContext = new ThymeleafEvaluationContext(this.ac,
            conversionService);
    mergedModel.put(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME,
            evaluationContext);
    SpringWebContext ctx = new SpringWebContext(this.request, this.response,
            this.request.getServletContext(), this.request.getLocale(), mergedModel, this.ac);

答案 2 :(得分:1)

简单地将完整的Context放在themeleaf上下文中

@Service
   public class MyEmailService {
   @Resource SpringTemplateEngine emailTemplateEngine;
   [....]
   public boolean sendHtmlEmail(...) {
       final Context ctx = new Context(locale);
      ctx.setVariable("beans", new Beans(appContext)); 
    final String body = this.emailTemplateEngine.process("myTemplate", ctx);
      [....]



<span th:text="${beans.myBean.doSomething()}">

@符号可能是一个问题,因为如果我理解正确,模板将使用spring el表达式进行评估。也许可以做到,但耗时。