FreeMarker在使用#include和#import时,路径问题

Posted 专注着

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeMarker在使用#include和#import时,路径问题相关的知识,希望对你有一定的参考价值。

最近在项目中使用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中如何解决路径问题。希望大家多多支持哦


以上是关于FreeMarker在使用#include和#import时,路径问题的主要内容,如果未能解决你的问题,请参考以下文章

freemarker include必须在同一个目录下吗

Freemarker:包含和导入之间的区别?

关于freemarker include的问题

Freemarker入门案例

如何配置 Maven Freemarker 以识别 `@include_page` 标签?

freemarker size判空 为啥出现这个错误