2021SC@SDUSC
目录
createProcessingEnvironment()方法
template包
与log有关的代码分析已经完成接下来集中于对FreeMarker的template进行分析。
模板引擎的作用
- 模板引擎的目标是“数据+模板=结果”
- 模板引擎将数据与展现有效的“解耦”
- 前端只需要知道怎么编写前端,后端只需关注后端,用模板引擎把两者整合
Template.java
源码展示
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.template;
import java.io.BufferedReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import javax.swing.tree.TreePath;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateLoader;
import freemarker.cache.TemplateLookupStrategy;
import freemarker.core.BugException;
import freemarker.core.Configurable;
import freemarker.core.Environment;
import freemarker.core.FMParser;
import freemarker.core.LibraryLoad;
import freemarker.core.Macro;
import freemarker.core.OutputFormat;
import freemarker.core.ParseException;
import freemarker.core.ParserConfiguration;
import freemarker.core.TemplateConfiguration;
import freemarker.core.TemplateElement;
import freemarker.core.TextBlock;
import freemarker.core.TokenMgrError;
import freemarker.core._CoreAPI;
import freemarker.debug.impl.DebuggerService;
/**
* Stores an already parsed template, ready to be processed (rendered) for unlimited times, possibly from multiple
* threads.
*
* <p>
* Typically, you will use {
@link Configuration#getTemplate(String)} to create/get {
@link Template} objects, so you
* don't construct them directly. But you can also construct a template from a {
@link Reader} or a {
@link String} that
* contains the template source code. But then it's important to know that while the resulting {
@link Template} is
* efficient for later processing, creating a new {
@link Template} itself is relatively expensive. So try to re-use
* {
@link Template} objects if possible. {
@link Configuration#getTemplate(String)} (and its overloads) does that
* (caching {
@link Template}-s) for you, but the constructor of course doesn't, so it's up to you to solve then.
*
* <p>
* Objects of this class meant to be handled as immutable and thus thread-safe. However, it has some setter methods for
* changing FreeMarker settings. Those must not be used while the template is being processed, or if the template object
* is already accessible from multiple threads. If some templates need different settings that those coming from the
* shared {
@link Configuration}, and you are using {
@link Configuration#getTemplate(String)} (or its overloads), then
* use {
@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)} to achieve that.
*/
public class Template extends Configurable {
public static final String DEFAULT_NAMESPACE_PREFIX = "D";
public static final String NO_NS_PREFIX = "N";
private static final int READER_BUFFER_SIZE = 4096;
private Map macros = new HashMap();
private List imports = new Vector();
private TemplateElement rootElement;
private String encoding, defaultNS;
private Object customLookupCondition;
private int interpolationSyntax;
private int actualTagSyntax;
private int actualNamingConvention;
private boolean autoEscaping;
private OutputFormat outputFormat;
private final String name;
private final String sourceName;
private final ArrayList lines = new ArrayList();
private final ParserConfiguration parserConfiguration;
private Map prefixToNamespaceURILookup = new HashMap();
private Map namespaceURIToPrefixLookup = new HashMap();
private Version templateLanguageVersion;
/**
* A prime constructor to which all other constructors should
* delegate directly or indirectly.
*/
private Template(String name, String sourceName, Configuration cfg, ParserConfiguration customParserConfiguration) {
super(toNonNull(cfg));
this.name = name;
this.sourceName = sourceName;
this.templateLanguageVersion = normalizeTemplateLanguageVersion(toNonNull(cfg).getIncompatibleImprovements());
this.parserConfiguration = customParserConfiguration != null ? customParserConfiguration : getConfiguration();
}
private static Configuration toNonNull(Configuration cfg) {
return cfg != null ? cfg : Configuration.getDefaultConfiguration();
}
/**
* Same as {
@link #Template(String, String, Reader, Configuration)} with {
@code null} {
@code sourceName} parameter.
*/
public Template(String name, Reader reader, Configuration cfg) throws IOException {
this(name, null, reader, cfg);
}
/**
* Convenience constructor for {
@link #Template(String, Reader, Configuration)
* Template(name, new StringReader(reader), cfg)}.
*
* @since 2.3.20
*/
public Template(String name, String sourceCode, Configuration cfg) throws IOException {
this(name, new StringReader(sourceCode), cfg);
}
/**
* Convenience constructor for {
@link #Template(String, String, Reader, Configuration, String) Template(name, null,
* reader, cfg, encoding)}.
*/
public Template(String name, Reader reader, Configuration cfg, String encoding) throws IOException {
this(name, null, reader, cfg, encoding);
}
/**
* Constructs a template from a character stream. Note that this is a relatively expensive operation; where higher
* performance matters, you should re-use (cache) {
@link Template} instances instead of re-creating them from the
* same source again and again. ({
@link Configuration#getTemplate(String) and its overloads already do such reuse.})
*
* @param name
* The path of the template file relatively to the (virtual) directory that you use to store the
* templates (except if {
@link #Template(String, String, Reader, Configuration, String) sourceName}
* differs from it). Shouldn't start with {
@code '/'}. Should use {
@code '/'}, not {
@code '\'}. Check
* {
@link #getName()} to see how the name will be used. The name should be independent of the actual
* storage mechanism and physical location as far as possible. Even when the templates are stored
* straightforwardly in real files (they often aren't; see {
@link TemplateLoader}), the name shouldn't be
* an absolute file path. Like if the template is stored in {
@code "/www/templates/forum/main.ftl"}, and
* you are using {
@code "/www/templates/"} as the template root directory via
* {
@link Configuration#setDirectoryForTemplateLoading(java.io.File)}, then the template name will be
* {
@code "forum/main.ftl"}. The name can be {
@code null} (should be used for template made on-the-fly
* instead of being loaded from somewhere), in which case relative paths in it will be relative to
* the template root directory (and here again, it's the {
@link TemplateLoader} that knows what that
* "physically" means).
* @param sourceName
* See {
@link #getSourceName()} for the meaning. Can be {
@code null}, in which case
* {
@link #getSourceName()} will return the same as {
@link #getName()}.
* @param reader
* The character stream to read from. It will always be closed ({
@link Reader#close()}) by
* this method (this is for backward compatibility; later major versions may discontinue this behavior).
* The {
@link Reader} need not be buffered, because this method ensures that it will be read in few
* kilobyte chunks, not byte by byte.
* @param cfg
* The Configuration object that this Template is associated with. If this is {
@code null}, the "default"
* {
@link Configuration} object is used, which is highly discouraged, because it can easily lead to
* erroneous, unpredictable behavior. (See more {
@link Configuration#getDefaultConfiguration() here...})
*
* @since 2.3.22
*/
public Template(
String name, String sourceName, Reader reader, Configuration cfg) throws IOException {
this(name, sourceName, reader, cfg, null);
}
/**
* Same as {
@link #Template(String, String, Reader, Configuration)}, but also specifies the template's encoding (not
* recommended).
*
* @param encoding
* This is the encoding that we are supposed to be using. At the first glance it's unnecessary because we
* already have a {
@link Reader} (so decoding with the charset has already happened), however, if this is
* non-{
@code null} and there's an {
@code #ftl} header with {
@code encoding} parameter, they must match,
* or else a {
@link WrongEncodingException} is thrown. Thus, it should be set if to decode the template,
* we were using an encoding (a charset), otherwise it should be {
@code null}. It's also kept as
* meta-info (returned by {
@link #getEncoding()}). It also has an impact when {
@code #include}-ing or
* {
@code #import}-ing another template from this template, as its default encoding will be this. But
* this behavior of said directives is considered to be harmful, and will be probably phased out.
*
* @since 2.3.22
*/
public Template(
String name, String sourceName, Reader reader, Configuration cfg, String encoding) throws IOException {
this(name, sourceName, reader, cfg, null, encoding);
}
/**
* Same as {
@link #Template(String, String, Reader, Configuration, String)}, but also specifies a
* {
@link TemplateConfiguration}. This is mostly meant to be used by FreeMarker internally, but advanced users might
* still find this useful.
*
* @param customParserConfiguration
* Overrides the parsing related configuration settings of the {
@link Configuration} parameter; can be
* {
@code null}. This is useful as the {
@link Configuration} is normally a singleton shared by all
* templates, and so it's not good for specifying template-specific settings. (While {
@link Template}
* itself has methods to specify settings just for that template, those don't influence the parsing, and
* you only have opportunity to call them after the parsing anyway.) This objects is often a
* {
@link TemplateConfiguration} whose parent is the {
@link Configuration} parameter, and then it
* practically just overrides some of the parser settings, as the others are inherited from the
* {
&