你还在使用 Java 服务器页面(俗称JSP)吗?我曾经也是,但是几年前我抛弃了它们,并且再也没有用过JSP了。JSP 是个很好的概念,但是它却剥夺了 web 开发的乐趣。 对我而言,这些都是小事,比如无法在页面模板上使用单独的文件header.jsp 和 footer.jsp,不能调用表达式语言的方法,在运行时无法合并,重新排列页面的各个部分。所以我转而使用 FreeMarker 模板。FreeMarker 已经存在一段时间了,如果你最近没有关注过 FreeMarker 的话,那这有些建议给你,让你考虑下个 web 应用使用 FreeMarker。
如果你已经开发Java Web应用程序一段时间,那么对于 JVM 的 PermGen 问题可能并不陌生。由于 FreeMarker 模板不编译成类,它们不占用 PermGen 空间,并不需要一个新的类加载器加载。
直接从数据源加载页面和模板岂不是很好?也许从 CMS 或数据库。也许你只想把它们放在一个地方,可以不重新部署整个应用程序就能更新它们。那么在 JSP 中你是很难做到这一点的,但 FreeMarker 提供的模板加载器就是为了 这个目的。你可以使用内建类或者创建你自己的实现。
FreeMarker 也可以将 多个加载器 链在一起形成一个系列模板装载器。我通常使用 WebappTemplateLoader 指向 WEB—INF 下一个内容文件夹。
Configuration configuration = new Configuration(); configuration.setTemplateLoader( new WebappTemplateLoader(servletContext, "WEB-INF/content"));
FreeMarker 能让你创建真正的模板,而不只是片段 ,还记得 JSP 中的 header 和 footer 吗?FreeMarker 允许你使用一个模板(在本例中为 head.ftl)
<head> <title>${title}</title> </head>
并将其添加到另一个模板(site.ftl body区域)。
<html> ${body} </html>
可以以编程的方式选择哪个模板进入 body 区。还可以添加多个模板一起放入同一区域。甚至可以将字符串值或计算的值放入 body 区域。在 JSP 中试试做到这些。
JSP 要求你导入每个你需要使用的类,就像一个常规的 Java 类一样。FreeMarker 模板,嗯,仅仅是模板。可以被包括在另一个模板中,但目前还不需要导入类。
使用 Jsp 的一个理由是有可用性很好的标签库。好消息是 FreeMarker 支持 JSP 标签。坏消息是它们使用 FreeMarker 的语法,不是 JSP 语法。
除非你的目标是 Servlet 3.0/El 2.2 标准的容器,那么表达式语言中方法调用是不可用的。不是所有人都同意 EL 表达式中方法调用是一件好事,但是当你需要它们的时候,使用 JSP 真的太痛苦了。 但是 FreeMarker 同等对待其中每个引用。
${customer.address.country}
${customer.getAddress().country}
FreeMarker 和 Jsp 都可以在表达式语言中处理空值,但 FreeMarker 在可用性上更先进一些。
Invoice Date: ${(customer.invoice.date)!}
感叹号告诉 FreeMarker 对表达式自动检查 null 值和空字符串。如果 customer、invoice 或者 date 中有一个为空值或空字符串,你只会得到标签:
Invoice Date:
另一个选择是感叹号后包括你的默认值。
Invoice Date: ${(customer.invoice.date)!'No Invoice Available'}
如果所有值丢失,你会得到:
Invoice Date: No Invoice Available
请参见 处理缺少的值 了解更多细节。
FreeMarker 的共享变量是我最喜欢的“隐藏”功能之一。此功能可以让你设置自动添加到所有模板的值。 例如,可以设置应用程序的名称作为共享变量。
Configuration configuration = new Configuration(); configuration.setSharedVariable("app", "StackHunter");
然后像任何其他变量一样访问它。
App: ${app}
在过去使用共享变量一般引用资源包 然后使用像 ${i18n.resourceBundle.key} 这样的表达式来获取值。
${i18n.countries.CA} ${i18n.countries['CA']} ${i18n.countries[countryCode]}
上面这些行都引用 countries_en.properties 资源包内的 key “CA”对应的值。你需要执行自己的 TemplateHashModel,然后将其添加为一个共享变量来实现这一目标。
FreeMarker 内置 JSON 支持。 比方说你有以下的 JSON 存储到变量命名 user 的字符串中。
{ 'firstName': 'John', 'lastName': 'Smith', 'age': 25, 'address': { 'streetAddress': '21 2nd Street', 'city': 'New York', 'state': 'NY', 'postalCode': 10021 }}
使用 ?eval 将从字符串转换为一个 JSON 对象,然后像其他数据一样在表达式中使用。
<#assign user = user?eval> User: ${user.firstName}, ${user.address.city}
最后,与 JSP 不同的是FreeMarker 模板可以在 servlet 容器之外使用。可以使用它们来生成电子邮件、 配置文件、 XML 映射等。你甚至可以使用它们来生成 web 页 并将它们保存在服务器端的缓存中。 请在下一个 web 项目尝试使用 FreeMarker把 web 开发的乐趣给找回来。