在模板路径的构建策略一文中谈到模板的最终路径是由三个参量 templateDir(模板目录),theme(主题),template(模板名称)所决定的,例如:
<@rs.nav templateDir='template' theme='ajax' template="nav" />
上面的一个自定义组件nav它的模板路径就为/template/ajax/nav,如果模板引擎选择freemarker则最终的路径应该为/template/ajax/nav.ftl,在进行路径配置的时候其中有很重要的条就是除了在template参数中可以带一个扩展名称外其它任何地方都不可以使用小数点,这是因为在struts2中路径中的点是非常敏感的,它决定了对模板引擎的选择问题,具体可以查看TemplateEngineManager中的代码,其中关键源代码如下:
public TemplateEngine getTemplateEngine(Template template, String templateTypeOverride) { String templateType = DEFAULT_TEMPLATE_TYPE; String templateName = template.toString(); if (templateName.indexOf(".") > 0) { templateType = templateName.substring(templateName.indexOf(".") + 1); } else if (templateTypeOverride !=null && templateTypeOverride.length() > 0) { templateType = templateTypeOverride; } else { String type = defaultTemplateType; if (type != null) { templateType = type; } } return templateEngines.get(templateType).create(); }
从这个地方的源代码可以看出此方法实现有些不好,至少要用一个lastIndexOf方法,使得开发者对路径的控制可以更灵活一些,根据上面的代码可以看出开发者不能在templateDir(模板目录),theme(主题),中添加任何小数点符号,也不能在template(模板名称)中添加两个或两个以上的小数点符号,
假如我们可以在theme中使用小数点则可以如些设计:
<@rs.nav templateDir='template' theme='.' template="nav" />
此时的路径就会变成/template/./nav.ftl而实际最终的路径为:/template/nav.ftl这样可以使得用开发者对默认模板有更灵活的控制.
另外还有几种怪异的符符号/与*如果程序为如下的形式:
<@rs.nav templateDir='template/ajax' theme='/' template="nav" />
假设在template/ajax路径下有一个模板nav.ftl那么上面的配置是否可以成功呢?答案是肯定的这是因为在一个文件路径中多几个/是可以忽略的,上面生成的最终路径是:template/ajax///nav.ftl但是加载器任何可以查找到相关的资源.
如果程序配置为如下形式:
<@rs.nav templateDir='template/ajax/a/b' theme='*' template="nav" />
是否可以查看到相关的资源,答案也是肯定的,上面的那个*是有特别含义的,此逻辑是freemarker框架中的TemplateCache对象提供的其关键源代码如下:
private Object acquireTemplateSource(String path) throws IOException { int asterisk = path.indexOf(ASTERISK); // Shortcut in case there is no acquisition if(asterisk == -1) { return mainLoader.findTemplateSource(path); } StringTokenizer tok = new StringTokenizer(path, "/"); int lastAsterisk = -1; List tokpath = new ArrayList(); while(tok.hasMoreTokens()) { String pathToken = tok.nextToken(); if(pathToken.equals(ASTERISKSTR)) { if(lastAsterisk != -1) { tokpath.remove(lastAsterisk); } lastAsterisk = tokpath.size(); } tokpath.add(pathToken); } String basePath = concatPath(tokpath, 0, lastAsterisk); String resourcePath = concatPath(tokpath, lastAsterisk + 1, tokpath.size()); if(resourcePath.endsWith("/")) { resourcePath = resourcePath.substring(0, resourcePath.length() - 1); } StringBuffer buf = new StringBuffer(path.length()).append(basePath); int l = basePath.length(); boolean debug = logger.isDebugEnabled(); for(;;) { String fullPath = buf.append(resourcePath).toString(); if(debug) { logger.debug("Trying to find template source " + fullPath); } Object templateSource = mainLoader.findTemplateSource(fullPath); if(templateSource != null) { return templateSource; } if(l == 0) { return null; } l = basePath.lastIndexOf(SLASH, l - 2) + 1; buf.setLength(l); } }
从上面的代码可以看出它会按*左侧的路径逐次查找:第一次查找template/ajax/a/b/nav.ftl;第二次查找template/ajax/a/nav.ftl;第三次查找template/ajax/nav.ftl(此时将会查找成功)