最近在客户需求的要求就定制开发一套CMS,模板采用了Freemarker。最初的时候是设想用Freemarker标签开发的模板,也做网站页面的模板。然后可以直接在页面模板中使用开发好的标签,来显示网站内容。但总还是觉得这样的模板不够灵活,局限性比较大。后来通过和领导的商议决定采用一下方式,最终可以直接在Freemarker页面模板层对已封装好的API进行调用,可以实现在模板中的简单编程。
这种做法也是在探索中,如果大家觉得有什么不妥之处还望点拨。
- 模板存放在数据库中(通常我们都是编写Freemarker的ftl模板文件)
- 采用StringTemplateLoader
- 采用BeansWrapper
首先我们要开发一个Servlet去拦截网站的请求,因为我们要去解析模板。FreeMarker为我们提供了一个现成的Servlet:freemarker.ext.servlet.FreemarkerServlet。但是这个不能满足我们的要求,它是按照URL请求的文件名称来去查找解析模板的,例如/article/demo.ftl,那么它只能去模板存放目录寻找demo.ftl模板。呵呵,其实这个 Servlet是未来让FreeMarker代替jsp使用的,而且他的它也无法装载数据库中的模板信息,所以我就参考它开发了自己的Servlet。
package freemarker.ext.servlet; /** * 参考自freemarker.ext.servlet.FreemarkerServlet * 支持自定义标签的使用,支持自定义扩展名拦截. */ public class FreeMarkerStringTemplateViewServlet extends javax.servlet.http.HttpServlet { /** * 为子类提供Log功能,方便子类使用 */ protected Log log = LogFactory.getLog(getClass()); /** TemplatePath */ private static final String TEMPLATE_PATH = "TemplatePath"; /** NoCache */ private static final String NOCACHE = "NoCache"; /** TemplateDelay */ private static final String TEMPLATE_DELAY = "template_update_delay"; /** DefaultEncoding */ private static final String DEF_ENCODING = "default_encoding"; /** Request */ public static final String KEY_REQUEST = "Request"; /** __FreeMarkerServlet.Request__ */ public static final String KEY_REQUEST_PRIVATE = "__FreeMarkerServlet.Request__"; /** RequestParameters */ public static final String KEY_REQUEST_PARAMETERS = "RequestParameters"; /** Session */ public static final String KEY_SESSION = "Session"; /** Application */ public static final String KEY_APPLICATION = "Application"; /** __FreeMarkerServlet.Application__ */ public static final String KEY_APPLICATION_PRIVATE = "__FreeMarkerServlet.Application__"; /** JspTaglibs */ public static final String KEY_JSP_TAGLIBS = "JspTaglibs"; /** .freemarker.Request */ private static final String ATTR_REQUEST_MODEL = ".freemarker.Request"; /** .freemarker.RequestParameters */ private static final String ATTR_REQUEST_PARAMETERS_MODEL = ".freemarker.RequestParameters"; /** .freemarker.Session */ private static final String ATTR_SESSION_MODEL = ".freemarker.Session"; /** .freemarker.Application */ private static final String ATTR_APPLICATION_MODEL = ".freemarker.Application"; /** .freemarker.JspTaglibs */ private static final String ATTR_JSP_TAGLIBS_MODEL = ".freemarker.JspTaglibs"; /** 日期 */ private static final String EXPIRATION_DATE; static { GregorianCalendar expiration = new GregorianCalendar(); expiration.roll(Calendar.YEAR, -1); SimpleDateFormat httpDate = new SimpleDateFormat( "yyyy-MMM-dd HH:mm:ss", java.util.Locale.CHINA); EXPIRATION_DATE = httpDate.format(expiration.getTime()); } /** * response返回是否使用缓存 */ private boolean nocache; /** * 创新Freemarker模板的必要条件 */ private Configuration config; /** * 采用BEANS_WRAPPER */ private ObjectWrapper wrapper; /** * text/html */ private String contentType; /** * 采用StringLoader,从数据库中读取模板信息 */ //private StringTemplateLoader strTmpt; /** * Servlet 初始化 */ public void init() throws ServletException { try { config = new Configuration(); config.setTemplateExceptionHandler( TemplateExceptionHandler.HTML_DEBUG_HANDLER); contentType = "text/html"; // 采用BEANS_WRAPPER wrapper = ObjectWrapper.BEANS_WRAPPER; config.setObjectWrapper(wrapper); // 初始化所有的Servlet参数 Enumeration initpnames = getServletConfig().getInitParameterNames(); while (initpnames.hasMoreElements()) { String name = (String) initpnames.nextElement(); String value = getInitParameter(name); if (name == null) { throw new ServletException(this.getClass().toString() + "需要一些初始化参数,web.xml可能尚未完成."); } if (value == null) { throw new ServletException(this.getClass().toString() + "有部分初始化参数未被赋值,web.xml可能尚未完成."); } if (name.equals(TEMPLATE_PATH)) { // ignore: we have already processed these do nothing.. log.debug("忽略" + TEMPLATE_PATH); } else if (name.equals(DEF_ENCODING)) { // set DefaultEncoding log.debug(DEF_ENCODING + " value is:" + value); config.setDefaultEncoding(value); } else if (name.equals(TEMPLATE_DELAY)) { // 模板延迟更新时间 try { log.debug(TEMPLATE_DELAY + " value is:" + value); config.setTemplateUpdateDelay(Integer.parseInt(value)); } catch (NumberFormatException e) { throw new ServletException(e.getMessage() + ". '" + TEMPLATE_DELAY + "'必须是整数"); } } else if (name.equals(NOCACHE)) { // 设置缓存 log.debug(NOCACHE + " value is :" + value); nocache = StringUtil.getYesNo(value); } else { // 设置其它参数,嘿嘿,如果参数名称不符合Configuration要求肯定要Exception config.setSetting(name, value); } } } catch (ServletException e) { throw e; } catch (Exception e) { throw new ServletException(e); } } /** Get请求 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } /** Post请求 */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } /** * 向StringTemplateLoader中添加模板内容 * @throws ServletException */ private StringTemplateLoader addStringTemplate( StringTemplateContext strTmpCtx) throws ServletException { if (strTmpCtx == null) { throw new ServletException("StringTemplateContext is null"); } if (strTmpCtx.getTemplateContent() != null) { StringTemplateLoader stl = getStrTmpt(); stl.putTemplate(strTmpCtx.getTemplateName(), strTmpCtx.getTemplateContent()); setStrTmpt(stl); //重写添加TemplateLoader log.debug("StringTemplateLoader成功添加模板'" + strTmpCtx.getTemplateName() + "'"); } return getStrTmpt(); } /** * 模板解析过程 * @throws ParseURLToTemplateException */ private void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { StringTemplateLoader strTmpt = getStrTmpt(); if (strTmpt == null) { strTmpt = new StringTemplateLoader(); setStrTmpt(strTmpt); } // 自定义类型StringTemplateContext,存放[模板名称]和[模板内容] StringTemplateContext strTmpCtx = null; try { strTmpCtx = reqUrlToModelCtx(req, strTmpt); } catch (ParseURLToTemplateException pe) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); pe.printStackTrace(); } //添加模板 strTmpt = addStringTemplate(strTmpCtx); config.setTemplateLoader(strTmpt); Template template = config.getTemplate(strTmpCtx.getTemplateName()); Object attrContentType = template.getCustomAttribute("content_type"); if (attrContentType != null) { resp.setContentType(attrContentType.toString()); } else { resp.setContentType(contentType + "; charset=" + template.getEncoding()); } setBrowserCaching(resp); ServletContext servletContext = getServletContext(); try { TemplateModel model = createModel(wrapper, servletContext, req, resp, strTmpCtx.getModel()); template.process(model, resp.getWriter()); } catch (TemplateException te) { ServletException e = new ServletException( "Error executing FreeMarker template", te); try { e.getClass().getMethod("initCause", new Class[] { Throwable.class }).invoke(e, new Object[] { te }); } catch (Exception ex) { // Can't set init cause, we're probably running on a pre-1.4 // JDK, oh well... } throw e; } } /** * 创建Freemarker模板的model */ protected TemplateModel createModel(ObjectWrapper wrap, ServletContext servletContext, HttpServletRequest request, HttpServletResponse response, Map model) throws TemplateModelException { AllHttpScopesHashModel params = new AllHttpScopesHashModel(wrap, servletContext, request); // Create hash model wrapper for servlet context (the application) ServletContextHashModel servletContextModel = (ServletContextHashModel) servletContext .getAttribute(ATTR_APPLICATION_MODEL); if (servletContextModel == null) { servletContextModel = new ServletContextHashModel(this, wrap); servletContext.setAttribute(ATTR_APPLICATION_MODEL, servletContextModel); TaglibFactory taglibs = new TaglibFactory(servletContext); servletContext.setAttribute(ATTR_JSP_TAGLIBS_MODEL, taglibs); } params.putUnlistedModel(KEY_APPLICATION, servletContextModel); params.putUnlistedModel(KEY_APPLICATION_PRIVATE, servletContextModel); params.putUnlistedModel(KEY_JSP_TAGLIBS, (TemplateModel) servletContext .getAttribute(ATTR_JSP_TAGLIBS_MODEL)); // Create hash model wrapper for session HttpSessionHashModel sessionModel; HttpSession session = request.getSession(); sessionModel = (HttpSessionHashModel) session .getAttribute(ATTR_SESSION_MODEL); if (sessionModel == null || sessionModel.isZombie()) { sessionModel = new HttpSessionHashModel(session, wrap); session.setAttribute(ATTR_SESSION_MODEL, sessionModel); } params.putUnlistedModel(KEY_SESSION, sessionModel); // Create hash model wrapper for request HttpRequestHashModel requestModel = (HttpRequestHashModel) request .getAttribute(ATTR_REQUEST_MODEL); if (requestModel == null || requestModel.getRequest() != request) { requestModel = new HttpRequestHashModel(request, response, wrap); request.setAttribute(ATTR_REQUEST_MODEL, requestModel); request.setAttribute(ATTR_REQUEST_PARAMETERS_MODEL, new HttpRequestParametersHashModel(request)); } params.putUnlistedModel(KEY_REQUEST, requestModel); params.putUnlistedModel(KEY_REQUEST_PRIVATE, requestModel); // Create hash model wrapper for request parameters HttpRequestParametersHashModel requestParametersModel = (HttpRequestParametersHashModel) request .getAttribute(ATTR_REQUEST_PARAMETERS_MODEL); params.putUnlistedModel(KEY_REQUEST_PARAMETERS, requestParametersModel); params.putAll(model); return params; } /** * 需要有自类重写,返回需要的TemplateModelContext[templateName Model] * * @return * @throws ParseURLToTemplateException * @throws Exception */ protected StringTemplateContext reqUrlToModelCtx(HttpServletRequest req, StringTemplateLoader tmLoader) throws ParseURLToTemplateException { // 需要子类去重写,完成封装数据 return null; } /** * If the parameter "nocache" was set to true, generate a set of headers * that will advise the HTTP client not to cache the returned page. */ private void setBrowserCaching(HttpServletResponse res) { if (nocache) { // HTTP/1.1 + IE extensions res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, " + "post-check=0, pre-check=0"); // HTTP/1.0 res.setHeader("Pragma", "no-cache"); // Last resort for those that ignore all of the above res.setHeader("Expires", EXPIRATION_DATE); } } /** * getter strTmpt * @return */ private StringTemplateLoader getStrTmpt() { return (StringTemplateLoader) getServletContext().getAttribute( "stringTemplateLoader"); } /** * setter strTmpt * @param strTmpt */ private void setStrTmpt(StringTemplateLoader strTmpt) { getServletContext().setAttribute("stringTemplateLoader", strTmpt); } }
未完待续....就像这人世间错综复杂、乱七八糟的种种破事