在模板路径的构建策略一文中谈到模板的最终路径是由三个参量 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(此时将会查找成功)