最近在项目中使用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>
在livary.html中,我们使用了#import指令,该指令的模版路径,也是依据/pages/templates/的路径进行查找的。
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
}
}
libary.html模版的#import路径变化如下:
<!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>
directive_define.html模版文件不变
由上面可以看出,为什么网上一直说路径以 "/"开头的原因,我之前试了很多次,都不能成功,现在给出输出页面:
路径问题终于解决啦,~~~~~~
希望以上文章大家能够喜欢,能够知道在web中如何解决路径问题。希望大家多多支持哦