-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpgui_datamodel_objectWrapper.html
348 lines (262 loc) · 23.5 KB
/
pgui_datamodel_objectWrapper.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<!doctype html>
<html lang="en" class="page-type-section">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>对象包装 - FreeMarker 手册</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="format-detection" content="telephone=no">
<meta property="og:site_name" content="FreeMarker 手册">
<meta property="og:title" content="对象包装">
<meta property="og:locale" content="en_US">
<meta property="og:url" content="http://freemarker.org/docs/pgui_datamodel_objectWrapper.html">
<link rel="canoical" href="http://freemarker.org/docs/pgui_datamodel_objectWrapper.html">
<link rel="icon" href="favicon.png" type="image/png">
<link rel="stylesheet" type="text/css" href="docgen-resources/docgen.min.css">
</head>
<body itemscope itemtype="https://schema.org/Code">
<meta itemprop="url" content="http://freemarker.org/docs/">
<meta itemprop="name" content="FreeMarker 手册">
<!--[if lte IE 9]>
<div style="background-color: #C00; color: #fff; padding: 12px 24px;">Please use a modern browser to view this website.</div>
<![endif]--><div class="header-top-bg"><div class="site-width header-top"><a class="logo" href="http://freemarker.org" role="banner"> <img itemprop="image" src="logo.png" alt="FreeMarker">
</a><ul class="tabs"><li><a href="http://freemarker.org/">Home</a></li><li class="current"><a href="index.html">Manual</a></li><li><a class="external" href="http://freemarker.org/docs/api/index.html">Java API</a></li></ul><ul class="secondary-tabs"><li><a class="tab icon-heart" href="http://freemarker.org/contribute.html" title="Contribute"><span>Contribute</span></a></li><li><a class="tab icon-bug" href="https://sourceforge.net/p/freemarker/bugs/new/" title="Report a Bug"><span>Report a Bug</span></a></li><li><a class="tab icon-download" href="http://freemarker.org/freemarkerdownload.html" title="Download"><span>Download</span></a></li></ul></div></div><div class="header-bottom-bg"><div class="site-width search-row"><a href="toc.html" class="navigation-header">Manual</a><div class="navigation-header"></div></div><div class="site-width breadcrumb-row"><ul class="breadcrumb" itemscope itemtype="http://schema.org/BreadcrumbList"><li class="step-0" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="toc.html"><span itemprop="name">FreeMarker 手册</span></a></li><li class="step-1" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui.html"><span itemprop="name">程序开发指南</span></a></li><li class="step-2" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui_datamodel.html"><span itemprop="name">数据模型</span></a></li><li class="step-3" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui_datamodel_objectWrapper.html"><span itemprop="name">对象包装</span></a></li></ul><div class="bookmarks" title="Bookmarks"><span class="sr-only">Bookmarks:</span><ul class="bookmark-list"><li><a href="alphaidx.html">Alpha. index</a></li><li><a href="gloss.html">Glossary</a></li><li><a href="dgui_template_exp.html#exp_cheatsheet">Expressions</a></li><li><a href="ref_builtins_alphaidx.html">?builtins</a></li><li><a href="ref_directive_alphaidx.html">#directives</a></li><li><a href="ref_specvar.html">.spec_vars</a></li><li><a href="app_faq.html">FAQ</a></li></ul></div></div></div> <div class="main-content site-width">
<div class="content-wrapper">
<div id="table-of-contents-wrapper" class="col-left">
<script>var breadcrumb = ["FreeMarker 手册","程序开发指南","数据模型","对象包装"];</script>
<script src="toc.js"></script>
<script src="docgen-resources/main.min.js"></script>
</div>
<div class="col-right"><div class="page-content"><div class="page-title"><div class="pagers top"><a class="paging-arrow previous" href="pgui_datamodel_node.html"><span>Previous</span></a><a class="paging-arrow next" href="pgui_config.html"><span>Next</span></a></div><div class="title-wrapper">
<h1 class="content-header header-section1" id="pgui_datamodel_objectWrapper" itemprop="headline">对象包装</h1>
</div></div><div class="page-menu">
<div class="page-menu-title">Page Contents</div>
<ul><li><a class="page-menu-link" href="#pgui_datamodel_defaultObjectWrapper" data-menu-target="pgui_datamodel_defaultObjectWrapper">默认对象包装器</a></li><li><a class="page-menu-link" href="#pgui_datamodel_customObjectWrappingExample" data-menu-target="pgui_datamodel_customObjectWrappingExample">自定义对象包装示例</a></li></ul> </div><p>对象包装器是实现了 <code class="inline-code">freemarker.template.ObjectWrapper</code>
接口的类。它的目标是实现Java对象(应用程序中特定类等,比如 <code class="inline-code">String</code>,
<code class="inline-code">Map</code>,<code class="inline-code">List</code> 实例)和FTL类型系统之间的映射。换句话说,
它指定了模板如何在数据模型(包含从模板中调用的Java方法的返回值)中发现Java对象。
对象包装器作为插件放入 <code class="inline-code">Configuration</code> 中,可以使用
<code class="inline-code">object_wrapper</code> 属性设置
(或者使用<code class="inline-code">Configuration.setObjectWrapper</code>)。</p><p>从技术角度来说,FTL类型系统由之前介绍过的 <code class="inline-code">TemplateModel</code> 子接口
(<code class="inline-code">TemplateScalarModel</code>,<code class="inline-code">TemplateHashMode</code>,
<code class="inline-code">TemplateSequenceModel</code>等)来表示。要映射Java对象到FTL类型系统中,
对象包装器的 <code class="inline-code">TemplateModel wrap(java.lang.Object obj)</code> 方法会被调用。</p><p>有时FreeMarker需要撤回映射,此时 <code class="inline-code">对象包装器ObjectWrapper</code>
的 <code class="inline-code">Object unwrap(TemplateModel)</code> 方法就被调用了
(或其他的变化,请参考API文档来获取详细内容)。最后的操作是在
<code class="inline-code">ObjectWrapperAndUnwrapper</code> 中,它是
<code class="inline-code">ObjectWrapper</code> 的子接口。很多实际的包装器会实现
<code class="inline-code">ObjectWrapperAndUnwrapper</code> 接口。</p><p>我们来看一下包装Java对象并包含其他对象
(比如 <code class="inline-code">Map</code>,<code class="inline-code">List</code>,数组,
或者有JavaBean属性的对象)是如何进行的。可以这么说,对象包装器将
<code class="inline-code">Object[]</code> 数组包装成 <code class="inline-code">TemplateSquenceModel</code>
接口的一些实现。当FreeMarker需要FTL序列中项的时候,它会调用
<code class="inline-code">TemplateSquenceModel.get(int index)</code> 方法。该方法的返回值是
<code class="inline-code">TemplateModel</code>,也就是说,<code class="inline-code">TemplateSquenceModel</code>
实现不仅仅可以从给定的数组序列获取 <code class="inline-code">对象</code>,
也可以负责在返回它之前包装该值。为了解决这个问题,典型的
<code class="inline-code">TemplateSquenceModel</code> 实现将会存储它创建的
<code class="inline-code">ObjectWrapper</code>,之后再调用该 <code class="inline-code">ObjectWrapper</code>
来包装包含的值。相同的逻辑代表了 <code class="inline-code">TemplateHashModel</code> 或其他的
<code class="inline-code">TemplateModel</code>,它是其它 <code class="inline-code">TemplateModel</code> 的容器。
因此,通常不论值的层次结构有多深,所有值都会被同一个 <code class="inline-code">ObjectWrapper</code>
包装。(要创建 <code class="inline-code">TemplateModel</code> 的实现类,请遵循这个原则,可以使用
<code class="inline-code">freemarker.template.WrappingTemplateModel</code> 作为基类。)</p><p>数据模型本身(root变量)是 <code class="inline-code">TemplateHashModel</code>。
在 <code class="inline-code">Template.process</code> 中指定的root对象将会被在
<code class="inline-code">object_wrapper</code> 配置中设置的对象包装器所包装,并产生一个
<code class="inline-code">TemplateHashModel</code>。从此,被包含值的包装遵循之前描述的逻辑
(比如,容器负责包装它的子实例)。</p><p>行为良好的对象包装器都会绕过已经实现 <code class="inline-code">TemplateModel</code>
接口的对象。如果将已经实现 <code class="inline-code">TemplateModel</code> 的对象放到数据模型中
(或者从模板中调用的Java方法返回这个对象),那么就可以避免实际的对象包装。
当特别是通过模板访问创建的值时,通常会这么做。因此,要避免更多上面对象包装的性能问题,
但也可以精确控制模板可以看到的内容(不是基于当前对象包装器的映射策略)。
常见的应用程序使用该手法是使用 <code class="inline-code">freemarker.template.SimpleHash</code>
作为数据模型的根root(而不是<code class="inline-code">Map</code>),当使用 <code class="inline-code">SimpleHash</code>
的 <code class="inline-code">put</code> 方法来填充(这点很重要,它不会复制已经填充并存在的
<code class="inline-code">Map</code>)。这会加快顶层数据模型变量的访问速度。</p>
<h2 class="content-header header-section2" id="pgui_datamodel_defaultObjectWrapper">默认对象包装器</h2>
<p><code class="inline-code">object_wrapper</code> <code class="inline-code">Configuration</code>
的默认设置是 <code class="inline-code">freemarker.template.DefaultObjectWrapper</code>
实例。除非有特别的需求,那么建议使用这个对象包装器,或者是自定义的
<code class="inline-code">DefaultObjectWrapper</code> 的子类。</p>
<p>它会识别大部分基本的Java类型,比如 <code class="inline-code">String</code>,
<code class="inline-code">Number</code>,<code class="inline-code">Boolean</code>,
<code class="inline-code">Date</code>,<code class="inline-code">List</code> (通常还有全部的
<code class="inline-code">java.util.Collection</code> 类型),
数组,<code class="inline-code">Map</code>等。并把它们自然地包装成匹配
<code class="inline-code">TemplateModel</code> 接口的对象。它也会使用
<code class="inline-code">freemarker.ext.dom.NodeModel</code> 来包装W3C DOM结点,
所以可以很方便地处理XML, <a href="xgui.html">在XML章节会有描述</a>)。
对于Jython对象,会代理到 <code class="inline-code">freemarker.ext.jython.JythonWrapper</code> 上。
而对于其它所有对象,则会调用 <code class="inline-code">BeansWrapper.wrap</code>(超类的方法),
暴露出对象的JavaBean属性作为哈希表项
(比如FTL中的 <code class="inline-code">myObj.foo</code> 会在后面调用 <code class="inline-code">getFoo()</code>),
也会暴露出对象(比如FTL中的 <code class="inline-code">myObj.bar(1, 2)</code> 就会调用方法)
的公有方法(JavaBean action)。(关于对象包装器的更多信息,请参阅
<a href="pgui_misc_beanwrapper.html">该章节</a>。)</p>
<p>关于 <code class="inline-code">DefaultObjectWrapper</code> 更多值得注意的细节:</p>
<ul>
<li>
<p>不用经常使用它的构造方法,而是使用
<code class="inline-code">DefaultObjectWrapperBuilder</code> 来创建它。
这就允许 FreeMarker 使用单例。</p>
</li>
<li>
<p><a name="topic.defaultObjectWrapperIcI"></a><code class="inline-code">DefaultObjectWrapper</code> 有
<code class="inline-code">incompatibleImprovements</code> 属性,
这在 2.3.22 或更高版本中是极力推荐的(参看该效果的 <a href="http://freemarker.org/docs/api/freemarker/template/DefaultObjectWrapper.html#DefaultObjectWrapper-freemarker.template.Version-">API文档</a>)。如何来设置:</p>
<ul>
<li>
<p>如果已经在 2.3.22 或更高版本的
<em><code class="inline-code">Configuration</code></em>
中设置了 <code class="inline-code">incompatible_improvements</code> 选项,
而没有设置 <code class="inline-code">object_wrapper</code> 选项(那么它就保留默认值),
我们就什么都做不了了,因为它已经使用了同等
<code class="inline-code">incompatibleImprovements</code> 属性值的
<code class="inline-code">DefaultObjectWrapper</code> 单例。</p>
</li>
<li>
<p><a name="topic.setDefaultObjectWrapperIcIIndividually"></a>另外也可以在 <code class="inline-code">Configuration</code> 中独立设置
<code class="inline-code">incompatibleImprovements</code>。基于如何创建/设置
<code class="inline-code">ObjectWrapper</code>,可以通过这样完成
(假设想要 <code class="inline-code">incompatibleImprovements</code> 2.3.22):</p>
<ul>
<li>
<p>如果使用了构建器API:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">... = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_22).build()</pre></div>
</li>
<li>
<p>或者使用构造方法:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">... = new DefaultObjectWrapper(Configuration.VERSION_2_3_22)</pre></div>
</li>
<li>
<p>或者使用 <code class="inline-code">object_wrapper</code> 属性
(<code class="inline-code">*.properties</code> 文件或
<code class="inline-code">java.util.Properties</code> 对象):</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">object_wrapper=DefaultObjectWrapper(2.3.21)</pre></div>
</li>
<li>
<p>或者通过 <code class="inline-code">FreemarkerServlet</code> 配置
<code class="inline-code">object_wrapper</code> 和在
<code class="inline-code">web.xml</code> 中的
<code class="inline-code">init-param</code> 属性来配置:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified"><init-param>
<param-name>object_wrapper</param-name>
<param-value>DefaultObjectWrapper(2.3.21)</param-value>
</init-param></pre></div>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>在新的或测试覆盖良好的项目中,也建议设置
<code class="inline-code">forceLegacyNonListCollections</code>
属性为 <code class="inline-code">false</code>。
如果使用 <code class="inline-code">.properties</code> 或
<code class="inline-code">FreemarkerServlet</code> 初始化参数,就会像
<code class="inline-code">DefaultObjectWrapper(2.3.22, forceLegacyNonListCollections=false)</code>,
同时,使用Java API可以在 <code class="inline-code">DefaultObjectWrapperBuilder</code> 对象调用
<code class="inline-code">build()</code> 之前调用
<code class="inline-code">setForceLegacyNonListCollections(false)</code>。</p>
</li>
<li>
<p>自定义
<code class="inline-code">DefaultObjectWrapper</code> 的最常用方法是覆盖
<code class="inline-code">handleUnknownType</code> 方法。</p>
</li>
</ul>
<h2 class="content-header header-section2" id="pgui_datamodel_customObjectWrappingExample">自定义对象包装示例</h2>
<p>我们假定有一个应用程序特定的类,像下面这样:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">package com.example.myapp;
public class Tupple<E1, E2> {
public Tupple(E1 e1, E2 e2) { ... }
public E1 getE1() { ... }
public E2 getE2() { ... }
}</pre></div>
<p>若想让模板将它视作长度为2的序列,那么就可以这么来调用
<code class="inline-code">someTupple[1]</code>,
<code class="inline-code"><#list someTupple
<em class="code-color">...</em>></code>, 或者
<code class="inline-code">someTupple?size</code>。需要创建一个
<code class="inline-code">TemplateSequenceModel</code> 实现来适配
<code class="inline-code">Tupple</code> 到
<code class="inline-code">TempateSequenceMoldel</code> 接口:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">package com.example.myapp.freemarker;
...
public class TuppleAdapter extends WrappingTemplateModel implements TemplateSequenceModel,
AdapterTemplateModel {
private final Tupple<?, ?> tupple;
public TuppleAdapter(Tupple<?, ?> tupple, ObjectWrapper ow) {
super(ow); // coming from WrappingTemplateModel
this.tupple = tupple;
}
@Override // coming from TemplateSequenceModel
public int size() throws TemplateModelException {
return 2;
}
@Override // coming from TemplateSequenceModel
public TemplateModel get(int index) throws TemplateModelException {
switch (index) {
case 0: return wrap(tupple.getE1());
case 1: return wrap(tupple.getE2());
default: return null;
}
}
@Override // coming from AdapterTemplateModel
public Object getAdaptedObject(Class hint) {
return tupple;
}
}</pre></div>
<p>关于类和接口:</p>
<ul>
<li>
<p><code class="inline-code">TemplateSequenceModel</code>:
这就是为什么模板将它视为序列</p>
</li>
<li>
<p><code class="inline-code">WrappingTemplateModel</code>:
只是一个方便使用的类,用于 <code class="inline-code">TemplateModel</code>
对象进行自身包装。通常仅对包含其它对象的对象需要。
参考上面的 <code class="inline-code">wrap(<em class="code-color">...</em>)</code> 调用。</p>
</li>
<li>
<p><code class="inline-code">AdapterTemplateModel</code>:
表明模板模型适配一个已经存在的对象到
<code class="inline-code">TemplateModel</code> 接口,
那么去掉包装就会给出原有对象。</p>
</li>
</ul>
<p>最后,我们告诉 FreeMarker 用 <code class="inline-code">TuppleAdapter</code>
(或者,可以在将它们传递到FreeMarker之前手动包装它们)
包装 <code class="inline-code">Tupple</code>。那样的话,首先创建一个自定义的对象包装器:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">package com.example.myapp.freemarker;
...
public class MyAppObjectWrapper extends DefaultObjectWrapper {
public MyAppObjectWrapper(Version incompatibleImprovements) {
super(incompatibleImprovements);
}
@Override
protected TemplateModel handleUnknownType(final Object obj) throws TemplateModelException {
if (obj instanceof Tupple) {
return new TuppleAdapter((Tupple<?, ?>) obj, this);
}
return super.handleUnknownType(obj);
}
}</pre></div>
<p>那么当配置 FreeMarker (<a href="pgui_config.html">关于配置,参考这里...</a>)
将我们的对象包装器插在:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">// Where you initialize the cfg *singleton* (happens just once in the application life-cycle):
cfg = new Configuration(Configuration.VERSION_2_3_22);
...
cfg.setObjectWrapper(new MyAppObjectWrapper(cfg.getIncompatibleImprovements()));</pre></div>
<p>或者使用 <code class="inline-code">java.util.Properties</code> 来代替配置
FreeMarker (也就是 <code class="inline-code">.properties</code> 文件):</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">object_wrapper=com.example.myapp.freemarker.MyAppObjectWrapper(2.3.22)</pre></div>
<div class="bottom-pagers-wrapper"><div class="pagers bottom"><a class="paging-arrow previous" href="pgui_datamodel_node.html"><span>Previous</span></a><a class="paging-arrow next" href="pgui_config.html"><span>Next</span></a></div></div></div></div> </div>
</div>
<div class="site-footer"><div class="site-width"><div class="footer-top"><div class="col-left sitemap"><div class="column"><h3 class="column-header">Overview</h3><ul><li><a href="http://freemarker.org/index.html">What is FreeMarker?</a></li><li><a href="http://freemarker.org/freemarkerdownload.html">Download</a></li><li><a href="app_versions.html">Version history</a></li><li><a href="http://freemarker.org/history.html">About us</a></li><li><a itemprop="license" href="app_license.html">License</a></li></ul></div><div class="column"><h3 class="column-header">Handy stuff</h3><ul><li><a href="http://freemarker-online.kenshoo.com/">Try template online</a></li><li><a href="dgui_template_exp.html#exp_cheatsheet">Expressions cheatsheet</a></li><li><a href="ref_directive_alphaidx.html">#directives</a></li><li><a href="ref_builtins_alphaidx.html">?built_ins</a></li><li><a href="ref_specvar.html">.special_vars</a></li></ul></div><div class="column"><h3 class="column-header">Community</h3><ul><li><a href="https://github.com/nanlei/freemarker/tree/manual-zh-2.3-gae/src/manual">Chinese Manual on Github</a></li><li><a href="https://github.com/freemarker/freemarker">FreeMarker on Github</a></li><li><a href="https://twitter.com/freemarker">Follow us on Twitter</a></li><li><a href="https://sourceforge.net/p/freemarker/bugs/new/">Report a bug</a></li><li><a href="http://stackoverflow.com/questions/ask?tags=freemarker">Ask a question</a></li><li><a href="http://freemarker.org/mailing-lists.html">Mailing lists</a></li></ul></div></div><div class="col-right"><ul class="social-icons"><li><a class="github" href="https://github.com/freemarker/freemarker">Github</a></li><li><a class="twitter" href="https://twitter.com/freemarker">Twitter</a></li><li><a class="stack-overflow" href="http://stackoverflow.com/questions/ask?tags=freemarker">Stack Overflow</a></li></ul><a class="xxe" href="http://www.xmlmind.com/xmleditor/" rel="nofollow" title="Edited with XMLMind XML Editor"><span>Edited with XMLMind XML Editor</span></a></div></div><div class="footer-bottom"><p><span class="generated-for-product">Generated for: Freemarker 2.3.23</span><span class="last-updated"> Last generated:
<time itemprop="dateModified" datetime="2015-09-18T14:38:51Z" title="Friday, September 18, 2015 2:38:51 PM GMT">2015-09-18 14:38:51 GMT</time></span></p> <p class="copyright">
© <span itemprop="copyrightYear">1999</span>–2015
<a itemtype="http://schema.org/Organization" itemprop="copyrightHolder" href="http://freemarker.org">The FreeMarker Project</a>. All rights reserved. </p>
</div></div></div></body>
</html>