最近在项目中使用FreeMarker时,需要使用 #import 引入自定义的库文件,始终出现无法找到模版文件(FileNotFoundException),在网上查了很多资料都没有查处原因,或者是说的不够详细,所以自己去查阅了一下官方文档,并做了实验,最终得到自己所要的结果,希望能够通过这篇文章,让大家能够比较详细的理解并且不走弯路。
一、开发环境
1. jdk1.6
2. maven工程
3. 开放工具: eclipse
二、项目目录结构
注: 我是将模版文件存放在 webapp/pages/templates下,不是maven工程,相当于(WebContent/pages/templates)下。
三、解决问题
1. 构建Configuration对象,与java普通工程有所不同,因为我没有将模版存在资源文件目录下,因此这里需要使用 Configuratio.setServletContextForTemplateLoading(ServletContext cxt, String path);方法加载模版。工具类代码如下
package com.freemarker.learn.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import org.apache.commons.lang3.StringUtils; import com.alibaba.fastjson.JSONObject; import freemarker.cache.StringTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.Version; /** * Created by mobao-xi on 16/4/12. */ public class TemplateTool { private static Map<String, Template> TEMPLATE_MAP = new HashMap<String, Template>(); private static Configuration cfg = new Configuration(new Version("2.3.23")); static { // Don't log exceptions inside FreeMarker that it will thrown at you anyway: cfg.setLogTemplateExceptions(false); } public static void settingConfig(ServletContext cxt) { cfg.setServletContextForTemplateLoading(cxt, "/pages/templates/"); } public static Template getTemplate(String path) throws IOException { Template template = TEMPLATE_MAP.get(path); if (null == template) { /*template = inputStream2String(new FileInputStream(path.toString())); TEMPLATE_MAP.put(path, template);*/ template = cfg.getTemplate(path); TEMPLATE_MAP.put(path, template); } return template; } public static String getTemplate(String path, Object data) throws Exception { Template template = getTemplate(path); //StringTemplateLoader loader = new StringTemplateLoader(); //loader.putTemplate("", source); //cfg.setTemplateLoader(loader); cfg.setDefaultEncoding("UTF-8"); //Template template = cfg.getTemplate(""); StringWriter writer = new StringWriter(); template.process(JSONObject.toJSON(data), writer); String source = writer.toString(); return source; } /** * 将stream 转成字符串 * * @param is * @return * @throws IOException */ private static String inputStream2String(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i = -1; while ((i = is.read()) != -1) { baos.write(i); } return baos.toString(); } }我在初始化Configuration时,设置了模版的基本路径,"/pages/templates/"在获取路径时,实际上是按照webapp/pages/templates的路径进行查找路径。
2. 在servlet 初始化时,配置Configuration。因为我采用的是Servlet方式,没有采用第三方框架,所以我在初始化servlet时,将ServletContext出入、这样会比较方便。
代码如下:
package com.freemarker.learn.servlet.base; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.freemarker.learn.response.ResponseEntity; import com.freemarker.learn.util.EntityUtil; import com.freemarker.learn.util.RequestUtil; import com.freemarker.learn.util.TemplateTool; public class BaseServlet extends HttpServlet{ private static final long serialVersionUID = 5315308689001350036L; @Override public void init() throws ServletException { // TODO Auto-generated method stub super.init(); TemplateTool.settingConfig(getServletContext()); } protected void returnResult(HttpServletResponse resp, ResponseEntity entity ) { } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { setHTMLPlain(resp); RequestUtil.setReq(req); //获取请求的资源 String servPath = req.getRequestURI(); System.out.println("request uri:" + servPath); //获取请求的类型 String reqUri = servPath.substring(servPath.indexOf("base/")+5, servPath.length()); //reqUri = reqUri.replace("/", ""); PrintWriter out = new PrintWriter(resp.getOutputStream()); try { ResponseEntity re = EntityUtil.getResponseEntity(reqUri); String template = TemplateTool.getTemplate(re.getFtlPath(), re.getData()); if(template == null) { out.println("找到对应服务"); } else { out.println(template); } } catch (Exception e) { e.printStackTrace(); } finally { out.flush(); out.close(); } RequestUtil.remove(); } private void setHTMLPlain(HttpServletResponse resp) { resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html"); } }3. 返回模版的路径。代码如下:
package com.freemarker.learn.response; import java.util.Map; import com.freemarker.learn.annotation.Abbr; @Abbr(name="directive/lib") public class LibResponseEntity extends ResponseEntity{ @Override public Map<String, Object> getData() { return null; } @Override public String getFtlPath() { return "libary.html"; } @Override public void generateData() { // TODO Auto-generated method stub } }以上类中,getFtlPath()方法返回了 “libary.html” 的字符串,通过上面的结构图,能够发现,libary.html 文件是存放在 webapps/pages/templates/libary.html。因此我们在初始化Configuration时的路径,是很重要的。
4. 定义模版libary.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>if调用库</title> </head> <body> <#import "directive_define.html" as lib /> <@lib.copyright date="1992-2017" /> </body> </html>
directive_define.html代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>FreeMarker自定义指令</title> </head> <body> <#macro greet> <h1>hello Joe!</h1> </#macro> <@greet/> <#macro copyright date> <p>Copyright (C) ${date} Julia Smith. All Right reserved!</p> </#macro> </body> </html>
package com.freemarker.learn.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import org.apache.commons.lang3.StringUtils; import com.alibaba.fastjson.JSONObject; import freemarker.cache.StringTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.Version; /** * Created by mobao-xi on 16/4/12. */ public class TemplateTool { private static Map<String, Template> TEMPLATE_MAP = new HashMap<String, Template>(); private static Configuration cfg = new Configuration(new Version("2.3.23")); static { // Don't log exceptions inside FreeMarker that it will thrown at you anyway: cfg.setLogTemplateExceptions(false); } public static void settingConfig(ServletContext cxt) { cfg.setServletContextForTemplateLoading(cxt, "/"); } public static Template getTemplate(String path) throws IOException { Template template = TEMPLATE_MAP.get(path); if (null == template) { /*template = inputStream2String(new FileInputStream(path.toString())); TEMPLATE_MAP.put(path, template);*/ template = cfg.getTemplate(path); TEMPLATE_MAP.put(path, template); } return template; } public static String getTemplate(String path, Object data) throws Exception { Template template = getTemplate(path); //StringTemplateLoader loader = new StringTemplateLoader(); //loader.putTemplate("", source); //cfg.setTemplateLoader(loader); cfg.setDefaultEncoding("UTF-8"); //Template template = cfg.getTemplate(""); StringWriter writer = new StringWriter(); template.process(JSONObject.toJSON(data), writer); String source = writer.toString(); return source; } /** * 将stream 转成字符串 * * @param is * @return * @throws IOException */ private static String inputStream2String(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i = -1; while ((i = is.read()) != -1) { baos.write(i); } return baos.toString(); } }
以上标红的行,相对于以上的代码,只是做了一个路径的变化,因为
setServletContextForTemplateLoading 的方法默认是调用ServletContext.getResource()的方式,
因此"/"代表着webapp/路径,这样我们就可以随心所欲的访问webapp下的资源: 对应的代码变化如下:
package com.freemarker.learn.response; import java.util.Map; import com.freemarker.learn.annotation.Abbr; @Abbr(name="directive/lib") public class LibResponseEntity extends ResponseEntity{ @Override public Map<String, Object> getData() { return null; } @Override public String getFtlPath() { return "/pages/templates/libary.html"; } @Override public void generateData() { // TODO Auto-generated method stub } }
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>if调用库</title> </head> <body> <#import "/pages/templates/directive_define.html" as lib /> <@lib.copyright date="1992-2017" /> </body> </html>
由上面可以看出,为什么网上一直说路径以 "/"开头的原因,我之前试了很多次,都不能成功,现在给出输出页面:
路径问题终于解决啦,~~~~~~
希望以上文章大家能够喜欢,能够知道在web中如何解决路径问题。希望大家多多支持哦