022_配置configuration

1. 配置(configuration)就是freemarker.template.Configuration对象, 它存储了常用(全局, 应用程序级)的设置, 定义了想要在所有模板中可用的变量(称为共享变量)。而且, 它会处理Template实例的新建和缓存。

2. 运行中的模板会受配置设置的影响, 每个Template实例都有和它相关联的Configuration实例。通常可以使用Configuration.getTemplate(而不是直接调用Template的构造方法)来获得Template实例, 此时, 关联的Configuration实例就是调用getTemplate方法的。

3. 共享变量

3.1. Shared variables(共享变量)是为所有模板定义的变量。可以使用setSharedVariable方法向配置中添加共享变量:

Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
...
cfg.setSharedVariable("warp", new WarpDirective());
cfg.setSharedVariable("company", "Foo Inc.");

3.2. 在所有使用这个配置的模板中, 名为wrap的用户自定义指令和一个名为company的字符串将会在数据模型的根root上可见, 那就不用在根哈希表上一次又一次的添加它们。在传递给Template.process的根root对象里的变量将会隐藏同名的共享变量。

3.3. 出于向后兼容的特性, 共享变量的集合初始化时(就是对于新的Configuration实例来说)不能为空。它包含下列用户自定义指令(用户自定义指令使用时需要用@来代替#):

4. 配置设置

4.1. Settings(配置设置)是影响FreeMarker行为的已被命名的值。配置设置有很多, 例如: locale, number_format, default_encoding, template_exception_handler等。

4.2. 配置设置存储在Configuration实例中, 可以在Template实例中被覆盖。比如: 在配置中给locale设置为"en_US", 那么使用该配置的所有模板中的locale都使用"en_US",  除非在模板中locale被明确地设置成其它不同的值(比如: "zh_CN")。因此, 在Configuration中的值充当默认值, 这些值在每个模板中也可以被覆盖。

4.3. 在Configuration或Template实例中的值也可以在单独调用Template.process方法后被覆盖。对于每个调用了freemarker.core.Environment对象的值在内部创建时就持有模板执行的运行时环境, 也包括了那个级别的设置信息。在模板执行时, 那里存储的值也可以被改变。

4.4. 配置信息可以被想象成3层(Configuration, Template, Environment), 最高层包含特定的值, 它为设置信息提供最有效的值。比如(设置信息A到F仅仅是为这个示例而构想的):

4.4.1. 配置信息的有效值为: A=1, B=2, C=3, D=1, E=2。而F的设置则是null, 或者在你获取它的时候将抛出异常。

4.5. Configuration层设置配置信息

4.5.1. Configuration层原则上设置配置信息时使用Configuration对象的setter方法, 例如:

Configuration myCfg = new Configuration(Configuration.VERSION_2_3_23);
myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
myCfg.setDefaultEncoding("UTF-8");

4.5.2. 在真正使用Configuration对象(通常在初始化应用程序时)之前来配置它, 后面必须将其视为只读的对象。

4.5.3. 在实践中, 比如很多Web应用框架中, 就应该使用这种框架特定的配置方式来进行配置, 比如使用成对的String来配置(像在 .properties属性配置文件中那样)。在这种情况下, 框架的作者大多数使用Configuration对象的setSetting(String name, String value)方法。

4.5.4. 而在Spring框架中, 我们可以这样进行:

<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	<property name="freemarkerSettings">
    	<props>
      		<prop key="incompatible_improvements">2.3.23</prop>
      		<prop key="template_exception_handler">rethrow</prop>
      		<prop key="default_encoding">UTF-8</prop>
		</props>
  	</property>
</bean>

4.6. Template层设置配置信息, 应该使用Configuration.getTemplate(...)获取模板, 然后进行配置设置。

4.7. Environment层设置配置信息

4.7.1. 使用Java API: 使用Environment对象的setter方法。当然想要在模板执行之前来做, 然后当调用myTemplate.process(...)时会遇到问题, 因为在内部创建Environment对象后立即就执行模板了, 导致没有机会来进行设置。这个问题的解决可以用下面两个步骤进行:

Environment env = myTemplate.createProcessingEnvironment(root, out);
env.setLocale(java.util.Locale.ITALY);
env.setNumberFormat("0.####");
env.process();

4.7.2. 在模板中(通常这被认为是不好的做法)直接使用setting指令, 例如:

<#setting locale="it_IT">
<#setting number_format="0.####">

5. 模板加载器

5.1. 模板加载器是加载基于抽象模板路径下, 比如: "index.ftl"或"products/catalog.ftl"的原生文本数据对象。

5.2. 当调用cfg.getTemplate(这里的cfg就是Configuration实例)时, FreeMarker询问模板加载器是否已经为cfg建立返回给定模板路径的文本, 之后FreeMarker解析文本生成模板。

5.3. 内建模板加载器

5.3.1. 在Configuration中可以使用下面的方法来方便建立三种模板加载。(每种方法都会在其内部新建一个模板加载器对象, 然后创建Configuration实例来使用它。)

void setDirectoryForTemplateLoading(File dir);
或
void setClassForTemplateLoading(Class cl, String prefix);
或
void setServletContextForTemplateLoading(Object servletContext, String path);

5.3.2. 模板加载器接口

5.3.3. 第一种方法在磁盘的文件系统上设置了一个明确的目录, 它确定了从哪里加载模板。不要说可能, File参数肯定是一个存在的目录。否则, 将会抛出异常。

5.3.4. 第二种调用方法使用了一个Class类型的参数和一个前缀。这是让你来指定什么时候通过相同的机制来加载模板, 不过是用Java的ClassLoader来加载类。这就意味着传入的class参数会被Class.getResource()用来调用方法来找到模板。参数prefix是给模板的名称来加前缀的。

5.3.5. 第三种调用方式需要Web应用的上下文和一个基路径作为参数, 这个基路径是Web应用根路径(WEB-INF目录的上级目录)的相对路径。那么加载器将会从Web应用目录开始加载模板。

5.4. 从多个位置加载模板

5.4.1. 如果需要从多个位置加载模板, 那就不得不为每个位置都实例化模板加载器对象, 将它们包装到一个称为MultiTemplateLoader的特殊模板加载器, 最终将这个加载器传递给Configuration对象的setTemplateLoader(TemplateLoader loader)方法。下面给出一个使用类加载器从两个不同位置加载模板的示例:

FileTemplateLoader ftl1 = new FileTemplateLoader(new File("/tmp/templates"));
FileTemplateLoader ftl2 = new FileTemplateLoader(new File("/usr/data/templates"));
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "");
TemplateLoader[] loaders = new TemplateLoader[] { ftl1, ftl2, ctl };
MultiTemplateLoader mtl = new MultiTemplateLoader(loaders);

cfg.setTemplateLoader(mtl);

5.4.2. 现在, FreeMarker将会尝试从/tmp/templates目录加载模板, 如果在这个目录下没有发现请求的模板, 它就会继续尝试从/usr/data/templates目录下加载, 如果还是没有发现请求的模板, 那么它就会使用类加载器来加载模板。

5.4.3. 多位置模板加载器

5.5. 从其他资源加载模板

5.5.1. 如果内建的类加载器都不适合使用, 那么就需要来编写自己的类加载器了, 这个类需要实现freemarker.cache.TemplateLoader接口, 然后将它传递给Configuration对象的setTemplateLoader(TemplateLoader loader)方法。

5.5.2. 如果模板需要通过URL访问其他模板, 那么就不需要实现TemplateLoader接口了, 可以选择子接口freemarker.cache.URLTemplateLoader来替代, 只需实现URL getURL(String templateName)方法即可。

5.6. 模板缓存

5.6.1. FreeMarker是会缓存模板的(假设使用Configuration对象的方法来创建Template对象)。这就是说当调用getTemplate方法时, FreeMarker不但返回了Template对象, 而且还会将它存储在缓存中, 当下一次再以相同(或相等)路径调用getTemplate方法时, 那么它只返回缓存的Template实例, 而不会再次加载和解析模板文件了。

5.6.2. 如果更改了模板文件, 当下次调用模板时, FreeMarker将会自动重新载入和解析模板。然而, 要检查模板文件是否改变内容了是需要时间的, 有一个Configuration级别的设置被称作"更新延迟", 它可以用来配置这个时间。这个时间就是从上次对某个模板检查更新后, FreeMarker再次检查模板所要间隔的时间。其默认值是5秒。如果想要看到模板立即更新的效果, 那么就要把它设置为0。要注意某些模板加载器也许在模板更新时可能会有问题。例如, 典型的例子就是在基于类加载器的模板加载器就不会注意到模板文件内容的改变。

5.6.3. 如果Java虚拟机认为会有内存溢出时, 默认情况它会从缓存中移除任意模板。此外, 你还可以使用Configuration对象的clearTemplateCache方法手动清空缓存。

5.6.4. 何时将一个被缓存了的模板清除的实际应用策略是由配置的属性cache_storage来确定的, 通过这个属性可以配置任何CacheStorage的实现。对于大多数用户来说, 使用freemarker.cache.MruCacheStorage就足够了。这个缓存存储实现了二级最近使用的缓存。在第一级缓存中, 组件都被强烈引用到特定的最大数目(引用次数最多的组件不会被Java虚拟机抛弃, 而引用次数很少的组件则相反)。当超过最大数量时, 最近最少使用的组件将被送至二级缓存中, 在那里它们被很少引用,  直到达到另一个最大的数目。引用强度的大小可以由构造方法来指定。例如, 设置强烈部分为20, 轻微部分为250:

cfg.setCacheStorage(new freemarker.cache.MruCacheStorage(20, 250))
或者
cfg.setSetting(Configuration.CACHE_STORAGE_KEY, "strong:20, soft:250");

5.6.5. 当创建了一个新的Configuration对象时, 它使用一个strongSizeLimit值为0的MruCacheStorage缓存来初始化, softSizeLimit的值是Integer.MAX_VALUE(也就是在实际中, 是无限大的)。但是使用非0的strongSizeLimit对于高负载的服务器来说也许是一个更好的策略, 对于少量引用的组件来说, 如果资源消耗已经很高的话, Java虚拟机往往会引发更高的资源消耗, 因为它不断从缓存中抛出经常使用的模板, 这些模板还不得不再次加载和解析。

6. 错误控制

6.1. 关于FreeMarker可能发生的异常, 可以分为如下几类:

6.1.1. 当配置 FreeMarker 时发生异常。

6.1.2. 当加载和解析模板时发生异常: 调用了Configuration.getTemplate(...)方法, FreeMarker就要把模板文件加载到内存中然后来解析它。在这期间, 有两种异常可能发生:

  • 因模板文件没有找到而发生的IOException异常, 或在读取文件时发生其他的I/O问题。比如没有读取文件的权限, 或者是磁盘错误。这些错误的发出者是TemplateLoader对象。
  • 根据FTL语言的规则, 模板文件发生语法错误时会导致freemarker.core.ParseException异常。当获得Template对象(Configuration.getTemplate(...))时, 这种错误就会发生, 而不是当执行(Template.process(...))模板的时候。这种异常是IOException的一个子类。

6.1.3. 当执行(处理)模板时发生的异常, 也就是当调用了Template.process(...)方法时会发生的两种异常:

  • 当试图写入输出对象时发生错误而导致的IOException异常。
  • 当执行模板时发生的其它问题而导致的freemarker.template.TemplatException异常。比如: 一个频繁发生的错误, 就是当模板引用一个不存在的变量。默认情况下, 当TemplatException异常发生时, FreeMarker会用普通文本格式在输出中打印出FTL的错误信息和堆栈跟踪信息, 然后再次抛出TemplatException异常而中止模板的执行, 就可以捕捉到Template.process(...)方法抛出的异常了。而这种行为是可以定制的。FreeMarker也会经常写TemplatException异常的日志。

6.2. TemplateException异常在模板处理期间的抛出是由freemarker.template.TemplateExceptionHandler对象控制的, 这个对象可以使用setTemplateExceptionHandler(...)方法配置到Configuration对象中。

6.3. TemplateExceptionHandler对象只包含一个handleTemplateException方法和几个预定义的异常处理方式:

6.4. 无论TemplateException异常什么时候发生, handleTemplateException这个方法都会被调用。异常处理是传递的te参数控制的, 模板处理的运行时(Runtime)环境可以访问env变量, 处理器可以使用out变量来打印输出信息。如果方法抛出异常(通常是重复抛出te), 那么模板的执行就会中止, 而且Template.process(...)方法也会抛出同样的异常。如果handleTemplateException对象不抛出异常, 那么模板将会继续执行, 就好像什么也没有发生过一样, 但是引发异常的语句将会被跳过。当然, 控制器仍然可以在输出中打印错误提示信息。

6.5. FreeMarker本身带有这些预先编写的错误控制器:

6.5.1. TemplateExceptionHandler.DEBUG_HANDLER: 打印堆栈跟踪信息(包括FTL错误信息和FTL堆栈跟踪信息)和重新抛出的异常。这是默认的异常控制器(也就是说, 在所有新的Configuration对象中, 它是初始化好的)。

6.5.2. TemplateExceptionHandler.HTML_DEBUG_HANDLER: 和DEBUG_HANDLER相同, 但是它可以格式化堆栈跟踪信息, 那么就可以在Web浏览器中来阅读错误信息。当你在制作HTML页面时, 建议使用它而不是DEBUG_HANDLER。

6.5.3. TemplateExceptionHandler.IGNORE_HANDLER: 简单地压制所有异常(但是要记住, FreeMarker仍然会写日志)。它对处理异常没有任何作用, 也不会重新抛出异常。

6.5.4. TemplateExceptionHandler.RETHROW_HANDLER: 简单重新抛出所有异常而不会做其它的事情。这个控制器对Web应用程序(假设你在发生异常之后不想继续执行模板)来说非常好,  因为它在生成的页面发生错误的情况下, 给了你很多对Web应用程序的控制权(因为FreeMarker不向输出中打印任何关于该错误的信息)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值