当前位置:   article > 正文

我与Freemarker二三事_freemarker首字母大写

freemarker首字母大写

最近工作不是很忙,所以想利用这段空闲时间学习学习。之前在微信上看到一篇关于Freemarker的文章(点击这里查看),加之现在的项目中使用到了Freemarker,当时为了急于完成在网上找了些速成的资料,所以我决定利用这段时间学习学习Freemarker。阅读了官方文档,总结了一些自己觉得还挺有用的东西,整理在这里。

一、Freemarker是个啥玩意儿?

官方文档上这么说:

Freemarker是一个模板引擎,基于模板和交换数据的用于创建文本输出的工具。作为一个java类库,可以作为一个组件供程序员嵌入他们的产品中。

例如,众所周知的MVC架构模式,在这其中Freemarker的作用其实相当于JSP,可以作为view层进行数据的展示。

既然可以MVC了,那么view层和controller层就可以分开开发了,所以我们需要了解一下模板设计者和程序设计者都需要干点啥。

二、模板设计者需要干点啥?

官方文档上这么说:

模板+数据模型=输出。

所以一个有点牛逼的模板设计者需要知道下面的这些东西。

1.关于模板那些事

Freemarker的模板使用的是FTL(FreemarkerTag Language),类似于HTML的语法,归纳如下:

1)模板上有点啥?

${}:表达式输出,使用${}会将其中的内容进行输出

FTL tags:标签。与html标签类似,但是必须以#开头,例如<#list></#list>。Freemarker自带的标签以#开头,用户自定义的标签以@开头。

注释:与html注释类似,使用<#-- -->,但与html注释不同的是,html注释会将注释体也输出到页面上,但是freemarker不会输出注释体。

在模版中,只要不是${},FTL tags,注释的内容,freemarker都会认为其是静态的文本,会直接将其输出。

2)模板咋写啊?

通过第一点可以知道Freemarker类似于HTML,但是它又与HTML有些许的不同,Freemarker有自己的一些语法,总结如下:

i.If判断:

语法:

  1. <#if expression>
  2. <#elseif expression >
  3. <#else>
  4. </#if>

ii.集合迭代:

  1. <#list 集合名 as 变量名>
  2. ${变量名.属性}
  3. </#list>

如果集合里元素为0,那么迭代集合的时候,依然会输出list标签中的内容。例如:

  1. <#list studentsList as student>
  2. <h1>id:${student.id}</h1>
  3. <h1>name:${student.name}</h1>
  4. </#list>

如果studentsList没有元素,那么依旧会有如下输出

id:

name:

所以为了避免这一问题。可以使用如下的表示:

  1. <#list studentsList>
  2. <#items as student>
  3. <h1>id:${student.id}</h1>
  4. <h1>name:${student.name}</h1>
  5. </#items>
  6. </#list>

如果集合元素为空,那么还可以使用<#else>来进行为空时的操作,这时就会去do something,例如:

  1. <#list studentsList>
  2. <#items as student>
  3. <h1>id:${student.id}</h1>
  4. <h1>name:${student.name}</h1>
  5. </#items>
  6. <#else>
  7. Do Something…
  8. </#list>


可以使用<#sep>something</#sep>来分割输出的内容。例如:

<p>Fruits: <#list misc.fruits as fruit>${fruit}<#sep>, </#list>

那么就会将输出的水果用逗号分开,like <p>Fruits: orange, banana

对于集合而言他有下面的属性:

集合本身有以下属性:
index:索引,从0开始
counter:计数,从1开始
item_parity:奇偶性,返回当前counter的奇偶性

iii.引用<#include url>

使用这个标签,可以引入外部的另一段内容到当前的文件中。

iv.内嵌函数:

Freemarker内置了很多方便好用的函数,不同于java的以.调用,freemarker以?调用。例如:

upper_case:大写转换
cap_first:首字母大写
length:获取长度
size:获取集合大小

3)要是有变量找不到咋整?

在freemarker中,一个不存在的变量a和a被赋值为null是一样的。
在freemarker中使用!为其设定一个默认值来解决变量缺失的问题。例如:
<h1>Welcome,${user!”visitor”}</h1>
如果变量user缺失了,那么模版引擎在解析到这里时会输出visitor。如果user没有缺失,那么会依旧只输出user的变量值。
通过.来调用属性,例如animal.python.price,如果使用!来为其设置默认值,需要将整个表达式都括起来,像是这样(animal.python.price)!0。因为如果animal或者python缺失了,那么freemarker解析到这里时就会抛出未定义变量的错误。一旦将其括起来作为一个整体了,那么即使animal或者python缺失了,那么也会返回设置的默认值0。

4)一个很重要但是很简单的东西,数据模型,data-model:

Freemarker的数据模型其实就是一个hash,可以将其理解为容器,存放了很多的变量与值,和java的collection是一个概念。

5)FreeMarker的几员虾兵蟹将(数据类型):

Freemarker中的数据类型和其他编程语言的变量类型类似,但是它还可以为一个变量设置多个类型。例如,mouse既可以是字符串类型,又可以是一个hash。就像是这样,mouse=”Jerry”,mouse.size=”small”,mouse.price=20。那么有如下的输出:
${mouse}作为字符串输出为Jerry,${mouse.size}作为hash输出为small,${mouse.prince}作为hash输出为20。

i.基本变量类型:

字符串
--使用索引来获取字符串的单个字符,例如:user=”Tom”,user[0]就输出T。
--使用字符串+range的形式来获取子字符串。例如:a=”ABCDEF”,a[1..3]就输出BCD
--字符串操作可以使用+来进行拼接。但是可能会因为数值格式,日期格式等因素而导致拼接的结果不正确,或者解析异常。例如:”url?id”+id有可能会出现url?id=1 234的情况。为了避免这种情况的发生,可以使用Freemarker内置的函数?c来将其进行转换,就像是这样,”url?id”+id?c或者”url?id=${id?c}”。
--数字字符串不能与数值进行运算。只能使用+操作进行字符串拼接。

数值

--数值表达0.02时,必须有小数点前的0,例如.02是错误的。

布尔类型Boolean

日期类型Date

ii.容器:

hash

--使用+连接多个hash

序列:类似于java的list,序列中的元素以逗号分隔

--在序列中会使用到range,有以下用法:
start..end:含头含尾
start..<end或者start..!end:含头不含尾
start..*length:从start开始,找到length个元素。length为正向后查找,反之向前查找
--使用+连接多个序列。例如[1,2]+[4,5]生成的新序列就是[1,2,4,5]。
--使用序列[range]的形式来获取子序列。例如:<#list [1,2,3,4,5][1..3] as i>${i}</#list>输出的就是[2,3,4]

集合:类似于java的map

6)还有一些面包渣:

i.关于range:

--range表达式本身不用带方括号,只有跟在序列后才需要方括号。
例如:<#assgin myRange=0..<x>是正确的。
<#assgin myRange=[0..<x]>是不正确的。
<#assgin mySeq[0..<x]>是正确的。
--range表达式操作符两边可以进行计算,而不需要将两边的计算使用圆括号包裹起来。
例如,x+1..y-2
--操作符是一体的,他们之间不能有空格。
--range并不真正存储它所包含的数值,因此0..1和0..100000000在创建和内存消耗上是一样的。

ii.Freemarker Template的结构:
包含文本,插值,FTL标签和注释。
FTL区分大小写。
FTL标签内部不允许再使用标签,例如:
<#if <#include “foo”>=”bar”>…</#if>,这是错误的用法。
注释可以写在任何正常的位置,只要保持正确的注释格式即可。

iii.注意:
--最好是将复杂的表达式用括号括起来,以便阅读和理解。
--对于一个负的序列索引,Freemarker将会解析错误。
--使用??来判断一个值是否为空,返回true和false两个值。
--如果向自定义标签传值,不需要${},直接将值的变量代入即可。

三、程序开发者需要干点啥?

首先介绍两个非常重要的类:

freemarker.template.Configuration:模板,环境等的配置对象。

freemarker.template.Template:模板对象

首先来看一段代码:

  1. public class MyTest {
  2. private Configuration configuration=new Configuration(Configuration.VERSION_2_3_23);
  3. private Template template=null;
  4. /*
  5. * configuration加载模版的三种方式:
  6. 1.根据类路径加载模版
  7. public void setClassForTemplateLoading(Class clazz, String pathPrefix);
  8. 2.根据文件系统加载模版
  9. public void setDirectoryForTemplateLoading(File dir) throws IOException;
  10. 3.根据servlet上下文加载模版
  11. public void setServletContextForTemplateLoading(Object servletContext, String path);
  12. *
  13. */
  14. public MyTest() {
  15. configuration.setClassForTemplateLoading(MyTest.class, "/config/template");
  16. configuration.setDefaultEncoding("UTF-8");
  17. //开发阶段使用
  18. configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
  19. /*
  20. * 生产阶段使用
  21. configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
  22. */
  23. try {
  24. //通过setSharedVariable可以将一些变量设置为公共的共享的变量,在任何使用该configuration获取template的模版中都可以访问共享变量
  25. configuration.setSharedVariable("author", "Earl");
  26. } catch (TemplateModelException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. /**
  31. * 获取模板
  32. * @param templatePath
  33. * @return
  34. * @throws TemplateNotFoundException
  35. * @throws MalformedTemplateNameException
  36. * @throws ParseException
  37. * @throws IOException
  38. */
  39. private Template getTemplate(String templatePath) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException{
  40. template=configuration.getTemplate(templatePath);
  41. return template;
  42. }
  43. /**
  44. * 向模板传递集合,迭代生成xml文件
  45. */
  46. @Test
  47. public void testManyPerson(){
  48. List<Person> persons=new ArrayList<Person>();
  49. Person p1=new Person("王一", 1, 20);
  50. Person p2=new Person("张二", 2, 21);
  51. Person p3=new Person("刘三", 1, 22);
  52. Person p4=new Person("李四", 2, 23);
  53. Person p5=new Person("赵五", 2, 24);
  54. Person p6=new Person("孙六", 1, 25);
  55. Person p7=new Person("杨七", 2, 26);
  56. Person p8=new Person("孔八", 1, 27);
  57. persons.add(0,p1);
  58. persons.add(1,p2);
  59. persons.add(2,p3);
  60. persons.add(3,p4);
  61. persons.add(4,p5);
  62. persons.add(5,p6);
  63. persons.add(6,p7);
  64. persons.add(7,p8);
  65. Map<String, Object> personMap=new HashMap<String, Object>();
  66. personMap.put("persons", persons);
  67. templateProcess(personMap, "d://persons.xml", "person_list_test.flt");
  68. }
  69. /**
  70. * 向模板传递一个对象,测试${}
  71. */
  72. @Test
  73. public void testOnePerson(){
  74. Person p1=new Person("王麻子", 1, 20);
  75. Map<String, Object> personMap=new HashMap<String, Object>();
  76. personMap.put("person", p1);
  77. templateProcess(personMap,"d://person.xml","person_test.flt");
  78. }
  79. /**
  80. * 向模板传递一个集合,并将模板返回的html代码输出
  81. */
  82. @Test
  83. public void testHtml(){
  84. List<Person> persons=new ArrayList<Person>();
  85. Person p1=new Person("王一", 1, 20);
  86. Person p2=new Person("张二", 2, 21);
  87. Person p3=new Person("刘三", 1, 22);
  88. Person p4=new Person("李四", 2, 23);
  89. Person p5=new Person("赵五", 2, 24);
  90. Person p6=new Person("孙六", 1, 25);
  91. Person p7=new Person("杨七", 2, 26);
  92. Person p8=new Person("孔八", 1, 27);
  93. persons.add(0,p1);
  94. persons.add(1,p2);
  95. persons.add(2,p3);
  96. persons.add(3,p4);
  97. persons.add(4,p5);
  98. persons.add(5,p6);
  99. persons.add(6,p7);
  100. persons.add(7,p8);
  101. Map<String, Object> personMap=new HashMap<String, Object>();
  102. personMap.put("persons", persons);
  103. personMap.put("title", "person");
  104. StringWriter sw = templateProcess(personMap,"html_test.flt");
  105. System.out.println(sw.toString());
  106. }
  107. /**
  108. * 测试方法的调用
  109. */
  110. @Test
  111. public void testMethodCall(){
  112. String s1="hello,world";
  113. String s2="你好,世界";
  114. Map<String, Object> paraMap=new HashMap<String, Object>();
  115. paraMap.put("s1", s1);
  116. paraMap.put("s2", s2);
  117. paraMap.put("StringLength", new StringLengthMethod());
  118. System.out.println(templateProcess(paraMap, "method_call_test.flt"));
  119. }
  120. /**
  121. * 测试用户自定义标签
  122. */
  123. @Test
  124. public void testUserDirective(){
  125. String s1="hello,world";
  126. Map<String, Object> paraMap=new HashMap<String, Object>();
  127. paraMap.put("s1", s1);
  128. paraMap.put("uppercase", new UpperCaseDirective());
  129. System.out.println(templateProcess(paraMap, "user_directive_test.flt"));
  130. }
  131. /**
  132. * 将生成的内容写入字符串
  133. * @param paraMap 数据集
  134. * @param templateName 模版名称
  135. * @return 生成的内容
  136. */
  137. private StringWriter templateProcess(Map<String, Object> paraMap,String templateName) {
  138. StringWriter sw=new StringWriter();
  139. try {
  140. template=getTemplate(templateName);
  141. template.process(paraMap, sw);
  142. } catch (TemplateException e) {
  143. e.printStackTrace();
  144. } catch (IOException e) {
  145. e.printStackTrace();
  146. }finally{
  147. try {
  148. sw.close();
  149. } catch (IOException e) {
  150. e.printStackTrace();
  151. }
  152. }
  153. return sw;
  154. }
  155. /**
  156. * 将数据集与模版结合,生成文件
  157. * @param paraMap 数据集
  158. * @param outputPath 生成文件输出路径
  159. * @param templateName 模版名称
  160. */
  161. private void templateProcess(Map<String, Object> paraMap,String outputPath,String templateName) {
  162. Writer out=null;
  163. try {
  164. out=new FileWriter(outputPath);
  165. template=getTemplate(templateName);
  166. template.process(paraMap, out);
  167. } catch (IOException e) {
  168. e.printStackTrace();
  169. } catch (TemplateException e) {
  170. e.printStackTrace();
  171. }finally{
  172. if(out!=null){
  173. try {
  174. out.close();
  175. } catch (IOException e) {
  176. e.printStackTrace();
  177. }
  178. }
  179. }
  180. }
  181. }

Freemarker的开发使用其实还是蛮简单的,大概过程是使用Configuration配置模板路径,再使用Template得到模板,得到模板之后,就可以根据需求对模板进行操作了。基本的使用可以参考上面代码中的testOnePerson(),testManyPerson(),testHtml()。

下面来说说方法的调用以及用户的自定义标签。

--方法的调用:

用户自定义的方法的开发需要实现freemarker.template.TemplateMethodModelEx,并实现其exec(java.util.List arg0)。开发还是比较简单的,如下:

  1. public class StringLengthMethod implements TemplateMethodModelEx {
  2. @Override
  3. public Object exec(List arg0) throws TemplateModelException {
  4. //arg0就是调用方法的参数集合。
  5. //在模版中无需关心参数,只需将需要的参数进行传递即可
  6. //在此处根据需要取得集合中的参数
  7. String paraStr=(String) arg0.get(0).toString();
  8. SimpleNumber length=new SimpleNumber(paraStr.length());
  9. return length;
  10. }
  11. }

在使用时,需要将方法对象传递到模板上,可参考如下的代码:

  1. /**
  2. * 测试方法的调用
  3. */
  4. @Test
  5. public void testMethodCall(){
  6. String s1="hello,world";
  7. String s2="你好,世界";
  8. Map<String, Object> paraMap=new HashMap<String, Object>();
  9. paraMap.put("s1", s1);
  10. paraMap.put("s2", s2);
  11. paraMap.put("StringLength", new StringLengthMethod());
  12. System.out.println(templateProcess(paraMap, "method_call_test.flt"));
  13. }

同时在模板上如下使用即可:${StringLength(s1)}

这样的话就可以通过${}输出字符串s1的长度了。

--用户自定义标签:

用户自定义标签类需要实现freemarker.template.TemplateDirectiveModel,并实现其execute(Environment arg0,Map arg1, TemplateModel[] arg2, TemplateDirectiveBody arg3)方法,如下:

  1. public class UpperCaseDirective implements TemplateDirectiveModel {
  2. @Override
  3. public void execute(Environment arg0, Map arg1, TemplateModel[] arg2,
  4. TemplateDirectiveBody arg3) throws TemplateException, IOException {
  5. //arg1为自定义标签的属性,key存储的是属性名,value存储的是相应的值。
  6. //arg0为系统环境变量,用于输出
  7. //arg3为标签体,生成标签
  8. //arg2为循环代替变量
  9. String attr=arg1.get("name").toString();
  10. //官方文档推荐使用DefaultObjectWrapper或者用户自定义的继承自DefaultObjectWrapper的子类wrapper
  11. //官方文档不推荐使用DefaultObjectWrapper的构造方法创建wrapper对象,推荐使用DefaultObjectWrapperBuilder,以保证wrapper为单例
  12. DefaultObjectWrapper wrapper=new DefaultObjectWrapperBuilder(new Version("2.3.23")).build();
  13. arg0.setVariable("upperName", wrapper.wrap(attr.toUpperCase()));
  14. arg3.render(arg0.getOut());
  15. }
  16. }

在使用时,同样需要将自定义标签对象传递到模板上,如下:

  1. /**
  2. * 测试用户自定义标签
  3. */
  4. @Test
  5. public void testUserDirective(){
  6. String s1="hello,world";
  7. Map<String, Object> paraMap=new HashMap<String, Object>();
  8. paraMap.put("s1", s1);
  9. paraMap.put("uppercase", new UpperCaseDirective());
  10. System.out.println(templateProcess(paraMap, "user_directive_test.flt"));
  11. }

同时在模板上使用<@uppercase name=s1>${upperName}</@uppercase>,upperName就是在自定义标签类中处理完成输出的数据。使用用户自定义的标签,一定是以@开头,不是以#开头了。

 

总结:以上便是我最近一小段时间对Freemarker的一些了解。本文开头我提到的那篇关于Freemarker的文章,里面对比了Freemarker和jsp。其实在我看来,实际应用中,解决问题的办法很多,没有必要非得说哪个更好,两者在解决各自擅长的问题上都是有很大的优势的。但是可能我的认识里,我感觉在我们伟大的祖国应该是鲜有公司使用Freemarker作为jsp的替代品的,毕竟中国的程序员是需要前端后端通吃的。这就勾起了我曾经看过的一篇关于印度程序员的文章的回忆。为何说印度程序员很多,抛开英语是他们的第二语言,就他们本身在工作中的角色而言,他们的分工是很明确的。做前端的就是做前端的,写后台的就是写后台的,他们只需要关心自己的那一部分就好了。但是中国的程序员必须两者兼顾,所以就我目前的工作经验来说,我觉得使用jsp来作为展示,开发调试更加的方便和快捷,而Freemarker用来做一些小的,例如生成一个邮件模板等我觉得更为合适。

源代码和模板可以点击这里下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/658248
推荐阅读
相关标签
  

闽ICP备14008679号