【参考: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就从重复工做中脱身了。