FreeMarker的用法
freemark就是一个对静态页面上的标签进行动态解析、填充数据的一个框架。
- 语法(转:http://zhuyuehua.iteye.com/blog/1975251):
1. freemarker获取list的size :
Java
ArrayList<String> list = new ArrayList<String>();
Freemaker
${list?size}
2. list的遍历:
<#list animals as being> <tr> <td>${being.name}${being.price}<td> </tr> </#list>
3. 遍历MAP
<#list map?keys as k> <option value="${k}">${map[k]}</option> </#list>
4.list遍历中的下标序号:
_index是list的一个属性
<#list list as a> ${a_index} </#list>
5.取LIST中第i个元素的值
${list[i]}
嵌套时前面要有括号,如下,将字符串变成list,然后取第i个元素的值
${(str?split(","))[i]}
6. list的嵌套:
<#list jsskList as jsskVO> <#list kcList as kcVO> <#if kcVO.kch=jsskVO.kch> (kcVO里有编号和名称,而jsskVO里只有编号) ${kcVO.kcm} </#if> </#list> </#list>
7. list排序:
升序 .sort_by()
<#list list?sort_by("字段") as x> </#list>
降序 .sort_by()?reverse
<#list list?sort_by("字段")?reverse as x> </#list>
8.item_has_next,size使用:
<#list userList as user> <#if !user_has_next> 共有${userList?size}最后一个用户是:${user.userName} </#if> </#list>
- 实际项目中的用法:
\My-SSH-Blog\WebContent\blog\template\template.ftl
1 <!DOCTYPE HTML> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1,user-scalable=no"> 6 <title>DX博客</title> 7 <link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style.css" /> 8 <link rel="stylesheet" type="text/css" media="all" href="${contextPath}/style/css/font-awesome.min.css" /> 9 <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,400italic,300italic,300,700,700italic|Open+Sans+Condensed:300,700" rel="stylesheet" type="text/css"> 10 <!--[if IE 7]> 11 <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/font-awesome-ie7.min.css"/> 12 <![endif]--> 13 <!--[if IE 8]> 14 <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie8.css" media="all" /> 15 <![endif]--> 16 <!--[if IE 9]> 17 <link rel="stylesheet" type="text/css" href="${contextPath}/style/css/ie9.css" media="all" /> 18 <![endif]--> 19 <script type="text/javascript" src="${contextPath}/style/js/jquery-1.7.2.min.js"></script> 20 <script type="text/javascript" src="${contextPath}/style/js/ddsmoothmenu.js"></script> 21 <script type="text/javascript" src="${contextPath}/style/js/retina.js"></script> 22 <script type="text/javascript" src="${contextPath}/style/js/selectnav.js"></script> 23 <script type="text/javascript" src="${contextPath}/style/js/jquery.backstretch.min.js"></script> 24 <script type="text/javascript"> 25 $.backstretch("${contextPath}/style/images/bg/16.jpg"); 26 </script> 27 </head> 28 <body> 29 <div class="scanlines"></div> 30 31 <div class="header-wrapper opacity"> 32 <div class="header"> 33 <div class="logo"> 34 <a href="${contextPath}"> 35 <h1>DX博客</h1> 36 </a> 37 </div> 38 39 <div id="menu-wrapper"> 40 <div id="menu" class="menu"> 41 <ul id="tiny"> 42 <li class="active"> 43 <a href="${contextPath}">博客</a> 44 <ul> 45 <li><a href="${contextPath}/listArticle.action">所有博文</a></li> 46 </ul> 47 </li> 48 <li> 49 <a href="#" title="进入我的Github">Github</a> 50 </li> 51 <li> 52 <a href="#" title="进入我的CSDN博客">CSDN博客</a> 53 </li> 54 <li> 55 <a href="${contextPath}/commentUI.action" title="给我留言">留言板</a> 56 </li> 57 <li><a href="">Menu</a> 58 <ul> 59 <li><a href="/s/">短网址</a></li> 60 <li><a href="/WorkUpload/">作业提交系统</a></li> 61 </ul> 62 </li> 63 </ul> 64 </div> 65 </div> 66 <div class="clear"></div> 67 </div> 68 </div> 69 70 <div class="wrapper"><!-- 71 <ul class="social"> 72 <li><a class="rss" href="#" title="博客订阅"></a></li> 73 <li><a class="qq" href="#" title="QQ"></a></li> 74 <li><a class="weibo" href="#" title="新浪微博"></a></li> 75 <li><a class="wechat" href="${contextPath}/public/wechat.jsp" title="微信"></a></li> 76 <li><a class="email" href="#" title="邮件"></a></li> 77 <li><a class="phone" href="#" title="手机"></a></li> 78 </ul> 79 <div class="intro">Intro...</div> 80 --> 81 <div class="content box"> 82 <h1 class="title article-title">${title}</h1> 83 <div class="info"> 84 <div><a href="${listCategoryArticle}" title="查看该类型博文"><i class="icon-folder-open-alt"></i>:${categoryName}</a></div> 85 <div><a href=""><i class="icon-user"></i>:${author}</a></div> 86 <div><i class="icon-time"></i>:${time}</div> 87 </div> 88 ${typeString} 89 ${content} 90 ${typeString} 91 <div class="record"> 92 <div> 93 <a id="looked" href=""><i class="icon-eye-open"></i> ${looked} 已阅</a> 94 </div> 95 96 <div> 97 <a id="likes" href="javascript:like('${contextPath}/likeAction.action?artid=${articleId}')"><i class="icon-heart-empty"></i> ${likes} 喜爱</a> 98 </div> 99 <a class="comment-btn" href="javascript:onComment('#')"><i class="icon-comments"></i> 给我留言</a> 100 </div> 101 <div class="last-next"> 102 <div> 103 <a href="${lastArticle.staticUrl}" title="上一篇"> 104 <i class="icon-double-angle-left"></i>${lastArticle.title} 105 </a> 106 </div> 107 <div> 108 <a href="${nextArticle.staticUrl}" title="下一篇"> 109 <i class="icon-double-angle-right"></i>${nextArticle.title} 110 </a> 111 </div> 112 </div> 113 </div> 114 115 <div class="sidebar box"> 116 <div class="sidebox widget"> 117 <h3 class="widget-title">最近更新</h3> 118 <ul class="post-list"> 119 <#list lastArticlesList as la> 120 <li> 121 <div class="meta"> 122 <h5><a href="${contextPath}${la.staticUrl}.html">${la.title}</a></h5> 123 <em>${la.createDate?string("yyyy-MM-dd HH:mm:ss")}</em> 124 </div> 125 </li> 126 </#list> 127 <li class="more"><a href="${contextPath}/listArticle.action">more</a></li> 128 </ul> 129 </div> 130 131 <div class="sidebox widget"> 132 <h3 class="widget-title"><i class="icon-search icon"></i></h3> 133 <form class="searchform" method="post" action="#"> 134 <input type="text" name="key" value="输入关键字搜索博客..." onFocus="this.value=''" onBlur="this.value='输入关键字搜索博客...'"/> 135 </form> 136 </div> 137 138 <div class="sidebox widget"> 139 <h3 class="widget-title categories">分类</h3> 140 <ul class="categories"> 141 <#list categoryList as cl> 142 <li><a href="${contextPath}/listArticle.action?categoryId=${cl.id}">${cl.title}</a></li> 143 </#list> 144 </ul> 145 </div> 146 </div> 147 148 <div class="clear"></div> 149 150 </div> 151 152 <div class="footer-wrapper"> 153 <div id="footer" class="four"> 154 <a href="/s/">短网址</a> 155 <a href="/WorkUpload/">作业提交系统</a> 156 </div> 157 </div> 158 <div class="site-generator-wrapper"> 159 <div class="site-generator"> 160 Copyright ©2017.DX Design by <a href="http://elemisfreebies.com">elemis.</a> All rights reserved. 161 </div> 162 </div> 163 <script type="text/javascript" src="${contextPath}/style/js/scripts.js"></script> 164 <script type="text/javascript"> 165 function onComment(url){ 166 var form = document.createElement('form'); 167 form.action = url; 168 form.method = "post"; 169 form.style.display = "none"; 170 171 var input = document.createElement("input"); 172 input.name = "title"; 173 input.value = "${title}"; 174 form.appendChild(input); 175 176 var input2 = document.createElement("input"); 177 input2.name = "articleId"; 178 input2.value = "${articleId}"; 179 form.appendChild(input2); 180 181 document.body.appendChild(form); 182 form.submit(); 183 } 184 </script> 185 </body> 186 </html>
我访问的对应网址:http://localhost:8080/My-SSH-Blog/blog/5/5-4.html,呈现效果:
这里我使用的是一个url重写的方式实现的,因此这里博客文章页面的template.ftl的解析过程是在filter中实现的。
web.xml中定义了filter过滤规则:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 5 id="WebApp_ID" version="3.0"> 6 <display-name>My-SSH-Blog</display-name> 7 8 <welcome-file-list> 9 <welcome-file>/index.jsp</welcome-file> 10 </welcome-file-list> 11 12 <error-page> 13 <error-code>404</error-code> 14 <location>/public/404.html</location> 15 </error-page> 16 <error-page> 17 <error-code>500</error-code> 18 <location>/public/500.html</location> 19 </error-page> 20 21 <!-- html请求过滤器 --> 22 <filter> 23 <filter-name>articleFilter</filter-name> 24 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 25 <init-param> 26 <param-name>targetFilterLifecycle</param-name> 27 <param-value>true</param-value> 28 </init-param> 29 </filter> 30 <filter-mapping> 31 <filter-name>articleFilter</filter-name> 32 <url-pattern>*.html</url-pattern> 33 </filter-mapping> 34 <filter-mapping> 35 <filter-name>articleFilter</filter-name> 36 <url-pattern>*.htm</url-pattern> 37 </filter-mapping> 38 39 <context-param> 40 <param-name>contextConfigLocation</param-name> 41 <param-value>classpath:applicationContext*.xml</param-value> 42 </context-param> 43 44 <!-- 监听session,在线人员信息(登录账户,不包括访客和前台在线用户) --> 45 <listener> 46 <listener-class>com.dx.ssh.listener.SessionListener</listener-class> 47 </listener> 48 <!-- Bootstraps the root web application context before servlet initialization --> 49 <listener> 50 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 51 </listener> 52 53 <!-- Struts2 filter --> 54 <filter> 55 <filter-name>struts2</filter-name> 56 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 57 </filter> 58 59 <filter-mapping> 60 <filter-name>struts2</filter-name> 61 <url-pattern>/*</url-pattern> 62 </filter-mapping> 63 64 <session-config> 65 <session-timeout>30</session-timeout> 66 </session-config> 67 </web-app>
ArticleFilter.java
1 package com.dx.ssh.filter; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.Map; 6 import java.util.regex.Matcher; 7 import java.util.regex.Pattern; 8 9 import javax.servlet.*; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletResponse; 12 import javax.servlet.http.HttpSession; 13 14 import org.springframework.web.context.WebApplicationContext; 15 import org.springframework.web.context.support.WebApplicationContextUtils; 16 17 import com.dx.ssh.service.ArticleService; 18 import com.dx.ssh.utils.TemplateUtils; 19 import com.opensymphony.xwork2.ActionContext; 20 21 public class ArticleFilter implements Filter { 22 public static final String ARTICLE_TOKEN = "view"; 23 private ArticleService articleService; 24 25 public ArticleService getArticleService() { 26 return articleService; 27 } 28 29 public void setArticleService(ArticleService articleService) { 30 this.articleService = articleService; 31 } 32 33 // protected Object getBean(ServletContext servletContext, String id) { 34 // WebApplicationContext context = 35 // WebApplicationContextUtils.getWebApplicationContext(servletContext); 36 // return context.getBean(id); 37 // } 38 39 @Override 40 public void init(FilterConfig filterConfig) throws ServletException { 41 } 42 43 @Override 44 public void destroy() { 45 } 46 47 @Override 48 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 49 throws IOException, ServletException { 50 HttpServletRequest request = (HttpServletRequest) servletRequest; 51 HttpServletResponse response = (HttpServletResponse) servletResponse; 52 53 // ArticleService articleService = (ArticleService) 54 // this.getBean(request.getServletContext(), "articleService"); 55 56 String path = request.getRequestURL().toString(); 57 // 模式匹配 58 Pattern pattern = Pattern.compile("/blog/([0-9]+)/([0-9]+)-([0-9]+)"); 59 Matcher matcher = pattern.matcher(path); 60 61 // 不匹配,路径不正确 62 if (!matcher.find()) { 63 // 路径不对,报错404 64 response.sendError(404, "您输入路径的不存在!"); 65 return; 66 } 67 // 模版文件绝对路径 68 String realPath = request.getServletContext().getRealPath("/"); 69 // 文件路径 70 String filePath = realPath + matcher.group() + ".ftl"; 71 72 filePath = realPath + matcher.group() + ".ftl"; 73 filePath = realPath + "blog/template/template.ftl"; 74 filePath = filePath.replace("/", "\\"); 75 filePath = filePath.replace("\\\\", "\\"); 76 77 System.out.println(filePath); 78 // 查看服务器资源是否存在 79 File file = new File(filePath); 80 if (!file.exists()) { 81 // 路径不对,报错404 82 response.sendError(404, "您输入路径的不存在!"); 83 return; 84 } 85 // 解析路径中的文章id 86 int articleId = Integer.parseInt(matcher.group(3)); 87 System.out.println("articleId = " + articleId); 88 89 // 防止同一用户session添加多次访问量 90 boolean isNew = false; 91 // 获取当前用户session 92 HttpSession session = request.getSession(); 93 // 还没看过就能添加访问量 94 if (session.getAttribute(ARTICLE_TOKEN + articleId) == null) { 95 isNew = true; 96 // 设置当前的用户session已经看过文章了 97 session.setAttribute(ARTICLE_TOKEN + articleId, "true"); 98 } 99 100 // 填充模版信息 101 Map<String, Object> params = null; 102 try { 103 // 得到freemarker模版文件所需参数 104 params = this.getArticleService().getTemplateParams(articleId, request.getContextPath(), isNew); 105 params.put("contextPath", request.getContextPath()); 106 } catch (Exception e) { 107 e.printStackTrace(); 108 } 109 110 String templateDir = realPath + File.separator + "blog"; 111 String templateFile = matcher.group(1) + "/" + matcher.group(2) + "-" + matcher.group(3) + ".ftl"; 112 templateFile = "template/template.ftl"; 113 boolean result = TemplateUtils.parserTemplate(templateDir, templateFile, params, response.getOutputStream()); 114 115 if (!result) { 116 // 服务器异常 117 response.sendError(500, "服务器未知异常!"); 118 } 119 response.getOutputStream().close(); 120 } 121 122 }
TemplateUtils.java
1 package com.dx.ssh.utils; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.OutputStream; 6 import java.io.OutputStreamWriter; 7 import java.util.Map; 8 9 import freemarker.template.Configuration; 10 import freemarker.template.Template; 11 12 public class TemplateUtils { 13 private static Configuration configuration = null; 14 15 private TemplateUtils() { 16 } 17 18 public static Configuration getConfiguration(String templateDir) throws IOException { 19 if (configuration == null) { 20 configuration = new Configuration(); 21 configuration.setDirectoryForTemplateLoading(new File(templateDir)); 22 } 23 return configuration; 24 } 25 26 /** 27 * 向ftl模版中的数据替换 28 * */ 29 public static boolean parserTemplate(String templateDir, String ftlPath, Map<String, Object> map, OutputStream os) { 30 try { 31 Template template = getConfiguration(templateDir).getTemplate(ftlPath, "UTF-8"); 32 template.process(map, new OutputStreamWriter(os, "UTF-8")); 33 return true; 34 } catch (Exception e) { 35 e.printStackTrace(); 36 return false; 37 } 38 } 39 }
模版填充数据类型整理方法:
1 /** 2 * 得到freemarker模版文件所需参数 3 */ 4 @Override 5 public Map<String, Object> getTemplateParams(int articleId, String contextPath, boolean isNew) { 6 System.out.println("articleId=" + articleId); 7 // 要看的文章 8 ArticleEntity article = getArticleDao().findById(articleId); 9 10 if (article == null || article.getId() <= 0) 11 return null; 12 13 // 最新三篇文章 14 List<ArticleEntity> lastArticles = getArticleDao().findAllLastArticle(3); 15 16 // 所有类别 17 List<ArticleCategoryEntity> categories = getArticleCategoryDao().findAll(); 18 19 // 下一篇 20 ArticleEntity next = null; 21 List<ArticleEntity> nextArticles = articleDao.findNextArticle(3, article.getCreateDate()); 22 if (nextArticles == null || nextArticles.size() <= 0) { 23 next = new ArticleEntity(); 24 next.setStaticUrl("#"); 25 next.setTitle("这是最后一篇了哦!"); 26 } else { 27 next = nextArticles.get(0); 28 next.setStaticUrl(contextPath + next.getStaticUrl() + ".html"); 29 } 30 31 // 上一篇文章 32 ArticleEntity last = null; 33 List<ArticleEntity> lastAs = articleDao.findLastArticle(3, article.getCreateDate()); 34 if (lastAs == null || lastAs.size() <= 0) { 35 last = new ArticleEntity(); 36 last.setStaticUrl("#"); 37 last.setTitle("这是第一篇哦!"); 38 } else { 39 last = lastAs.get(0); 40 last.setStaticUrl(contextPath + last.getStaticUrl() + ".html"); 41 } 42 43 java.text.SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 44 // 封装模版所需参数 45 Map<String, Object> params = new HashMap<String, Object>(); 46 params.put("looked", article.getLooks()); 47 params.put("articleId", article.getId()); 48 params.put("time", format.format(article.getCreateDate())); 49 params.put("title", article.getTitle()); 50 params.put("content", article.getContent()); 51 params.put("typeString", "------------------------------------------------------"); 52 params.put("author", article.getAuthor()); 53 params.put("categoryName", getArticleCategoryDao().findById(article.getCategoryId()).getTitle()); 54 params.put("likes", article.getLikes()); 55 params.put("listCategoryArticle", contextPath + "/listArticle.action?categoryId=" + article.getCategoryId()); 56 params.put("lastArticlesList", lastArticles); 57 params.put("categoryList", categories); 58 params.put("likesURL", contextPath + "/likeAction.action?articleId=" + article.getId()); 59 params.put("nextArticle", next); 60 params.put("lastArticle", last); 61 params.put("staticURL", article.getStaticUrl()); 62 63 return params; 64 }
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。