022_配置configuration
1. 配置(configuration)就是freemarker.template.Configuration對象, 它存儲了常用(全局, 應(yīng)用程序級)的設(shè)置, 定義了想要在所有模板中可用的變量(稱為共享變量)。而且, 它會處理Template實例的新建和緩存。
2. 運行中的模板會受配置設(shè)置的影響, 每個Template實例都有和它相關(guān)聯(lián)的Configuration實例。通常可以使用Configuration.getTemplate(而不是直接調(diào)用Template的構(gòu)造方法)來獲得Template實例, 此時, 關(guān)聯(lián)的Configuration實例就是調(diào)用getTemplate方法的。
3. 共享變量
3.1. Shared variables(共享變量)是為所有模板定義的變量。可以使用setSharedVariable方法向配置中添加共享變量:
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22); ... cfg.setSharedVariable("warp", new WarpDirective()); cfg.setSharedVariable("company", "Foo Inc.");3.2. 在所有使用這個配置的模板中, 名為wrap的用戶自定義指令和一個名為company的字符串將會在數(shù)據(jù)模型的根root上可見, 那就不用在根哈希表上一次又一次的添加它們。在傳遞給Template.process的根root對象里的變量將會隱藏同名的共享變量。
3.3. 出于向后兼容的特性, 共享變量的集合初始化時(就是對于新的Configuration實例來說)不能為空。它包含下列用戶自定義指令(用戶自定義指令使用時需要用@來代替#):
4. 配置設(shè)置
4.1. Settings(配置設(shè)置)是影響FreeMarker行為的已被命名的值。配置設(shè)置有很多, 例如: locale, number_format, default_encoding, template_exception_handler等。
4.2. 配置設(shè)置存儲在Configuration實例中, 可以在Template實例中被覆蓋。比如: 在配置中給locale設(shè)置為"en_US", 那么使用該配置的所有模板中的locale都使用"en_US", ?除非在模板中l(wèi)ocale被明確地設(shè)置成其它不同的值(比如: "zh_CN")。因此, 在Configuration中的值充當默認值, 這些值在每個模板中也可以被覆蓋。
4.3. 在Configuration或Template實例中的值也可以在單獨調(diào)用Template.process方法后被覆蓋。對于每個調(diào)用了freemarker.core.Environment對象的值在內(nèi)部創(chuàng)建時就持有模板執(zhí)行的運行時環(huán)境, 也包括了那個級別的設(shè)置信息。在模板執(zhí)行時, 那里存儲的值也可以被改變。
4.4. 配置信息可以被想象成3層(Configuration, Template, Environment), 最高層包含特定的值, 它為設(shè)置信息提供最有效的值。比如(設(shè)置信息A到F僅僅是為這個示例而構(gòu)想的):
4.4.1. 配置信息的有效值為: A=1, B=2, C=3, D=1, E=2。而F的設(shè)置則是null, 或者在你獲取它的時候?qū)伋霎惓!?/p>
4.5. Configuration層設(shè)置配置信息
4.5.1. Configuration層原則上設(shè)置配置信息時使用Configuration對象的setter方法, 例如:
Configuration myCfg = new Configuration(Configuration.VERSION_2_3_23); myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); myCfg.setDefaultEncoding("UTF-8");4.5.2. 在真正使用Configuration對象(通常在初始化應(yīng)用程序時)之前來配置它, 后面必須將其視為只讀的對象。
4.5.3. 在實踐中, 比如很多Web應(yīng)用框架中, 就應(yīng)該使用這種框架特定的配置方式來進行配置, 比如使用成對的String來配置(像在 .properties屬性配置文件中那樣)。在這種情況下, 框架的作者大多數(shù)使用Configuration對象的setSetting(String name, String value)方法。
4.5.4. 而在Spring框架中, 我們可以這樣進行:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"><property name="freemarkerSettings"><props><prop key="incompatible_improvements">2.3.23</prop><prop key="template_exception_handler">rethrow</prop><prop key="default_encoding">UTF-8</prop></props></property> </bean>4.6. Template層設(shè)置配置信息, 應(yīng)該使用Configuration.getTemplate(...)獲取模板, 然后進行配置設(shè)置。
4.7. Environment層設(shè)置配置信息
4.7.1. 使用Java API: 使用Environment對象的setter方法。當然想要在模板執(zhí)行之前來做, 然后當調(diào)用myTemplate.process(...)時會遇到問題, 因為在內(nèi)部創(chuàng)建Environment對象后立即就執(zhí)行模板了, 導致沒有機會來進行設(shè)置。這個問題的解決可以用下面兩個步驟進行:
Environment env = myTemplate.createProcessingEnvironment(root, out); env.setLocale(java.util.Locale.ITALY); env.setNumberFormat("0.####"); env.process();4.7.2. 在模板中(通常這被認為是不好的做法)直接使用setting指令, 例如:
<#setting locale="it_IT"> <#setting number_format="0.####">5. 模板加載器
5.1. 模板加載器是加載基于抽象模板路徑下, 比如: "index.ftl"或"products/catalog.ftl"的原生文本數(shù)據(jù)對象。
5.2. 當調(diào)用cfg.getTemplate(這里的cfg就是Configuration實例)時, FreeMarker詢問模板加載器是否已經(jīng)為cfg建立返回給定模板路徑的文本, 之后FreeMarker解析文本生成模板。
5.3. 內(nèi)建模板加載器
5.3.1. 在Configuration中可以使用下面的方法來方便建立三種模板加載。(每種方法都會在其內(nèi)部新建一個模板加載器對象, 然后創(chuàng)建Configuration實例來使用它。)
void setDirectoryForTemplateLoading(File dir); 或 void setClassForTemplateLoading(Class cl, String prefix); 或 void setServletContextForTemplateLoading(Object servletContext, String path);5.3.2. 模板加載器接口
5.3.3. 第一種方法在磁盤的文件系統(tǒng)上設(shè)置了一個明確的目錄, 它確定了從哪里加載模板。不要說可能, File參數(shù)肯定是一個存在的目錄。否則, 將會拋出異常。
5.3.4. 第二種調(diào)用方法使用了一個Class類型的參數(shù)和一個前綴。這是讓你來指定什么時候通過相同的機制來加載模板, 不過是用Java的ClassLoader來加載類。這就意味著傳入的class參數(shù)會被Class.getResource()用來調(diào)用方法來找到模板。參數(shù)prefix是給模板的名稱來加前綴的。
5.3.5. 第三種調(diào)用方式需要Web應(yīng)用的上下文和一個基路徑作為參數(shù), 這個基路徑是Web應(yīng)用根路徑(WEB-INF目錄的上級目錄)的相對路徑。那么加載器將會從Web應(yīng)用目錄開始加載模板。
5.4. 從多個位置加載模板
5.4.1. 如果需要從多個位置加載模板, 那就不得不為每個位置都實例化模板加載器對象, 將它們包裝到一個稱為MultiTemplateLoader的特殊模板加載器, 最終將這個加載器傳遞給Configuration對象的setTemplateLoader(TemplateLoader loader)方法。下面給出一個使用類加載器從兩個不同位置加載模板的示例:
FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates")); FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates")); ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), ""); TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2, ctl }; MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);cfg.setTemplateLoader(mtl);5.4.2. 現(xiàn)在, FreeMarker將會嘗試從/tmp/templates目錄加載模板, 如果在這個目錄下沒有發(fā)現(xiàn)請求的模板, 它就會繼續(xù)嘗試從/usr/data/templates目錄下加載, 如果還是沒有發(fā)現(xiàn)請求的模板, 那么它就會使用類加載器來加載模板。
5.4.3. 多位置模板加載器
5.5. 從其他資源加載模板
5.5.1. 如果內(nèi)建的類加載器都不適合使用, 那么就需要來編寫自己的類加載器了, 這個類需要實現(xiàn)freemarker.cache.TemplateLoader接口, 然后將它傳遞給Configuration對象的setTemplateLoader(TemplateLoader loader)方法。
5.5.2. 如果模板需要通過URL訪問其他模板, 那么就不需要實現(xiàn)TemplateLoader接口了, 可以選擇子接口freemarker.cache.URLTemplateLoader來替代, 只需實現(xiàn)URL getURL(String templateName)方法即可。
5.6. 模板緩存
5.6.1. FreeMarker是會緩存模板的(假設(shè)使用Configuration對象的方法來創(chuàng)建Template對象)。這就是說當調(diào)用getTemplate方法時, FreeMarker不但返回了Template對象, 而且還會將它存儲在緩存中, 當下一次再以相同(或相等)路徑調(diào)用getTemplate方法時, 那么它只返回緩存的Template實例, 而不會再次加載和解析模板文件了。
5.6.2. 如果更改了模板文件, 當下次調(diào)用模板時, FreeMarker將會自動重新載入和解析模板。然而, 要檢查模板文件是否改變內(nèi)容了是需要時間的, 有一個Configuration級別的設(shè)置被稱作"更新延遲", 它可以用來配置這個時間。這個時間就是從上次對某個模板檢查更新后, FreeMarker再次檢查模板所要間隔的時間。其默認值是5秒。如果想要看到模板立即更新的效果, 那么就要把它設(shè)置為0。要注意某些模板加載器也許在模板更新時可能會有問題。例如, 典型的例子就是在基于類加載器的模板加載器就不會注意到模板文件內(nèi)容的改變。
5.6.3. 如果Java虛擬機認為會有內(nèi)存溢出時, 默認情況它會從緩存中移除任意模板。此外, 你還可以使用Configuration對象的clearTemplateCache方法手動清空緩存。
5.6.4. 何時將一個被緩存了的模板清除的實際應(yīng)用策略是由配置的屬性cache_storage來確定的, 通過這個屬性可以配置任何CacheStorage的實現(xiàn)。對于大多數(shù)用戶來說, 使用freemarker.cache.MruCacheStorage就足夠了。這個緩存存儲實現(xiàn)了二級最近使用的緩存。在第一級緩存中, 組件都被強烈引用到特定的最大數(shù)目(引用次數(shù)最多的組件不會被Java虛擬機拋棄, 而引用次數(shù)很少的組件則相反)。當超過最大數(shù)量時, 最近最少使用的組件將被送至二級緩存中, 在那里它們被很少引用, ?直到達到另一個最大的數(shù)目。引用強度的大小可以由構(gòu)造方法來指定。例如, 設(shè)置強烈部分為20, 輕微部分為250:
cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250)) 或者 cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");5.6.5. 當創(chuàng)建了一個新的Configuration對象時, 它使用一個strongSizeLimit值為0的MruCacheStorage緩存來初始化, softSizeLimit的值是Integer.MAX_VALUE(也就是在實際中, 是無限大的)。但是使用非0的strongSizeLimit對于高負載的服務(wù)器來說也許是一個更好的策略, 對于少量引用的組件來說, 如果資源消耗已經(jīng)很高的話, Java虛擬機往往會引發(fā)更高的資源消耗, 因為它不斷從緩存中拋出經(jīng)常使用的模板, 這些模板還不得不再次加載和解析。
6. 錯誤控制
6.1. 關(guān)于FreeMarker可能發(fā)生的異常, 可以分為如下幾類:
6.1.1. 當配置 FreeMarker 時發(fā)生異常。
6.1.2. 當加載和解析模板時發(fā)生異常: 調(diào)用了Configuration.getTemplate(...)方法, FreeMarker就要把模板文件加載到內(nèi)存中然后來解析它。在這期間, 有兩種異常可能發(fā)生:
- 因模板文件沒有找到而發(fā)生的IOException異常, 或在讀取文件時發(fā)生其他的I/O問題。比如沒有讀取文件的權(quán)限, 或者是磁盤錯誤。這些錯誤的發(fā)出者是TemplateLoader對象。
- 根據(jù)FTL語言的規(guī)則, 模板文件發(fā)生語法錯誤時會導致freemarker.core.ParseException異常。當獲得Template對象(Configuration.getTemplate(...))時, 這種錯誤就會發(fā)生, 而不是當執(zhí)行(Template.process(...))模板的時候。這種異常是IOException的一個子類。
6.1.3. 當執(zhí)行(處理)模板時發(fā)生的異常, 也就是當調(diào)用了Template.process(...)方法時會發(fā)生的兩種異常:
- 當試圖寫入輸出對象時發(fā)生錯誤而導致的IOException異常。
- 當執(zhí)行模板時發(fā)生的其它問題而導致的freemarker.template.TemplatException異常。比如: 一個頻繁發(fā)生的錯誤, 就是當模板引用一個不存在的變量。默認情況下, 當TemplatException異常發(fā)生時, FreeMarker會用普通文本格式在輸出中打印出FTL的錯誤信息和堆棧跟蹤信息, 然后再次拋出TemplatException異常而中止模板的執(zhí)行, 就可以捕捉到Template.process(...)方法拋出的異常了。而這種行為是可以定制的。FreeMarker也會經(jīng)常寫TemplatException異常的日志。
6.2. TemplateException異常在模板處理期間的拋出是由freemarker.template.TemplateExceptionHandler對象控制的, 這個對象可以使用setTemplateExceptionHandler(...)方法配置到Configuration對象中。
6.3. TemplateExceptionHandler對象只包含一個handleTemplateException方法和幾個預(yù)定義的異常處理方式:
6.4. 無論TemplateException異常什么時候發(fā)生, handleTemplateException這個方法都會被調(diào)用。異常處理是傳遞的te參數(shù)控制的, 模板處理的運行時(Runtime)環(huán)境可以訪問env變量, 處理器可以使用out變量來打印輸出信息。如果方法拋出異常(通常是重復拋出te), 那么模板的執(zhí)行就會中止, 而且Template.process(...)方法也會拋出同樣的異常。如果handleTemplateException對象不拋出異常, 那么模板將會繼續(xù)執(zhí)行, 就好像什么也沒有發(fā)生過一樣, 但是引發(fā)異常的語句將會被跳過。當然, 控制器仍然可以在輸出中打印錯誤提示信息。
6.5. FreeMarker本身帶有這些預(yù)先編寫的錯誤控制器:
6.5.1. TemplateExceptionHandler.DEBUG_HANDLER: 打印堆棧跟蹤信息(包括FTL錯誤信息和FTL堆棧跟蹤信息)和重新拋出的異常。這是默認的異常控制器(也就是說, 在所有新的Configuration對象中, 它是初始化好的)。
6.5.2. TemplateExceptionHandler.HTML_DEBUG_HANDLER: 和DEBUG_HANDLER相同, 但是它可以格式化堆棧跟蹤信息, 那么就可以在Web瀏覽器中來閱讀錯誤信息。當你在制作HTML頁面時, 建議使用它而不是DEBUG_HANDLER。
6.5.3. TemplateExceptionHandler.IGNORE_HANDLER: 簡單地壓制所有異常(但是要記住, FreeMarker仍然會寫日志)。它對處理異常沒有任何作用, 也不會重新拋出異常。
6.5.4. TemplateExceptionHandler.RETHROW_HANDLER: 簡單重新拋出所有異常而不會做其它的事情。這個控制器對Web應(yīng)用程序(假設(shè)你在發(fā)生異常之后不想繼續(xù)執(zhí)行模板)來說非常好, ?因為它在生成的頁面發(fā)生錯誤的情況下, 給了你很多對Web應(yīng)用程序的控制權(quán)(因為FreeMarker不向輸出中打印任何關(guān)于該錯誤的信息)。
總結(jié)
以上是生活随笔為你收集整理的022_配置configuration的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 021_程序指令
- 下一篇: 023_运行时变量和范围