先来讲讲用Freemarker的好处和jsp的坏处,Freemarker是一个模板引擎框架,众所周知的MVC真理:结构+数据=文档(又译作template+data=output),Freemarker可以将数据填入结构中,生成最终的文档,并且在结构处理的过程中,它提供了强大了宏控制,无论是判断还是循环,亦或宏定义甚至递归,都可以实现;而数据这一块就需要我们自己通过DAO来实现了,当模板和数据都准备就绪后,Freemarker的一个process方法就帮我们将模板和数据融合成最终文档了。而在用JSP时,结构和数据是混杂在一起的(即使用了Struts,引入了struts标签,其臃肿程度一样很高),怎一个混乱了得。使用Freemarker时,我们需要准备一个后缀为ftl的模板,这个模板中定义好文档结构,然后使用设计好DAO层及其实现类,完成数据操作(我们这里使用Hibernate,至于原生态的JDBC或者myBatis那就是看各个项目的需求了)。对于我们的jPress来说,ftl就是我们上一章设计的html,去掉所有的文本和数据就变成ftl了,然后在原来有文本的地方用${}来引用数据即可。当然实际开发过程中会碰到各种基于数据的判断和循环,甚至更高级的递归调用,还有不同类型的数据访问,比如数组,MAP类型的访问等等,这些问题在freemarker文档中都有描述,可以边开发,边用,边学。
在pom.xml中加了struts2的依赖以后,freemarker被默认添加进环境了,所以无需手工引入。下面我把上一讲中设计的HTML做成ftl贴出来给大家参考,为了最大限度的复用,我设计了两个文件,一个是index.ftl,另一个是blog.ftl,前者是首页的模板,后者是定义了很多模板宏,供首页和其他页面调用,代码如下:
<#assign s=JspTaglibs["/WEB-INF/views/common/struts-tags.tld"] /> <#import "blog.ftl" as blog/> <@blog.blogHeader/> <!--主面板--> <div class="grid_9"> <#list entryPage.items as entry> <@blog.blogEntry entry /> </#list> </div> <!--侧边栏--> <div class="grid_3"> <@blog.blogSiderbar/> </div> <@blog.blogPagination entryPage.pages[0] , entryPage.pages[entryPage.pages?size-1] , entryPage.currentPage , entryPage.totalPage /> </div> <@blog.blogFooter /> </body> <script type="text/javascript"> document.getElementById("searchInput").value="<@s.text name="button.search"/>"; document.getElementById("searchInput").οnfοcus=function(){if(this.value=="<@s.text name="button.search"/>")this.value="";}; document.getElementById("searchInput").οnblur=function(){if(this.value=="")this.value="<@s.text name="button.search"/>";}; </script> </html>
<#assign s=JspTaglibs["/WEB-INF/views/common/struts-tags.tld"] /> <#--The blog's header--> <#macro blogHeader> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="description" content="${siteConfig.siteAbout}"/> <meta name="keywords" content="${siteConfig.siteKeyWords}"/> <title>${siteConfig.siteTitle}</title> <link href="themes/admired/css/960_12_col.css" rel="stylesheet" type="text/css" /> <link href="themes/admired/css/reset.css" rel="stylesheet" type="text/css" /> <link href="themes/admired/css/common.css" rel="stylesheet" type="text/css" /> <style> </style> </head> <body> <div id="header"> <div class="textShadow" id="header_top"> <!--标题--> <h1 id="site-title"><a href="#">${siteConfig.siteTitle}</a></h1> <!--副标题--> <h2 id="site-description">${siteConfig.subTitle}</h2> </div> </div> <div class="container_12" id="main"> <!--社交功能区--> <ul id="admired-social"> <#if themeConfig.social_qq??><li id="admired-qq"><a href="http://wpa.qq.com/msgrd?v=3&uin=${themeConfig.social_qq}&site=qq&menu=yes" title="给我发消息"></a></li></#if> <#if themeConfig.social_renren??><li id="admired-renren"><a target="_blank" href="http://www.renren.com/${themeConfig.social_renren}" title="人人"></a></li></#if> </ul><div class="clear"></div> <!--导航--> <ul id="nav-bar"> <li class="now-page" id="nav-homePage"><a href="#"><@s.text name="label.home"/></a></li> <li><a href="#">About</a></li> <form id="searchForm" action="search" method="get"> <label for="s"><@s.text name="button.search"/></label> <input id="searchInput" type="text" name="s" value="<@s.text name="button.search"/>"/> <input id="searchSubmit" type="submit"/> </form> </ul> </#macro> <#--The blog's footer--> <#macro blogFooter> <div id="footer"> <div class="container_12"> <a id="scrollTop" href="#header"></a> <div id="site-info" class="textShadow">${siteConfig.copyRight}</div> </div> </div> </#macro> <#--The pagination--> <#macro blogPagination beginPage endPage currentPage totalPage> <div id="pagination" class="grid_12"> <span>Page ${currentPage} of ${totalPage}</span> <#list beginPage..endPage as p> <#if p=currentPage> <span class="current-page">${p}</span> <#else> <a href="#">${p}</a> </#if> </#list> </div> </#macro> <#--The Entry--> <#macro blogEntry entry> <!--文章--> <div class="entry boxShadow"> <!--文章标题栏--> <div class="entry-header"> <!--日历--> <div class="entry-calendar textShadow"> <p>${entry.createDt?string("MMM")}</p> <p>${entry.createDt?string("dd")}</p> </div> <!--标题--> <h1 class="entry-title"><a href="#">${entry.title}</a></h1> <!--标题栏其他信息--> <div class="entry-meta"> <p>Posted on <a href="#">${entry.createDt}</a></p> <p class="entry-author">By <a href="#">${entry.author.nickname}</a></p> </div> </div> <!--内容--> <div class="entry-content"> ${entry.content}<a href="#" class="more-link">Continue reading <span class="meta-nav">→</span></a></p> </div> <!--文章底部--> <div class="entry-footer"> <!--底部信息--> <div class="entry-meta"> <#if entry.categories?has_content><p>Posted in <#list entry.categories as category> <a href="#" title="View all posts in ${category.name}">${category.name}</a> </#list> | </p></#if> <#if entry.tags?has_content><p>Tagged with <#list entry.tags as tag> <a href="#" title="View all posts tagged with ${tag.name}">${tag.name}</a> </#list> | </p></#if> <a href="#" title="Comment on THIS TITLE">Leave a replay </a><p>${entry.views} views</p> </div> <!--编辑,如果有的话--> <a class="entry-edit" href="#">Edit</a> <div class="clear"></div> </div> </div> </#macro> <#--SiderBar--> <#macro blogSiderbar> <div class="widget" id="tags-widget"> <h3 class="widget-title">Tags</h3> <div class="boxShadow tags"> <#list tags as tag> <a href="#" style="font-size:${tag.fontSize}px;">${tag.name}</a> </#list> </div> </div> <div class="widget" id="category-widget"> <h3 class="widget-title">Category</h3> <div class="boxShadow"> <@showCate cates/> </div> </div> </#macro> <#macro showCate icates> <#if icates?has_content> <ul> <#list icates as cate> <li><a href="#" title="${cate.description}">${cate.name}</a>(${cate.count}) <@showCate cate.subCategories/> </li> </#list> </ul> </#if> </#macro>设计好模板后,就可以让struts来用模板建立返回页面了,方法是在struts配置文件中,为action配上freemarker的返回类型,配置示例如下:
<action name="index" class="com.flyding.jpress.actions.IndexAction"> <result type="freemarker">/WEB-INF/views/admired/index.ftl</result> </action>这一章主要介绍了freemarker及其在struts中的使用,本博的目的是带领大家认知J2EE领域广泛使用的一些技术,并带领大家一步一步熟悉J2EE一个项目开发的流程,真正深入研究某项技术的话还需要感兴趣的朋友花点时间阅读DOC和DEMO,今后jPress还将陆续用到Spring Security和Lucence,不管这些技术在jPress这一牛刀小试的项目中作用有多微弱,毕竟能够给新人带来参考和入门DEMO,这就是我想做的。