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