2021SC@SDUSC
cache.TemplateLoader及其子类总览图
代码
public interface TemplateLoader {
public Object findTemplateSource(String name)
throws IOException;
public long getLastModified(Object templateSource);
public Reader getReader(Object templateSource, String encoding) throws IOException;
public void closeTemplateSource(Object templateSource) throws IOException;
}
作用:模板加载器的接口
2.URLTemplateloader代码
public abstract class URLTemplateLoader implements TemplateLoader {
private Boolean urlConnectionUsesCaches;
@Override
public Object findTemplateSource(String name)
throws IOException {
URL url = getURL(name);
return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
}
@Override
public long getLastModified(Object templateSource) {
return ((URLTemplateSource) templateSource).lastModified();
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
return new InputStreamReader(
((URLTemplateSource) templateSource).getInputStream(),
encoding);
}
@Override
public void closeTemplateSource(Object templateSource)
throws IOException {
((URLTemplateSource) templateSource).close();
}
public Boolean getURLConnectionUsesCaches() {
return urlConnectionUsesCaches;
}
public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {
this.urlConnectionUsesCaches = urlConnectionUsesCaches;
}
protected abstract URL getURL(String name);
protected static String canonicalizePrefix(String prefix) {
// make it foolproof
prefix = prefix.replace('\', '/');
// ensure there's a trailing slash
if (prefix.length() > 0 && !prefix.endsWith("/")) {
prefix += "/";
}
return prefix;
}
}
作用:抽象模板加载器,它可以加载位置可以用URL描述的模板。这个超类只适用于仅获取URL就能立即判断资源是否存在的情况,而不适用于需要检查响应头才能知道的情况。子类只需要重写getURL(String)方法。
3.ClassTemplateLoader代码
public class ClassTemplateLoader extends URLTemplateLoader {
private final Class> resourceLoaderClass;
private final ClassLoader classLoader;
private final String basePackagePath;
@Deprecated
public ClassTemplateLoader() {
this(null, true, null, "/");
}
@Deprecated
public ClassTemplateLoader(Class> resourceLoaderClass) {
this(resourceLoaderClass, "");
}
public ClassTemplateLoader(Class> resourceLoaderClass, String basePackagePath) {
this(resourceLoaderClass, false, null, basePackagePath);
}
public ClassTemplateLoader(ClassLoader classLoader, String basePackagePath) {
this(null, true, classLoader, basePackagePath);
}
private ClassTemplateLoader(Class> resourceLoaderClass, boolean allowNullResourceLoaderClass,
ClassLoader classLoader, String basePackagePath) {
if (!allowNullResourceLoaderClass) {
NullArgumentException.check("resourceLoaderClass", resourceLoaderClass);
}
NullArgumentException.check("basePackagePath", basePackagePath);
// Either set a non-null resourceLoaderClass or a non-null classLoader, not both:
this.resourceLoaderClass = classLoader == null ? (resourceLoaderClass == null ? this.getClass()
: resourceLoaderClass) : null;
if (this.resourceLoaderClass == null && classLoader == null) {
throw new NullArgumentException("classLoader");
}
this.classLoader = classLoader;
String canonbasePackagePath = canonicalizePrefix(basePackagePath);
if (this.classLoader != null && canonbasePackagePath.startsWith("/")) {
canonbasePackagePath = canonbasePackagePath.substring(1);
}
this.basePackagePath = canonbasePackagePath;
}
@Override
protected URL getURL(String name) {
String fullPath = basePackagePath + name;
// Block java.net.URLClassLoader exploits:
if (basePackagePath.equals("/") && !isSchemeless(fullPath)) {
return null;
}
return resourceLoaderClass != null ? resourceLoaderClass.getResource(fullPath) : classLoader
.getResource(fullPath);
}
private static boolean isSchemeless(String fullPath) {
int i = 0;
int ln = fullPath.length();
// Skip a single initial /, as things like "/file:/..." might work:
if (i < ln && fullPath.charAt(i) == '/') i++;
// Check if there's no ":" earlier than a '/', as the URLClassLoader
// could interpret that as an URL scheme:
while (i < ln) {
char c = fullPath.charAt(i);
if (c == '/') return true;
if (c == ':') return false;
i++;
}
return true;
}
@Override
public String toString() {
return TemplateLoaderUtils.getClassNameForToString(this) + "("
+ (resourceLoaderClass != null
? "resourceLoaderClass=" + resourceLoaderClass.getName()
: "classLoader=" + StringUtil.jQuote(classLoader))
+ ", basePackagePath"
+ "="
+ StringUtil.jQuote(basePackagePath)
+ (resourceLoaderClass != null
? (basePackagePath.startsWith("/") ? "" : " ")
: ""
)
+ ")";
}
public Class getResourceLoaderClass() {
return resourceLoaderClass;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public String getbasePackagePath() {
return basePackagePath;
}
}
作用:一个可以从类路径加载模板的TemplateLoader的子类。它既可以在jar包中加载,也可以通过调用此类进行加载。
4.WebappTemplateLoader代码
public class WebappTemplateLoader implements TemplateLoader {
private static final Logger LOG = Logger.getLogger("freemarker.cache");
private final ServletContext servletContext;
private final String subdirPath;
private Boolean urlConnectionUsesCaches;
private boolean attemptFileAccess = true;
public WebappTemplateLoader(ServletContext servletContext) {
this(servletContext, "/");
}
public WebappTemplateLoader(ServletContext servletContext, String subdirPath) {
NullArgumentException.check("servletContext", servletContext);
NullArgumentException.check("subdirPath", subdirPath);
subdirPath = subdirPath.replace('\', '/');
if (!subdirPath.endsWith("/")) {
subdirPath += "/";
}
if (!subdirPath.startsWith("/")) {
subdirPath = "/" + subdirPath;
}
this.subdirPath = subdirPath;
this.servletContext = servletContext;
}
@Override
public Object findTemplateSource(String name) throws IOException {
String fullPath = subdirPath + name;
if (attemptFileAccess) {
// First try to open as plain file (to bypass servlet container resource caches).
try {
String realPath = servletContext.getRealPath(fullPath);
if (realPath != null) {
File file = new File(realPath);
if (file.canRead() && file.isFile()) {
return file;
}
}
} catch (SecurityException e) {
;// ignore
}
}
// If it fails, try to open it with servletContext.getResource.
URL url = null;
try {
url = servletContext.getResource(fullPath);
} catch (MalformedURLException e) {
LOG.warn("Could not retrieve resource " + StringUtil.jQuoteNoXSS(fullPath),
e);
return null;
}
return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
}
@Override
public long getLastModified(Object templateSource) {
if (templateSource instanceof File) {
return ((File) templateSource).lastModified();
} else {
return ((URLTemplateSource) templateSource).lastModified();
}
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
if (templateSource instanceof File) {
return new InputStreamReader(
new FileInputStream((File) templateSource),
encoding);
} else {
return new InputStreamReader(
((URLTemplateSource) templateSource).getInputStream(),
encoding);
}
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
if (templateSource instanceof File) {
// Do nothing.
} else {
((URLTemplateSource) templateSource).close();
}
}
public Boolean getURLConnectionUsesCaches() {
return urlConnectionUsesCaches;
}
public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {
this.urlConnectionUsesCaches = urlConnectionUsesCaches;
}
@Override
public String toString() {
return TemplateLoaderUtils.getClassNameForToString(this)
+ "(subdirPath=" + StringUtil.jQuote(subdirPath)
+ ", servletContext={contextPath=" + StringUtil.jQuote(getContextPath())
+ ", displayName=" + StringUtil.jQuote(servletContext.getServletContextName()) + "})";
}
private String getContextPath() {
try {
Method m = servletContext.getClass().getMethod("getContextPath", CollectionUtils.EMPTY_CLASS_ARRAY);
return (String) m.invoke(servletContext, CollectionUtils.EMPTY_OBJECT_ARRAY);
} catch (Throwable e) {
return "[can't query before Serlvet 2.5]";
}
}
public boolean getAttemptFileAccess() {
return attemptFileAccess;
}
public void setAttemptFileAccess(boolean attemptLoadingFromFile) {
this.attemptFileAccess = attemptLoadingFromFile;
}
}
作用:TemplateLoader的子类,以通过ServletContext.getResource(String)可以到达的stream作为模板来源
5.StringTemplateLoader代码
public class StringTemplateLoader implements TemplateLoader {
private final Map templates = new HashMap<>();
public void putTemplate(String name, String templateContent) {
putTemplate(name, templateContent, System.currentTimeMillis());
}
public void putTemplate(String name, String templateContent, long lastModified) {
templates.put(name, new StringTemplateSource(name, templateContent, lastModified));
}
public boolean removeTemplate(String name) {
return templates.remove(name) != null;
}
@Override
public void closeTemplateSource(Object templateSource) {
}
@Override
public Object findTemplateSource(String name) {
return templates.get(name);
}
@Override
public long getLastModified(Object templateSource) {
return ((StringTemplateSource) templateSource).lastModified;
}
@Override
public Reader getReader(Object templateSource, String encoding) {
return new StringReader(((StringTemplateSource) templateSource).templateContent);
}
private static class StringTemplateSource {
private final String name;
private final String templateContent;
private final long lastModified;
StringTemplateSource(String name, String templateContent, long lastModified) {
if (name == null) {
throw new IllegalArgumentException("name == null");
}
if (templateContent == null) {
throw new IllegalArgumentException("source == null");
}
if (lastModified < -1L) {
throw new IllegalArgumentException("lastModified < -1L");
}
this.name = name;
this.templateContent = templateContent;
this.lastModified = lastModified;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StringTemplateSource other = (StringTemplateSource) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return name;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(TemplateLoaderUtils.getClassNameForToString(this));
sb.append("(Map { ");
int cnt = 0;
for (String name : templates.keySet()) {
cnt++;
if (cnt != 1) {
sb.append(", ");
}
if (cnt > 10) {
sb.append("...");
break;
}
sb.append(StringUtil.jQuote(name));
sb.append("=...");
}
if (cnt != 0) {
sb.append(' ');
}
sb.append("})");
return sb.toString();
}
}
作用:使用字符串创建模板加载器
6.ByteArrayTemplateLoader代码
public class ByteArrayTemplateLoader implements TemplateLoader {
private final Map templates = new HashMap<>();
ds a template to this template loader; see {@link StringTemplateLoader#putTemplate(String, String)} for more.
*/
public void putTemplate(String name, byte[] templateContent) {
putTemplate(name, templateContent, System.currentTimeMillis());
}
public void putTemplate(String name, byte[] templateContent, long lastModified) {
templates.put(name, new ByteArrayTemplateSource(name, templateContent, lastModified));
}
public boolean removeTemplate(String name) {
return templates.remove(name) != null;
}
@Override
public void closeTemplateSource(Object templateSource) {
}
@Override
public Object findTemplateSource(String name) {
return templates.get(name);
}
@Override
public long getLastModified(Object templateSource) {
return ((ByteArrayTemplateSource) templateSource).lastModified;
}
@Override
public Reader getReader(Object templateSource, String encoding) throws UnsupportedEncodingException {
return new InputStreamReader(
new ByteArrayInputStream(((ByteArrayTemplateSource) templateSource).templateContent),
encoding);
}
private static class ByteArrayTemplateSource {
private final String name;
private final byte[] templateContent;
private final long lastModified;
ByteArrayTemplateSource(String name, byte[] templateContent, long lastModified) {
if (name == null) {
throw new IllegalArgumentException("name == null");
}
if (templateContent == null) {
throw new IllegalArgumentException("templateContent == null");
}
if (lastModified < -1L) {
throw new IllegalArgumentException("lastModified < -1L");
}
this.name = name;
this.templateContent = templateContent;
this.lastModified = lastModified;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ByteArrayTemplateSource other = (ByteArrayTemplateSource) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(TemplateLoaderUtils.getClassNameForToString(this));
sb.append("(Map { ");
int cnt = 0;
for (String name : templates.keySet()) {
cnt++;
if (cnt != 1) {
sb.append(", ");
}
if (cnt > 10) {
sb.append("...");
break;
}
sb.append(StringUtil.jQuote(name));
sb.append("=...");
}
if (cnt != 0) {
sb.append(' ');
}
sb.append("})");
return sb.toString();
}
}
作用:与StringTemplateLoader类似,使用byte[]创建模板加载器
7.FileTemplateLoaderpublic class FileTemplateLoader implements TemplateLoader {
public static String SYSTEM_PROPERTY_NAME_EMULATE_CASE_SENSITIVE_FILE_SYSTEM
= "org.freemarker.emulateCaseSensitiveFileSystem";
private static final boolean EMULATE_CASE_SENSITIVE_FILE_SYSTEM_DEFAULT;
static {
final String s = SecurityUtilities.getSystemProperty(SYSTEM_PROPERTY_NAME_EMULATE_CASE_SENSITIVE_FILE_SYSTEM,
"false");
boolean emuCaseSensFS;
try {
emuCaseSensFS = StringUtil.getYesNo(s);
} catch (Exception e) {
emuCaseSensFS = false;
}
EMULATE_CASE_SENSITIVE_FILE_SYSTEM_DEFAULT = emuCaseSensFS;
}
private static final int CASE_CHECH_CACHE_HARD_SIZE = 50;
private static final int CASE_CHECK_CACHE__SOFT_SIZE = 1000;
private static final boolean SEP_IS_SLASH = File.separatorChar == '/';
private static final Logger LOG = Logger.getLogger("freemarker.cache");
public final File baseDir;
private final String canonicalbasePath;
private boolean emulateCaseSensitiveFileSystem;
private MruCacheStorage correctCasePaths;
@Deprecated
public FileTemplateLoader() throws IOException {
this(new File(SecurityUtilities.getSystemProperty("user.dir")));
}
public FileTemplateLoader(final File baseDir) throws IOException {
this(baseDir, false);
}
public FileTemplateLoader(final File baseDir, final boolean disableCanonicalPathCheck) throws IOException {
try {
Object[] retval = AccessController.doPrivileged(new PrivilegedExceptionAction
作用:使用特定文件夹下的文件生成模板加载器。生成之前会进行安全性检测
8.MultiTemplateLoader代码
public class MultiTemplateLoader implements StatefulTemplateLoader {
private final TemplateLoader[] templateLoaders;
private final Map lastTemplateLoaderForName
= new ConcurrentHashMap<>();
private boolean sticky = true;
public MultiTemplateLoader(TemplateLoader[] templateLoaders) {
NullArgumentException.check("templateLoaders", templateLoaders);
this.templateLoaders = templateLoaders.clone();
}
@Override
public Object findTemplateSource(String name)
throws IOException {
TemplateLoader lastTemplateLoader = null;
if (sticky) {
// Use soft affinity - give the loader that last found this
// resource a chance to find it again first.
lastTemplateLoader = lastTemplateLoaderForName.get(name);
if (lastTemplateLoader != null) {
Object source = lastTemplateLoader.findTemplateSource(name);
if (source != null) {
return new MultiSource(source, lastTemplateLoader);
}
}
}
// If there is no affine loader, or it could not find the resource
// again, try all loaders in order of appearance. If any manages
// to find the resource, then associate it as the new affine loader
// for this resource.
for (TemplateLoader templateLoader : templateLoaders) {
if (lastTemplateLoader != templateLoader) {
Object source = templateLoader.findTemplateSource(name);
if (source != null) {
if (sticky) {
lastTemplateLoaderForName.put(name, templateLoader);
}
return new MultiSource(source, templateLoader);
}
}
}
if (sticky) {
lastTemplateLoaderForName.remove(name);
}
// Resource not found
return null;
}
@Override
public long getLastModified(Object templateSource) {
return ((MultiSource) templateSource).getLastModified();
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
return ((MultiSource) templateSource).getReader(encoding);
}
@Override
public void closeTemplateSource(Object templateSource)
throws IOException {
((MultiSource) templateSource).close();
}
@Override
public void resetState() {
lastTemplateLoaderForName.clear();
for (TemplateLoader loader : templateLoaders) {
if (loader instanceof StatefulTemplateLoader) {
((StatefulTemplateLoader) loader).resetState();
}
}
}
static final class MultiSource {
private final Object source;
private final TemplateLoader loader;
MultiSource(Object source, TemplateLoader loader) {
this.source = source;
this.loader = loader;
}
long getLastModified() {
return loader.getLastModified(source);
}
Reader getReader(String encoding)
throws IOException {
return loader.getReader(source, encoding);
}
void close()
throws IOException {
loader.closeTemplateSource(source);
}
Object getWrappedSource() {
return source;
}
@Override
public boolean equals(Object o) {
if (o instanceof MultiSource) {
MultiSource m = (MultiSource) o;
return m.loader.equals(loader) && m.source.equals(source);
}
return false;
}
@Override
public int hashCode() {
return loader.hashCode() + 31 * source.hashCode();
}
@Override
public String toString() {
return source.toString();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MultiTemplateLoader(");
for (int i = 0; i < templateLoaders.length; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append("loader").append(i + 1).append(" = ").append(templateLoaders[i]);
}
sb.append(")");
return sb.toString();
}
public int getTemplateLoaderCount() {
return templateLoaders.length;
}
public TemplateLoader getTemplateLoader(int index) {
return templateLoaders[index];
}
lean isSticky() {
return sticky;
}
public void setSticky(boolean sticky) {
this.sticky = sticky;
}
}
作用:使用其他的模板生成器生成模板
注:Freemarker代码来自FreeMarker 中文官方参考手册
新手写的代码分析,文章若有错误还请指出



