【參考:http://blog.csdn.net/fangzhangsc2006/article/details/8687371】html
在採用FreeMarker作前臺視圖模板的狀況下,咱們能夠經過<#include>標籤和自定義宏來解決不少重複性工做。前端
一個簡單的FreeMarker宏:java
- <#macro sayHello name="">
- hello ${name}
- </#macro>
而後經過以下的形式調用:web
- <@sayHello name="shannon" />
不過這種在模板頁中定義的宏能力有限。【1】假設,咱們不少頁面都要輸出一個熱門排行框,而排行數據須要從controller層動態獲取,咱們能夠用這種宏來完成全部的展現工做,但前提是相應的controller和接口中層須要預先將這些排行數據放到model中去,所以對於後端來講這也是一個重複性的工做。那麼有沒有一種方式可讓後端也脫離這種重複工做呢?答案是確定的,這也是寫這篇博客的目的。spring
在一個偶然的機會發現jeecms項目中用到了這種方式,因而借鑑了一番。後端
FreeMarker不只能夠在前端的模板頁中定義宏,還能夠經過擴展其接口在後端實現宏,這有什麼好處呢?這種方式就比如讓你的模板頁具有了從前端再次回到後端的能力。這樣咱們就能很好的解決【1】處的假設,咱們無需在各個controller的各個接口中去重複的向model中添加所需的排行數據,而是當FreeMarker渲染模板頁時遇到相應的宏它能夠回到後端去調用相應的方法取到所需的數據。例子以下:app
- import freemarker.core.Environment;
- import freemarker.template.ObjectWrapper;
- import freemarker.template.TemplateDirectiveModel;
-
- public class FMAppRankDirective implements TemplateDirectiveModel {
-
- @Resource(name = "appRankService")
- private AppRankService appRankService;
-
-
- @SuppressWarnings("unchecked")
- @Override
- public void execute(Environment env, Map params, TemplateModel[] loopVars,
- TemplateDirectiveBody body) throws TemplateException, IOException {
-
-
- Integer length = DirectiveUtils.getInt("length", params);
- Integer mtypeCode = DirectiveUtils.getInt("mtypeCode", params);
- Integer typeCode = DirectiveUtils.getInt("typeCode", params);
- Integer rankMode = DirectiveUtils.getInt("rankMode", params);
- ArrayList<App> rankList = appRankService.getRankList(length, mtypeCode, typeCode, rankMode);
-
- env.setVariable("appRankList", ObjectWrapper.DEFAULT_WRAPPER.wrap(rankList));
- if (body != null) {
- body.render(env.getOut());
- }
- }
- }
經過實現FreeMarker的TemplateDirectiveModel就在後端實現了一個自定義的宏,這個宏的功能很簡單,只是根據給定的參數將排行數據「appRankList」放到model中去,而後模板頁中就可使用這個變量了。ide
FreeMarker的配置參數中須要將這個宏加入進去。工具
- <bean id="appRankDirective" class="com.shannon.example.rank.util.FMAppRankDirective" />
- <bean id="freemarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
- ……其餘配置略……
- <property name="freemarkerVariables">
- <map>
- ……其餘配置略……
- <entry key="appRankDirective" value-ref="appRankDirective"/>
- </map>
- </property>
- </bean>
在模板頁中使用:oop
- <#-- 應用下載排行框,title爲該框的標題,length爲排行列表長度,mtypeCode爲主類型代碼,typeCode爲小類型代碼,rankMode爲排行方式
- 1爲總下載量,2爲月下載量,3爲昨日增加下載量
- -->
- <#macro appRankBox title="" length=10 mtypeCode=1 typeCode=-1 rankMode=1>
- <@appRankDirective length=length mtypeCode=mtypeCode typeCode=typeCode rankMode=rankMode />
- <h3 class="box-title">${title}</h3>
- <div class="box">
- <ul class="row-list">
- <#list appRankList as item>
- ……詳細輸出內容略……
- </#list>
- </ul>
- </div>
- </#macro>
這裏我在模板頁中又定義了一個宏,負責內容及樣式的輸出,由於模板頁中的宏比較直觀,讓後端的宏只負責拿數據。其餘頁面直接使用「appRankBox」就能夠了,而後由它來調用後端的「appRankDirective」宏來拿數據。這樣,controller就從重複工做中脫身了。