FreeMarker配置(Configuration)
p> 基礎(chǔ)
Configuration 是一個(gè)存放應(yīng)用級(jí)別(application level)公共配置信息,以及模版(Template)可使用的全局共享變量的一個(gè)對(duì)象。同時(shí)它還負(fù)責(zé)模版(Template)實(shí)例的創(chuàng)建以及緩存。Configuration 實(shí)際上是freemarker.template.Configuration 對(duì)象的實(shí)例,使用其構(gòu)造函數(shù)創(chuàng)建。通常應(yīng)用使用一個(gè)共享的單實(shí)例Configuration 對(duì)象。
Configuration 對(duì)象可被Template 對(duì)象的方法使用,每一個(gè)模版實(shí)例都關(guān)聯(lián)與一個(gè)Configuration 實(shí)例,它是通過(guò)Template 的構(gòu)造函數(shù)被關(guān)聯(lián)進(jìn)去的,通常是你使用這個(gè)方法來(lái)Configuration.getTemplate 獲得模版對(duì)象的。
共享變量
共享變量是那些定義給所有模版(Template)使用的變量。你可以通過(guò)configuration對(duì)象的setSharedVariable 方法來(lái)添加共享變量。
Configuration cfg = new Configuration(); ... cfg.setSharedVariable("wrap", new WrapDirective()); cfg.setSharedVariable("company", "Foo Inc."); // Using ObjectWrapper.DEFAULT_WRAPPER
所有與該configuration 對(duì)象關(guān)聯(lián)的模版實(shí)例都就可以通過(guò)獲得to_upper 轉(zhuǎn)換器,company 來(lái)獲得字符串,因此你不需要再一次次的往root 中添加這些變量了。如果你往root 添加同名的變量,那么你新添加的變量將會(huì)覆蓋之前的共享變量。
警告!
如果configuration 對(duì)象被多線程調(diào)用,那么不要使用TemplateModel 實(shí)現(xiàn)類作為共享變量,因?yàn)樗麄兪欠蔷€程安全的,例如基于servlet 的web 站點(diǎn)就是這種情況。
Configuration 對(duì)象初始化時(shí)已經(jīng)包含一些共享轉(zhuǎn)換器變量:
名字類
配置參數(shù)
配置參數(shù)是那些可以影響FreeMarker 運(yùn)行行為的那些命名參數(shù)。例如locale,number_format。
配置參數(shù)存儲(chǔ)在Configuration實(shí)例中,它可以被模版實(shí)例(Template)修改。例如,你在Configuration中設(shè)置了locale等于"en_US",那么所有的模版對(duì)象都會(huì)使用,"en_US"除非你在單個(gè)模版實(shí)例中利用setLocale方法修改了默認(rèn)配置。因此configuration設(shè)置的參數(shù)可以當(dāng)作是默認(rèn)參數(shù),它可以被Template一級(jí)設(shè)置的參數(shù)覆蓋,而它們兩者設(shè)置的參數(shù)信息又可以被環(huán)境中設(shè)置的參數(shù)所覆蓋(也就是模版文件指令設(shè)置的)如下:
${1.2}<#setting locale="en_US">${1.2}
這種調(diào)用方式你可以想象成3 個(gè)層(配置對(duì)象層,模版層,運(yùn)行環(huán)境層)下面表格中顯示了每一層對(duì)于參數(shù)的設(shè)置:
那么配置參數(shù)的最終結(jié)果分別是:A = 1, B = 2, C = 3, D = 1, E = 2.而F 參數(shù)很可能就是null。
如果要查詢可設(shè)置的參數(shù)列表,你可以查閱FreeMarker API 文檔的以下兩個(gè)部分:
所有層的配置
freemarker.core.Configurable.setSetting(String, String)
Coniguration 層的配置
freemarker.template.Configuration.setSetting(String,String)
加載模板
模版加載器
模版加載器是基于抽象路徑("index.ftl"或"products/catalog.ftl")加載原始數(shù)據(jù)的那些對(duì)象,而究竟加載何種資源(目錄中的文件數(shù)據(jù)還是數(shù)據(jù)庫(kù)中的數(shù)據(jù))取決于具體的加載器實(shí)現(xiàn)。當(dāng)你調(diào)用cfg.getTemplate 時(shí),F(xiàn)reeMarker 將會(huì)詢問(wèn)你之前配置給Configuration 對(duì)象的模版加載器,有該模版加載器負(fù)責(zé)文件的載入。
內(nèi)建的模版加載器
你可以用以下三個(gè)方法來(lái)設(shè)置模版加載的三種方式
void setDirectoryForTemplateLoading(File dir);
或者
void setClassForTemplateLoading(Class cl, String prefix);
或者
void setServletContextForTemplateLoading(Object servletContext, String path);
以上第一種方式顯示的指定了一個(gè)文件系統(tǒng)中的目錄,F(xiàn)reeMarker 將會(huì)在此目錄記載模版,不用說(shuō),此目錄必須存在,否在會(huì)拋出異常。
第二種方式以一個(gè)Class作為一個(gè)輸入?yún)?shù),當(dāng)你想使用ClassLoader的方式來(lái)加載模版的時(shí)候,你就可以使用這種方式,這種方式將會(huì)調(diào)用來(lái)尋找模版文件,同時(shí)這種模版加載的方式要比前一種穩(wěn)定一些尤其是在生產(chǎn)系統(tǒng)中。你可以很容易的把資源文件,以及圖標(biāo)等打包到.jar 文件中。
第三種方式把web 應(yīng)用的上下文以及基路徑(相對(duì)與WEN-INF 的父路進(jìn)來(lái)說(shuō))作為參數(shù)。該種方式的模版加載器將會(huì)從web 應(yīng)用上下文種加載模版。
從多個(gè)位置加載模版
如果你想從多個(gè)位置加載模版的話,你可以分別創(chuàng)建與不同位置對(duì)應(yīng)的單個(gè)模版加載器,然后把它們包裹到一個(gè)名叫MultiTemplateLoader模版加載器中,最終通過(guò)方法setTemplateLoader(TemplateLoader loader)把其設(shè)置給Configuration 對(duì)象,以下有一個(gè)從兩個(gè)不同位置加載模版的例子:
import freemarker.cache.*; // template loaders live in this package ... 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);
FreeMarker 將會(huì)首先在路徑/tmp/templates中搜索模版文件,如果沒(méi)有找到那么回到路徑/usr/data/templates中搜索,如果還沒(méi)有找到,那么則會(huì)嘗試用class-loader的方式加載。
從其他資源中獲取模版文件
如果在這些內(nèi)建的模版加載器中沒(méi)有一個(gè)符合你的要求,那么你可以自己定制一個(gè)模版加載器,只需要實(shí)現(xiàn)freemarker.cache.TemplateLoader 接口就可以了,然后通過(guò)方法setTemplateLoader(TemplateLoader loader)把其傳遞給Configuration對(duì)象。
緩存模版
FreeMarker緩存模版的意思就是,當(dāng)你通過(guò)getTemplate方法獲取一個(gè)模版的時(shí)候,F(xiàn)reeMarker不僅會(huì)返回一個(gè)Template對(duì)象,而且會(huì)緩存該對(duì)象,當(dāng)你下一次以相同的路徑請(qǐng)求模版的時(shí)候,它就會(huì)返回緩存中的模版對(duì)象。如果你改變了模版文件,那么當(dāng)你下一次獲取模版的時(shí)候,F(xiàn)reeMarker會(huì)自動(dòng)重新加載,重新解析模版。雖然如此,但是如果直接判斷一個(gè)文件是否修改過(guò)是一個(gè)耗時(shí)的操作,那么FreeMarker 在Configuration 對(duì)象級(jí)別提供了一個(gè)配置參數(shù)“update delay”。該參數(shù)的意思是FreeMarker多長(zhǎng)時(shí)間去判斷一次模版的版本,默認(rèn)設(shè)置是5秒鐘,也就是每個(gè)5秒就會(huì)判斷模版是否經(jīng)過(guò)修改,如果你想實(shí)時(shí)的判斷,那么設(shè)置該參數(shù)為0。另外一點(diǎn)需要注意,并不是所有的加載器都支持這種判斷方式,舉例來(lái)說(shuō)基于class-loader 的模版加載器就不會(huì)發(fā)現(xiàn)你修改過(guò)模版文件。
對(duì)于刪除緩存中的模版FreeMarker 是這么做的,你可以使用Configuration 對(duì)象方法clearTemplateCache 以手工的方式清楚緩存中的模版對(duì)象。而實(shí)際上緩存部分可以作為一個(gè)組建加入到FreeMarker 中(也就是它可以使用第三方緩存方案)你可以通過(guò)設(shè)置cache_storage 這個(gè)參數(shù)來(lái)實(shí)現(xiàn)。對(duì)大多數(shù)開發(fā)者來(lái)FreeMarker 自帶的freemarker.cache.MruCacheStorage 實(shí)現(xiàn)已經(jīng)足夠了。這個(gè)緩存使用2 個(gè)級(jí)別的Most Recently Used(最近最多用)策略。在第一個(gè)級(jí)別,所有的緩存條目都是使用強(qiáng)引用(strongly referenced:條目并不會(huì)被JVM 所清楚,與其相對(duì)的弱引用softly reference)直到達(dá)到最大時(shí)間,那些最近最少使用的條目就會(huì)被遷移到二級(jí)緩存。在這個(gè)級(jí)別條目都是使用弱引用直到達(dá)到過(guò)期。若引用與強(qiáng)引用的區(qū)域的大小是可以在構(gòu)造函數(shù)中設(shè)置的,例如你想把強(qiáng)引用區(qū)域設(shè)置為20,弱引用區(qū)域設(shè)置為250,那你可以使用以下代碼:
cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))
由于MruCacheStorage 是默認(rèn)的緩存實(shí)現(xiàn),那么你也可以這樣設(shè)置:
cfg.setSetting(Configuration.CACHE_STORAGE_KEY,"strong:20, soft:250");
當(dāng)你創(chuàng)建一個(gè)新的Configuration時(shí),其默認(rèn)使用MruCacheStorage緩存實(shí)現(xiàn)且默認(rèn)的值maxStrongSize等于0,maxSoftSize等于Integer.MAX_VALUE(也就是理論最大值)。但是對(duì)于高負(fù)荷的系統(tǒng)來(lái)說(shuō),我們建議maxStrongSize 設(shè)置成一個(gè)非0 的數(shù)值,不然會(huì)導(dǎo)致頻繁的重新加載,重新解析模版。
異常處理
可能產(chǎn)生的異常
FreeMarker 產(chǎn)生的異常一般可歸以下幾類:
FreeMarker 初始化階段產(chǎn)生的異常: 通常在你的應(yīng)用中僅需要初始化FreeMarker 一次,而當(dāng)在這個(gè)時(shí)間段類產(chǎn)生的異常就叫做初始化異常。
加載解析模版期的異常:當(dāng)你通過(guò)Configuration.getTemplate()方法獲取模版的時(shí)候(如果模版之前沒(méi)有被緩存),將會(huì)產(chǎn)生兩類異常:
IOException:由于模版沒(méi)有找到,或在讀取模版的時(shí)候發(fā)生其他的IO異常,比如你沒(méi)有讀取該文件的權(quán)限等等; freemarker.core.ParseException 由于模版文件的語(yǔ)法使用不正確;
執(zhí)行期間的異常:當(dāng)你調(diào)用Template.process(...)方法的時(shí)候,會(huì)拋出兩類異常:
IOException 往輸出寫數(shù)據(jù)時(shí)候發(fā)生的錯(cuò)誤; freemarker.template.TemplatException其他運(yùn)行期產(chǎn)生的異常,比如一個(gè)最常見的錯(cuò)誤就是模版引用了一個(gè)不存在的變量;
- spring boot 集成 shiro 自定義密碼驗(yàn)證 自定義freemarker標(biāo)簽根據(jù)權(quán)限渲染不同頁(yè)面(推薦
- spring boot里增加表單驗(yàn)證hibernate-validator并在freemarker模板里顯示錯(cuò)誤信息(推薦)
- Spring Boot使用模板freemarker的示例代碼
- 詳解MyEclipse中搭建spring-boot+mybatis+freemarker框架
- Java實(shí)現(xiàn)用Freemarker完美導(dǎo)出word文檔(帶圖片)
- springboot整合freemarker詳解
- springmvc整合freemarker配置的詳細(xì)步驟
- java、freemarker保留兩位小數(shù)
- Java用freemarker導(dǎo)出word實(shí)用示例
- SpringBoot整合freemarker的講解
相關(guān)文章
spring aop execution表達(dá)式的用法
這篇文章主要介紹了spring aop execution表達(dá)式的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù)
這篇文章主要介紹了Java兩種方法計(jì)算出階乘尾部連續(xù)0的個(gè)數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03一文詳解Springboot中filter的原理與注冊(cè)
這篇文章主要為大家詳細(xì)介紹了Springboot中filter的原理與注冊(cè)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們掌握SpringBoot有一定的幫助,需要的可以參考一下2023-02-02SpringBoot使用PageHelper插件實(shí)現(xiàn)Mybatis分頁(yè)效果
這篇文章主要介紹了SpringBoot使用PageHelper插件實(shí)現(xiàn)Mybatis分頁(yè)效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-02-02基于java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng)
這篇文章主要介紹了基于java實(shí)現(xiàn)簡(jiǎn)單的銀行管理系統(tǒng),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01Spring Cloud Alibaba整合Sentinel的實(shí)現(xiàn)步驟
這篇文章主要介紹了Spring Cloud Alibaba整合Sentinel的實(shí)現(xiàn)步驟,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10