Freemarker:访问模板中没有 getter 的 public 字段

Freemarker: access public field with no getter in template

我正在 Dropwizard 应用程序中设置视图,运行 遇到 Freemarker 的一个奇怪问题。

下面docs here我设置了一个非常简单的例子如下

public class ExampleFreemarkerView extends View {
  private Foo foo;

  public ContractHtmlView(Foo Foo) {
    super("FooView.ftl");
    this.foo = foo;
  }

  public Contract getFoo() { return foo };
}

public class Foo {
  public String bar = "Hello World";
}

FooView.ftl

<html>
  <body>
    <h1>${foo.bar}</h1>
  </body>
</html>

呈现 ExampleFreemarkerView 时的预期输出是 HTML 显示 Hello World 的文档。

实际发生的是 Freemarker 抛出异常,抱怨 ${foo.bar} - 特别是 bar - 未定义。

这似乎是因为 bar 是一个 public 字段,没有 getter。当我将 public String getBar() { return bar; } getter 添加到 Foo 时,它起作用了。

我对这种情况感到有些惊讶 - 即 Freemarker 似乎需要 getters 并且不能使用开箱即用的 public 字段。我故意在我的模型对象上使用 public 字段而不是 getters/setters,因此添加 getter 只是为了让 Freemarker 工作不是我会考虑的解决方案。

我在谷歌上搜索了很多,通读了 Freemarker 文档,但找不到任何方法来 'turn on' Freemarker 中的这种行为。 可能吗?

只是为了兴趣——我也尝试了上面的例子,完全一样,但是使用 Mustache 模板和 public 字段在那里工作正常(即 {{foo.bar}} 渲染 Hello World 没有问题).这解决了眼前的问题,所以这个问题主要是出于好奇,或者以防万一我出于其他原因决定使用 Freemarker 而不是 Mustache。


根据评论编辑 - 我知道 Freemarker 这样做(坚持 getter 开箱即用)以遵循 Java Beans 规范,但是 Java 生态系统中的大多数库都支持 public 字段 - Hibernate 和 Jackson 是突出的例子 - 在某种程度上我个人认为它是一个同样有效的标准并且发现库不支持它盒子令人惊讶。

freemarker docs

中有说明

Every object will be wrapped into a TemplateHashModel that will expose JavaBeans properties and methods of the object. This way, you can use model.foo in the template to invoke obj.getFoo() or obj.isFoo() methods. (Note that public fields are not visible directly; you must write a getter method for them.)

注意它也跟着java encapsulation concept

您也可以使用框架作为 lombok getters 自动使用仅 class 注释

这完全取决于 objectWrapper 配置设置。大多数项目正在使用的 DefaultObjectWrapper(和任何 BeansWrapper 子类)有一个 exposeFields 设置,可以设置为 true

在 Dropwizard 中,如果您以兼容的方式(基于 https://github.com/apache/freemarker-online-tester)设置 ViewBundle,则可以在配置 YML 中这样做:

viewRendererConfiguration:
  freemarker:  # was `.ftl:` before Dropwizard 1.3.0
    objectWrapper=DefaultObjectWrapper(2.3.28, exposeFields=true)