【问题标题】:Accessing application context beans from thymeleaf email template从 thymeleaf 电子邮件模板访问应用程序上下文 bean
【发布时间】:2015-09-23 12:37:48
【问题描述】:

我已按照 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 - 我猜如果有人通过“beans”变量创建了用于访问 bean 的类,那么使用 @myBean 表示法肯定是不可能的)。

【问题讨论】:

  • 尝试使用 SpringWebContext 而不是 Context。
  • SpringWebContext 是一个很好的建议,因为它添加了“类 (Beans) 的特殊 bean 变量,允许用户在应用程序上下文中访问 bean。这个变量可以像上下文中的任何其他变量一样被访问:${beans.myBean.doSomething()}。”不过,在我的特殊情况下,我没有提到我也不能使用 Request 对象,因为我在 @Async 中发送电子邮件 - 现在更新问题。
  • 是否可以选择自定义上下文实现? javased.com/?source_dir=thymeleaf-spring3/src/main/java/org/…
  • 是的,可以选择自定义上下文!如何将“@”变量的解析添加到上下文中?

标签: spring thymeleaf


【解决方案1】:

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

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

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

例如(在 Kotlin 中)

@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)
    }
}

【讨论】:

    【解决方案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);
    

    【讨论】:

    • 新 Beans(appContext) 自动添加到 SpringWebContext 构造函数中
    • @xtian 不,如果我们没有请求/响应,我们不能使用 SpringWebContext,您需要使用简单的 org.thymeleaf.context.Context。
    • thymeleaf 使用的 Spring EL 上下文的配置在这里会非常有用。我现在卡住了,因为我无法配置它而且我不在网络环境中。
    • 只需将 ThymeleafEvaluationContext(使用 applicationConext 和 null 作为转换服务)作为变量添加到 org.thymeleaf.context.Context 为我们工作:context.setVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME, new ThymeleafEvaluationContext(applicationContext, null))。无需网络上下文或请求/响应。
    【解决方案3】:

    只需将完整的 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()}">
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-11
      • 1970-01-01
      • 1970-01-01
      • 2016-03-31
      • 1970-01-01
      • 2014-02-09
      • 2019-05-04
      相关资源
      最近更新 更多