1. 对象包装器是实现了freemarker.template.ObjectWrapper接口的类。它的目标是实现Java对象(应用程序中特定类等, 比如: String, Map, List实例)和FTL类型系统之间的映射。
2. 对象包装器作为插件放入Configuration中, 可以使用object_wrapper属性设置(或者使用Configuration.setObjectWrapper)。
3. 要映射Java对象到FTL类型系统中, 对象包装器的TemplateModel wrap(java.lang.Object obj)方法会被调用。
4. 有时FreeMarker需要撤回映射, 此时对象包装器的Object unwrap(TemplateModel)方法就被调用了。最后的操作是在ObjectWrapperAndUnwrapper中, 它是ObjectWrapper的子接口。很多实际的包装器会实现ObjectWrapperAndUnwrapper接口。
5. 我们来看一下包装Java对象是如何进行的。可以这么说, 对象包装器将Object[]数组包装成TemplateSquenceModel接口的一些实现。当FreeMarker需要FTL序列中项的时候, 它会调用TemplateSquenceModel.get(int index)方法。该方法的返回值是TemplateModel, 也就是说, TemplateSquenceModel实现不仅仅可以从给定的数组序列获取对象, 也可以负责在返回它之前包装该值。为了解决这个问题, 典型的TemplateSquenceModel实现将会存储它创建的ObjectWrapper, 之后再调用该ObjectWrapper来包装包含的值。
6. 要创建TemplateModel的实现类, 请遵循这个原则, 可以使用freemarker.template.WrappingTemplateModel作为基类。
7. 默认对象包装器
7.1. object_wrapper Configuration的默认设置是freemarker.template.DefaultObjectWrapper实例。除非有特别的需求, 那么建议使用这个对象包装器, 或者是自定义的DefaultObjectWrapper的子类。
7.2. 它会识别大部分基本的Java类型, 比如: String, Number, Boolean, Date, List(通常还有全部的java.util.Collection类型), 数组, Map等。并把它们自然地包装成匹配TemplateModel接口的对象。
7.3. 关于DefaultObjectWrapper更多值得注意的细节:
7.3.1. 不用经常使用它的构造方法, 而是使用DefaultObjectWrapperBuilder来创建它。
7.3.2. DefaultObjectWrapper有incompatibleImprovements属性, 这在2.3.22或更高版本中是极力推荐的。如何来设置:
- 如果已经在2.3.22或更高版本的Configuration中设置了incompatible_improvements选项, 而没有设置object_wrapper选项(那么它就保留默认值), 我们什么都不用做, 因为它已经使用了同等incompatibleImprovements属性值的DefaultObjectWrapper单例。
- 另外也可以在Configuration中独立设置incompatibleImprovements。基于如何创建/设置ObjectWrapper, 可以通过这样完成(假设想要incompatibleImprovements 2.3.22):
- 如果使用了构建器API:
... = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_22).build()
- 或者使用构造方法:
... = new DefaultObjectWrapper(Configuration.VERSION_2_3_22)
- 或者使用 object_wrapper 属性 (*.properties 文件或 java.util.Properties 对象):
object_wrapper=DefaultObjectWrapper(2.3.21)
- 或者通过 FreemarkerServlet 配置 object_wrapper 和在 web.xml 中的 init-param 属性来配置:
<init-param>
<param-name>object_wrapper</param-name>
<param-value>DefaultObjectWrapper(2.3.21)</param-value>
</init-param>