zoukankan      html  css  js  c++  java
  • Freemaker 开发学习笔记

    Freemaker 是一个强大的模板引擎,相比 velocity 而言,其强大的过程调用、递归和闭包回调功能让 freemaker 可以完成几乎所有我们所想的功能。从个人看法而言,freemaker 完全有能力作为 MDA 的代码辅助生成工具。
    
    本文试图越过传统的概念性介绍,通过一组例子直接把读者带入到 Freemaker 应用的较高层阶。
    
    大家看文章标题就应该知道,我想用一篇文章,把大家从对 freemaker 的陌生直接带入到比较深入的境界,所以不想说一些基础性的东西,如果大家不习惯我的表达方法,大可通过 google 去找习惯于自己阅读方式的相关文章。
    我用过 velocity,最近才用 freemaker,才知道我以前的选择是错了,因为 velocity 不支持过程的调用,所以我为 velocity 增加了很多的东西,写了很多代码,而且脚本也累赘得要命。freemaker 首先吸引我的是它强大的过程调用和递归处理能力,其次则是 xml 风格的语法结构有着明显的边界,不象 velocity 要注意段落之间要留空格。所以我建议大家直接使用 Freemaker,虽然 freemaker 没有. net 版本,我想不嵌入程序中使用的话,freemaker 是绝对的首选。(题外话,谁有兴趣移植一个 NFreeMaker?)
    在使用之前我们先要设置运行环境,在使用 Freemaker 的时候,我们需要下载相关的程序:
    
    freemaker: http://freemarker.sourceforge.net/
    fmpp: http://fmpp.sourceforge.net/
    其中 fmpp 是一个 freemaker 的辅助工具,有了它,我们可以实现更多的功能。以下例子必须 fmpp 辅助。
    这里我们首先提出问题。大家看如下的一个 xml 文件,虽然 freemaker 的能力不仅在于处理 xml 文件,但是用 xml 作为例子更直观一些:
    
    <?xml version='1.0' encoding="gb2312" ?>
    <types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:DruleForm-Lite.xsd">
             <type name="Type1" >
                 <labels>
                     <label lang="zh-CN" value="投保单"/>
                 </labels>
                 <field name="Field11" type="Float" lbound="1" ubound="1" >
                     <labels>
                         <label lang="zh-CN" value="投保单ID"/>
                     </labels>
                 </field>
                 <field name="Field12" type="String" lbound="1" ubound="*"/>
                 <field name="Field13" type="Integer" lbound="1" ubound="*"/>
                 <field name="Field14" type="Type2" lbound="1" ubound="*">
                     <type name="Type2">
                         <field name="Field21" type="String" lbound="1" ubound="*"/>
                         <field name="Field22" type="Integer" lbound="1" ubound="*"/>    
                     </type>
                 </field>
                 <field name="Field15" type="InsuranceProduct" lbound="1" ubound="*"/>
             <type>
             <type name="Type3">
                 <field name="Field31" type="Type1" lbound="1" ubound="*" />
             </type>
         </types>
    freemaker 的基本语法
    <# …> 中存放所有 freemaker 的内容,之外的内容全部原样输出。
    <@ … /> 是函数调用两个定界符内的内容中,第一个符号表示指令或者函数名,其后的跟随参数。
    freemaker 提供的控制包括如下:
    freemaker 的基本语法
    <# …> 中存放所有 freemaker 的内容,之外的内容全部原样输出。
    <@ … /> 是函数调用
    两个定界符内的内容中,第一个符号表示指令或者函数名,其后的跟随参数。
    freemaker 语句:
    <#if condition>
        <#elseif condition>
        <#else>
    </#if>
    条件判断
    
    <#list hash_or_seq as var>
    </#list>
    遍历 hash 表或者 collection(freemaker 称作 sequence)的成员
    
    <#macro name param1 param2 ... >
    <#nested param>
    </#macro>
    宏,无返回参数
    
    <#function name param1 param2>
    <#return val>
    </#function>
    函数,有返回参数
    
    var?member_function(...)
    用函数对 var 进行转换,freemaker 称为 build-ins。实际内部实现类似
    
    member_function(var, ...)
    stringA[M .. N] 取子字符串,类似
    
    substring(stringA, M, N)
    {key:value, key2:value2 ...}
    直接定义一个 hash 表
    
    [item0, item1, item2 ...]
    直接定义一个序列
    
    hash0[key0]
    存取 hash 表中 key 对应的元素
    
    seq0[5]
    存取序列指定下标的元素
    
    <@function1 param0 param1 ... />
    调用函数 function1
    
    <@macro0 param0 param1 ; nest_param0 nest_param1 ...> nest_body 
    < /@macro>
    调用宏,并处理宏的嵌套
    
    <#assign var = value >
    定义变量并初始化
    
    <#local var = value>
    在 macro 或者 function 中定义局部变量并初始化
    
    <#global var = value >
    定义全局变量并初始化
    
    ${var}
    输出并替换为表达式的值
    
    <#visit xmlnode>
    调用 macro 匹配 xmlnode 本身及其子节点
    
    <#recurse xmlnode>
    调用 macro 匹配 xmlnode 的子节点
    
    freemaker–设计指导
    一个 ftl 标记不能放在另外一个 ftl 标记里面,但是注释标记能够放在 ftl 标记里面。
    
    系统预定义指令采用 <#…></#>
    用户自定义指令采用 <@…></@>
    hash 片段可以采用: products[10..19] or products[5..] 的格式。
    
    序列也可以做加法计算:passwords + {“joe”:”secret42”}
    缺省值: name!”unknown” 或者 (user.name)!”unknown” 或者 name! 或者 (user.name)!
    null 值检查: name?? or (user.name)??
    
    转义列表:
     Escape sequence Meaning
     ” Quotation mark (u0022)
     ’ Apostrophe (a.k.a. apostrophe-quote) (u0027)
      Back slash (u005C)
     
     Line feed (u000A)
     
     Carriage return (u000D)
     	 Horizontal tabulation (a.k.a. tab) (u0009)
      Backspace (u0008)
     f Form feed (u000C)
     l Less-than sign: <
     g Greater-than sign: >
     a Ampersand: &
     { Curly bracket: {
     xCode Character given with its hexadecimal Unicode code (UCS code)
    如果想打印 ${,则需要将 {转义,可以写成”${user}”,或者可以用生字符(r 指令):$(r “${xx}”}
    
    序列构成:
    <#list ["winter", "spring", "summer", "autumn"] as x>${x}</#list>
    不同的对象可以存放在一个序列里面,比如:[2 + 2, [1, 2, 3, 4], “whatnot”]. 第一个是数字,第二个是序列,第三个是字符串。
    
    可用采用 start..end 的方式来定义一个数字序列,start 可以小于 end,同时,end 也可以省略。
    
    hash 取值支持一下四种模式:
    
    book.author.name, 
    book["author"].name, 
    book.author.["name"], 
    book["author"]["name"].
    特殊变量是指 freemaker 引擎本身定义的变量。访问时,以. variable_name 的语法访问。
    
    变量表达式支持嵌套模式,比如:${“Hello ${user}!”}。
    
    变量表达式在指令中的使用情况:
    变量表达式可以在指令中,用 “” 的方式存在,不如:<#include “/footer/${company}.html”>.
    但是不允许下面的方式存在: <#if ${isBig}>Wow!</#if>
    
    正确写法
    
    <#if isBig>Wow!</#if>
    而且 <#if “${isBig}”>Wow!</#if > 写法也不正确,因为”${isBig}” 返回的是字符串,不是 boolean 类型。
    
    字符串中取字符或字符串采用以下语法:
    
    ${user[0]},${user[0..2]} ${user[4..]},${user?string(4)}
    序列操作:
    加法:<#list [“Joe”, “Fred”] + [“Julia”, “Kate”] as user> 但要注意串联之后的读取速度变慢。
    子序列:seq[1..4]
    
    序列和 hash 的串联都只能用于两个相加,不能有多个相加的模式,hash 相加,如果两个相加的 hash 存在相同的 key,则后面会覆盖前面的。
    
    在使用 >= 或者 > 时,需要注意一些问题,因为 freemaker 会将 > 解释成标记的关闭符,为了解决这个问题,需要在表达式加上括号,比如: <#if (x> y)>. 或者使用 > and &lt 符号来代替。
    
    无值变量(包括无该变量,null,返回 void,无属性等):unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!
    缺省值可以是任何类型,不一定是数字,比如:hits!0 或者 colors![“red”, “green”, “blue”].
    
    如果缺省值忽略,那么将会默认为空串、空序列或者空 hash,因为 freemarker 支持多类型的值。不过要让默认值为 0false,则不能省略缺省值。
    
    非顶层变量的无值处理:
    product.color!”red”:只处理 product 不为空,color 为空的缺省值处理,如果 product 为空,则 freemaker 会抛出异常。(product.color)!”red”:则会处理 product 为空,color 为空,或者没有 color 属性的无值情况。
    
    无值变量的判断操作:
    unsafe_expr?? or (unsafe_expr)??
    判断变量是否是无值。
    
    普通变量插入方式:
    ${expression},${3+5);
    
    数字变量插入方式:
    #{expression} or #{expression; format}:过期。
    变量只能用于文本区或者是字符串里面,比如:
    
    Hello ${name}!
    以及 <#include “/footer/${company}.html”>
    数字值的插入:
    根据缺省的 number_format 输出,以及可以通过 setting 来达到设置数字格式的目的,也可以通过内置函数 string 来改变输出格式。
    
    日期类型的格式设置:
    date_format, time_format 和 datetime_format
    
    定义宏:
    不带参数:<#macro 宏名>…</#macro>,引用 <@宏名 />
    带参数:<#macro 宏名 参数…>…</#macro>,引用 <@宏名 参数 1 = 值 1…/>,带有参数的宏,调用是参数的值必须和参数的个数相同。当然也可以在宏定义时给参数一些默认值。比如:<#macro greet person color=”black”>
    
    宏里面的嵌套内容:
    <#macro border>
    
    <#nested>
    
    </#macro>
    在宏的定义 body 中加入 <#nested> 指令。嵌套的内容可以是任何正确的 ftl 块。
    宏的本地变量在嵌套内容中是不可见的。
    
    宏定义时,<#nest> 指令相当于调用定义的内容,而使用宏时,nest body 相当于定义。
    <#macro repeat count> <#list 1..count as x> <#nested x, x/2, x==count> </#list></#macro><@repeat count=4 ; c, halfc, last> ${c}. ${halfc}<#if last> Last!</#if>/@repeat
    
    定义变量:
    在模板中定义的变量将会隐藏(不是更改)数据模型根下面的同名的变量。
    
    模板中的 3 种类型变量:
     1:plain variables,能够在模板中的任何地方访问,一个模板 include 另外一个模板,也可以访问被包含模板的变量。可以通过 assign 或者 macro 指令产生或替换变量。
    如果要访问数据模型中的变量,则可以通过. global 来访问:
    <#assign user = “Joe Hider”>
    ${user} <#– prints: Joe Hider –>
    ${.globals.user} <#– prints: Big Joe–>
     2:Local variables,宏定义 body 中用 local 指令创建或者替换。
     3:Loop variables: 由 list 指令产生。
    namespaces:
    <#import “/lib/my_test.ftl” as my> <#– the hash called “my” will be the “gate” –>
    <@my.copyright date=”1999-2002”/>
    ${my.mail}
    
    设置命名空间里面的变量:<#assign mail=”jsmith@other.com” in my>
    命名空间与数据模型:命名空间的 ftl 可以访问数据模型的变量。同样命名空间的变量也会隐藏数据模型中同名的变量。
    空白问题:
     1:White-space stripping,默认为 enabled,清除 ftl 标记带来的空白以及缩进。处理模板的空白。
     2:t, rt, lt 指令。
     3:ftl 的参数 strip_text.
    用 compress directive 或者 transform 来处理输出。
    <#compress>…</#compress>:消除空白行。
    <@compress single_line=true>…/@compress 将输出压缩为一行。
    
    可替换语法:
    freemarker 可用”[“代替”<”. 在模板的文件开头加上 [#ftl].
    
    Spring MVC 使用 Freemarker
    • Freemaker 是取代 JSP 的又一种视图技术,和 Velocity 非常类似,但是它比 Velocity 多了一个格式化的功能,因此使用上较 Velocity 方便一点,但语法也稍微复杂一些。
    将 Velocity 替换为 Freemarker 只需要改动一些配置文件,同样,在 Spring 中使用 Freemarker 也非常方便,根本无须与 Freemarker 的 API 打交道。我们将 Spring_Velocity 工程复制一份,命名为 Spring_Freemarker
    
    修改 dispatcher-servlet.xml,将 velocityConfig 删除,修改 viewResolver 为 FreeMarker ViewResolver,并添加一个 freemarkerConfig。
    
    <!-- 使用Freemarker视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view. freemarker.FreeMarkerViewResolver">
        <property name="contentType" value="text/html;charset=UTF-8" />
        <property name="prefix" value="/" />
        <property name="suffix" value=".html" />
    </bean>
    <!-- 配置Freemarker -->
    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view. freemarker.FreeMarkerConfigurer">
        <!-- 视图资源位置 -->
        <property name="templateLoaderPath" value="/" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>
    模板 test.html 可以稍做修改,加入 Freemarker 内置的格式化功能来定制 Date 类型的输出格式。
    
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Spring_Freemarker</title>
    </head>
    <body>
        <h3>Hello, ${name}, it is ${time?string("yyyy-MM-dd HH:mm:ss")}</h3>
    </body>
    </html>
    添加 freemarker.jar 到 web/WEB-INF/lib 目录后,启动 Resin,可以看到由 Freemarker 渲染的页面
    
    FreeMarker 入门:
    快速入门
    模板 + 数据模型 = 输出
     FreeMarker基于设计者和程序员是具有不同专业技能的不同个体的观念 
    他们是分工劳动的:设计者专注于表示——创建HTML文件、图片、Web页面的其它可视化方面;程序员创建系统,生成设计页面要显示的数据 
    经常会遇到的问题是:在Web页面(或其它类型的文档)中显示的信息在设计页面时是无效的,是基于动态数据的 
    在这里,你可以在HTML(或其它要输出的文本)中加入一些特定指令,FreeMarker会在输出页面给最终用户时,用适当的数据替代这些代码 
    下面是一个例子:
    
    <html> 
    <head> 
      <title>Welcome!</title> 
    </head> 
    <body> 
      <h1>Welcome ${user}!</h1> 
      <p>Our latest product: 
      <a href="${latestProduct.url}">${latestProduct.name}</a>! 
    </body> 
    </html>
    这个例子是在简单的 HTML 中加入了一些由 ${…} 包围的特定代码,这些特定代码是 FreeMarker 的指令,而包含 FreeMarker 的指令的文件就称为模板(Template)
    至于 user、latestProduct.url 和 latestProduct.name 来自于数据模型(data model)
    数据模型由程序员编程来创建,向模板提供变化的信息,这些信息来自于数据库、文件,甚至于在程序中直接生成
    模板设计者不关心数据从那儿来,只知道使用已经建立的数据模型
    下面是一个可能的数据模型:
    
    (root) 
      | 
      +- user = "Big Joe" 
      | 
      +- latestProduct 
          | 
          +- url = "products/greenmouse.html" 
          | 
          +- name = "green mouse"
    数据模型类似于计算机的文件系统,latestProduct可以看作是目录,而user、url和name看作是文件,url和name文件位于latestProduct目录中(这只是一个比喻,实际并不存在) 
    当FreeMarker将上面的数据模型合并到模板中,就创建了下面的输出: 
    <html> 
    <head> 
      <title>Welcome!</title> 
    </head> 
    <body> 
      <h1>Welcome Big Joe!</h1> 
      <p>Our latest product: 
      <a href="products/greenmouse.html">green mouse</a>! 
    </body> 
    </html>
    数据模型
    典型的数据模型是树型结构,可以任意复杂和深层次,如下面的例子: 
    (root) 
      | 
      +- animals 
      |   | 
      |   +- mouse 
      |   |   |   
      |   |   +- size = "small" 
      |   |   |   
      |   |   +- price = 50 
      |   | 
      |   +- elephant 
      |   |   |   
      |   |   +- size = "large" 
      |   |   |   
      |   |   +- price = 5000 
      |   | 
      |   +- python 
      |       |   
      |       +- size = "medium" 
      |       |   
      |       +- price = 4999 
      | 
      +- test = "It is a test" 
      | 
      +- whatnot 
          | 
          +- because = "don't know"
     类似于目录的变量称为hashes,包含保存下级变量的唯一的查询名字 
    类似于文件的变量称为scalars,保存单值 
     scalars保存的值有两种类型:字符串(用引号括起,可以是单引号或双引号)和数字(不要用引号将数字括起,这会作为字符串处理) 
    对scalars的访问从root开始,各部分用“.”分隔,如animals.mouse.price 
     另外一种变量是sequences,和hashes类似,只是不使用变量名字,而使用数字索引,如下面的例子: 
    (root) 
      | 
      +- animals 
      |   | 
      |   +- (1st) 
      |   |   | 
      |   |   +- name = "mouse" 
      |   |   | 
      |   |   +- size = "small" 
      |   |   | 
      |   |   +- price = 50 
      |   | 
      |   +- (2nd) 
      |   |   | 
      |   |   +- name = "elephant" 
      |   |   | 
      |   |   +- size = "large" 
      |   |   | 
      |   |   +- price = 5000 
      |   | 
      |   +- (3rd) 
      |       | 
      |       +- name = "python" 
      |       | 
      |       +- size = "medium" 
      |       | 
      |       +- price = 4999 
      | 
      +- whatnot 
          | 
          +- fruits 
              | 
              +- (1st) = "orange" 
              | 
              +- (2nd) = "banana"
    这种对scalars的访问使用索引,如animals[0].name 
    模板
     在FreeMarker模板中可以包括下面三种特定部分: 
     ${…}:称为interpolations,FreeMarker会在输出时用实际值进行替代 
    FTL标记(FreeMarker模板语言标记):类似于HTML标记,为了与HTML标记区分,用#开始(有些以@开始,在后面叙述) 
    注释:包含在<#--和-->(而不是<!--和-->)之间 
    下面是一些使用指令的例子: 
    if 指令
    <#if animals.python.price < animals.elephant.price> 
      Pythons are cheaper than elephants today. 
    <#else> 
      Pythons are not cheaper than elephants today. 
    </#if>
    list 指令
    We have these animals:
    
    ``html
    
    
    Name    Price
    <#list animals as being>
    ${being.name}    ${being.price} Euros
    </#list>
    
     
    输出为: 
    <p>We have these animals: 
    
    ```html
    <table border=1> 
      <tr><th>Name<th>Price 
      <tr><td>mouse<td>50 Euros 
      <tr><td>elephant<td>5000 Euros 
      <tr><td>python<td>4999 Euros 
    </table>
    include 指令
    <html> 
    <head> 
      <title>Test page</title> 
    </head> 
    <body> 
      <h1>Test page</h1> 
      <p>Blah blah... 
    <#include "/copyright_footer.html"> 
    </body> 
    </html>
    一起使用指令
    We have these animals:
    
    <table border=1> 
      <tr><th>Name<th>Price 
      <#list animals as being> 
      <tr> 
        <td> 
          <#if being.size = "large"><b></#if> 
          ${being.name} 
          <#if being.size = "large"></b></#if> 
        <td>${being.price} Euros 
      </#list> 
    </table>
    常用语法
    EG. 一个对象 BOOK
    
    1. 输出
    ${book.name}
    空值判断:
    
    ${book.name?if_exists },
    ${book.name?default(‘xxx’)}//默认值xxx
    ${ book.name!"xxx"}//默认值xxx
    日期格式:
    
    ${book.date?string('yyyy-MM-dd')}
    数字格式:
    
    ${book?string.number}--20
    ${book?string.currency}--<#-- $20.00 -->
    ${book?string.percent}—<#-- 20% -->
    插入布尔值:
    
    <#assign foo=ture />
    ${foo?string("yes","no")} <#-- yes -->
    2.逻辑判断
    a:
    
    <#if condition>...
    <#elseif condition2>...
    <#elseif condition3>......
    <#else>...
    其中空值判断可以写成
    
    <#if book.name?? >
    b:
    
    <#switch value>
    <#case refValue1>
    ...
    <#break>
    <#case refValue2>
    ...
    <#break>
    ...
    <#case refValueN>
    ...
    <#break>
    <#default>
    ...
    3.循环读取
    <#list sequence as item>
    ...
    空值判断
    <#if bookList?size = 0>
    e.g.
    <#list employees as e>
    ${e_index}. ${e.name}
    输出:
    
    1. Readonly
    2. Robbin
    freemarker 中 Map 的使用
    
    <#list testMap?keys as testKey> 
           < option value="${testKey}" > 
                  ${testMap[testKey]} 
         </option> 
    </#list>
    解析 FreeMarker 视图
    声明一个针对 FreeMarker 的视图解析器:
    
    <bean id="viewResolver" class="org.springframework.
              ➥web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="suffix"><value>.ftl</value></property>
      </bean>
    FreeMarkerViewResolver 和 VelocityViewResolver 或 InternalResourceViewResolver 的工作机制相同。模板资源是通过在视图的逻辑名上增加 prefix 属性的值作为前缀,以及增加 suffix 属性的值作为后缀进行解析的。和 VelocityViewResolver 一样,在这里我们又一次只设置 suffix 属性,因为模板的路径已经在 FreeMarkerConfigurer 的 templateLoaderPath 属性中定义了。
    暴露请求和会话属性。你看到如何告诉 VelocityViewResolver 将请求和会话属性复制到模型 map 中,从而它们能够在模板中作为变量使用。采用同样的方式配置 FreeMarkerViewResolver,可以将请求和会话属性作为变量暴露给 FreeMarker 模板使用。要做到这一点,可以设置 exposeRequestAttributes 或者 exposeSessionAttributes 为 true<bean id="viewResolver" class="org.springframework.
              web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="exposeRequestAttributes">
          <value>true</value>
        </property>
        <property name="exposeSessionAttributes">
          <value>true</value>
        </property>
      </bean>
    这里,两个属性都被设置为 true。结果是请求和会话属性都被复制到模板的属性集中,可以使用 FreeMarker 的表达式语言来访问并显示。
    
    数据源 + freemarker+servlet 生成 xml 文件
    一. 步骤:
    1. 在 server.xml 文件中建立数据源.
    <Service name="Cms">
          <Connector debug="0" enableLookups="false" port="8084" protocol="AJP/1.3" redirectPort="8443"/>
         <Connector acceptCount="100" connectionTimeout="20000" debug="0" disableUploadTimeout="true" 
     
     enableLookups="false" maxSpareThreads="75" maxThreads="150" minSpareThreads="25" port="8081" redirectPort="8443"/>
         <Engine defaultHost="localhost_Cms" name="Catalina_Cms">
           <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_cmt_log." suffix=".txt" timestamp="true"/>
           <Realm className="org.apache.catalina.realm.UserDatabaseRealm"/>
           <Host autoDeploy="true" debug="0" name="localhost_Cms" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
              <Context debug="0" docBase="D:WorkspaceCMSweb" path="/" reloadable="true" workDir="D:WorkspaceCMSj2src"> 
               <Resource name="jdbc/news_DB" auth="Container" type="javax.sql.DataSource"/>
             <ResourceParams name="jdbc/news_DB">
          <parameter>
            <name>factory</name>
            <!-- DBCP Basic Datasource Factory -->
            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
          </parameter>
           
          <parameter>
            <name>maxActive</name>
            <value>1000</value>
          </parameter>
          <parameter>
             <name>validationQuery</name>
             <value>select 1+1</value>
          </parameter>
          <parameter>
            <name>maxIdle</name>
            <value>100</value>
          </parameter>
          <parameter>
            <name>maxWait</name>
            <value>10000</value>
          </parameter>
          <parameter>
            <name>removeAbandoned</name>
            <value>true</value>
          </parameter>
          <parameter>
            <name>removeAbandonedTimeout</name>
            <value>60</value>
          </parameter>
          <parameter>
            <name>logAbandoned</name>
            <value>false</value>
          </parameter>
           
          <parameter>
            <name>username</name>
            <value>aaaa</value>
          </parameter>
          <parameter>
            <name>password</name>
            <value>bbbbbb</value>
          </parameter>
          <parameter>
            <name>driverClassName</name>
            <value>net.sourceforge.jtds.jdbc.Driver</value>
          </parameter>
          <parameter>
            <name>url</name>
            <value>jdbc:jtds:sqlserver://111.111.111.111:1433/cms</value>
          </parameter>
           </ResourceParams>
           
        </Context>
           </Host>
     </Engine>
       </Service>
    2. 在 web.xml 文件中配置 servlet
    <servlet>
             <description>generate xml file</description>
             <servlet-name>NewsXmlServlet</servlet-name>
             <servlet-class>xml.NewsXmlServlet</servlet-class>
         </servlet>
         <servlet-mapping>
             <servlet-name>NewsXmlServlet</servlet-name>
             <url-pattern>/xmlServlet</url-pattern>
         </servlet-mapping>
         <servlet>
    3.newsXmlServlet.java
    package xml;
     
     import java.io.*;
     import java.nio.charset.Charset;
     import java.sql.Connection;
     import java.sql.ResultSet;
     import java.sql.SQLException;
     import java.sql.Statement;
     import java.util.ArrayList;
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
     
     import javax.naming.Context;
     import javax.naming.InitialContext;
     import javax.naming.NamingException;
     import javax.servlet.ServletException;
     import javax.servlet.http.HttpServlet;
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
     import javax.sql.DataSource;
     
     import freemarker.template.Configuration;
     import freemarker.template.Template;
     import freemarker.template.TemplateException;
     
     import java.util.Locale;
     
      public class NewsXmlServlet extends HttpServlet{
     
         protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                  throws ServletException, IOException {
             // TODO Auto-generated method stub
              try {
                 Connection conn=null;
                 Context ctx = new InitialContext();
                 DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/news_DB");
                 conn=ds.getConnection();
                 Statement stmt=conn.createStatement();
                 ResultSet rs=stmt.executeQuery("select url,updatetime,tpf_edu_contentTitle,tpf_edu_contentneirong,tpf_edu_contentlaiyuan,tpf_edu_contentkeyword from tp5__edu_content where url<>'' and url is not null and dateDiff(d,updatetime,getDate())=1 order by updatetime desc");
                 Configuration cfg=new Configuration();
                 cfg.setDirectoryForTemplateLoading(new File("E:/wwwroot/CMS/web/WEB-INF/classes/xml"));
                 Template tem=cfg.getTemplate("news.ftl");
                 
                 List list=new ArrayList();
                 OutputStreamWriter out=new OutputStreamWriter(System.out);
                  while(rs.next()){
                     Map item=new HashMap();
                     item.put("title",rs.getString(3));
                     item.put("link","http://test.com.cn"+rs.getString(1));
                     item.put("pubdate",rs.getTimestamp(2));
                     item.put("content",DelHtml(rs.getString(4)));
                     item.put("source",rs.getString(5));
                     item.put("keywords",DelHtml(rs.getString(6)));
                     list.add(item);
                 }
                 Map data=new HashMap();
                 data.put("items",list);
                 StringWriter writer=new StringWriter();
                 tem.process(data,writer);
                 String content=writer.toString();
                 writer.close();
                 createXml(content);
                 out.close();
                 //resp.setContentType("text/xml; charset=utf-8");
                 //resp.getWriter().write(content);
                 
              } catch (NamingException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
              } catch (SQLException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
              } catch (TemplateException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
     
         }
          public String DelHtml(String content){
             String contents=content.replaceAll("</?s*(S+)(s*[^>]*)?s*/?>","");
              contents=contents.replaceAll("&ldquo;", "");
              contents=contents.replaceAll("&rdquo;","");
              contents=contents.replaceAll("&ldquo;", "");
              contents=contents.replaceAll("&rdquo;","");
              contents=contents.replaceAll("&middot;","");
              contents=contents.replaceAll("&mdash;","");
              contents=contents.replaceAll("&hellip;","");
              contents=contents.replaceAll("&nbsp;","");
              contents=contents.replaceAll(","," ");
             return contents;        
         }
          public void createXml(String fileContent){
              try {
                 String filePath="E:/wwwroot/cmsHtml/education/news.xml";
                 File fileXml=new File(filePath);
                  if(!fileXml.exists()){            
                     fileXml.createNewFile();
                 }
                 
                  /*FileWriter fileWriter=new FileWriter(fileXml);
                 fileWriter.
                 fileWriter.write(fileContent);
                 fileWriter.close();*/
                     OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream(fileXml), Charset.forName("utf-8"));     
                     writer.write(fileContent);
                     writer.close();
                  } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
             }
         }
         
          public void destroy() {
             // TODO Auto-generated method stub
             super.destroy();
         }
     
          public void init() throws ServletException {
             // TODO Auto-generated method stub
             super.init();
         }
         
     }
    4.news.ftl
    <?xml version="1.0" encoding="utf-8" ?>
     <document>
         <webSite>edu.aweb.com.cn</webSite>
         <webMaster>webmaster@aweb.com.cn</webMaster>
         <updatePeri>1440</updatePeri>
         <#list items as it>
         <item>
              <title><![CDATA[${it.title}]]></title>
              <link>${it.link}</link>
              <pubDate>${it.pubdate}</pubDate>
              <text><![CDATA[${it.content}]]></text>
             <image/>
              <source>${it.source}</source>
              <keywords><![CDATA[${it.keywords}]]></keywords>
         </item>
         </#list>
     </document>
    Spring 中使用 FreeMaker 或 Vilocity 模板发送邮件
    本文以用户注册后为用户发送一封邮件为例子,讲述如何在 Spring 中使用 FreeMaker 或 Vilocity 发送邮件。
    
    Spring 配置文件:
    xml 代码
    
    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">   
                 <property name="host" value="smtp.163.com"/>   
                 <property name="username" value="test"/>   
                 <property name="password" value="123456"/>   
                 <property name="javaMailProperties">   
                       <props>   
                         <prop key="mail.smtp.auth">trueprop>   
                       props>   
                 property>   
             bean>   
            
                
             <bean id="freeMarkerConfigurer"                    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">   
                 <property name="templateLoaderPath" value="/WEB-INF/freemakertemplate/" />   
                 <property name="freemarkerSettings">   
                     <props>   
                         <prop key="template_update_delay">0prop>   
                         <prop key="default_encoding">GBKprop>   
                         <prop key="locale">zh_CNprop>   
                     props>   
                 property>   
             bean>   
                 
                
             <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">   
                 <property name="resourceLoaderPath" value="/WEB-INF/vilocitytemplate/" />   
                 <property name="velocityProperties">   
                     <props>   
                         <prop key="velocimacro.library">*.vmprop>   
                         <prop key="default.contentType">text/html; charset=utf-8prop>   
                         <prop key="output.encoding">utf-8prop>   
                         <prop key="input.encoding">utf-8prop>   
                     props>   
                 property>   
             bean>   
                 
              <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage" singleton="false">   
                 <property name="from" value="test@163.com"/>   
             bean>   
                 
             <bean id="mailEngine" class="test.MailEngine">   
                 <property name="mailSender" ref="mailSender"/>   
                    
                 <property name="velocityEngine" ref="velocityEngine"/>   
                   
                <property name="freeMarkerConfigurer" ref="freeMarkerConfigurer" />   
             bean>
    java 代码
    MailEngine 类:
    
    public class MailEngine {    
            protected static final Log log = LogFactory.getLog(MailEngine.class);    
           
        //    private FreeMarkerConfigurer freeMarkerConfigurer;    
            private VelocityEngine velocityEngine;    
            private MailSender mailSender;    
           
        //    public void setFreeMarkerConfigurer(    
        //            FreeMarkerConfigurer freeMarkerConfigurer) {    
        //        this.freeMarkerConfigurer = freeMarkerConfigurer;    
        //    }    
           
            public void setMailSender(MailSender mailSender) {    
                this.mailSender = mailSender;    
            }    
           
            public void setVelocityEngine(VelocityEngine velocityEngine) {    
                this.velocityEngine = velocityEngine;    
            }    
           
            /**   
             * 通过模板产生邮件正文   
             * @param templateName    邮件模板名称   
             * @param map            模板中要填充的对象   
             * @return 邮件正文(HTML)   
             */   
            public String generateEmailContent(String templateName, Map map) {    
                //使用FreeMaker模板    
        //        try {    
        //            Configuration configuration = freeMarkerConfigurer.getConfiguration();    
        //            Template t = configuration.getTemplate(templateName);    
        //            return FreeMarkerTemplateUtils.processTemplateIntoString(t, map);    
        //        } catch (TemplateException e) {    
        //            log.error("Error while processing FreeMarker template ", e);    
        //        } catch (FileNotFoundException e) {    
        //            e.printStackTrace();    
        //            //log.error("Error while open template file ", e);    
        //        } catch (IOException e) {    
        //            log.error("Error while generate Email Content ", e);    
        //        }    
                    
        //        使用Vilocity模板    
                try {    
                   return VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateName, map);    
                } catch (VelocityException e) {    
                    log.error("Error while processing Vilocity template ", e);    
                }    
                    
                return null;    
            }    
           
            /**   
             * 发送邮件   
             * @param emailAddress        收件人Email地址的数组   
             * @param fromEmail            寄件人Email地址, null为默认寄件人web@vnvtrip.com   
             * @param bodyText            邮件正文   
             * @param subject            邮件主题   
             * @param attachmentName    附件名   
             * @param resource            附件   
             * @throws MessagingException   
             */   
            public void sendMessage(String[] emailAddresses, String fromEmail,    
                    String bodyText, String subject, String attachmentName,    
                    ClassPathResource resource) throws MessagingException {    
                MimeMessage message = ((JavaMailSenderImpl) mailSender)    
                        .createMimeMessage();    
           
                // use the true flag to indicate you need a multipart message    
                MimeMessageHelper helper = new MimeMessageHelper(message, true);    
           
                helper.setTo(emailAddresses);    
                if(fromEmail != null){    
                    helper.setFrom(fromEmail);    
                }    
                helper.setText(bodyText, true);    
                helper.setSubject(subject);    
                    
                if(attachmentName!=null && resource!=null)    
                    helper.addAttachment(attachmentName, resource);    
           
                ((JavaMailSenderImpl) mailSender).send(message);    
            }    
           
            /**   
             * 发送简单邮件   
             * @param msg       
             */   
            public void send(SimpleMailMessage msg) {    
                try {    
                    ((JavaMailSenderImpl) mailSender).send(msg);    
                } catch (MailException ex) {    
                    //log it and go on    
                    log.error(ex.getMessage());    
                }    
            }    
                
            /**   
             * 使用模版发送HTML格式的邮件   
             *   
             * @param msg          装有to,from,subject信息的SimpleMailMessage   
             * @param templateName 模版名,模版根路径已在配置文件定义于freemakarengine中   
             * @param model        渲染模版所需的数据   
             */   
            public void send(SimpleMailMessage msg, String templateName, Map model) {    
                //生成html邮件内容    
                String content = generateEmailContent(templateName, model);    
                MimeMessage mimeMsg = null;    
                try {    
                    mimeMsg = ((JavaMailSenderImpl) mailSender).createMimeMessage();    
                    MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, true, "utf-8");    
                    helper.setTo(msg.getTo());    
                        
                    if(msg.getSubject()!=null)    
                        helper.setSubject(msg.getSubject());    
                        
                    if(msg.getFrom()!=null)    
                        helper.setFrom(msg.getFrom());    
                        
                    helper.setText(content, true);    
                        
                    ((JavaMailSenderImpl) mailSender).send(mimeMsg);    
                } catch (MessagingException ex) {    
                    log.error(ex.getMessage(), ex);    
                }    
           
            }    
        }    
           
        //发送邮件:    
        SimpleMailMessage message = (SimpleMailMessage) getBean("mailMessage");    
                        message.setTo(user.getName() + "<" + user.getEmail() + ">");    
                            
                        Map model = new HashMap();    
                        model.put("user", user);    
                            
                        MailEngine engine = (MailEngine)getBean("mailEngine");    
                        //Vilocity模板    
                        engine.send(message, "notifyUser.vm", model);    
                        //FreeMaker模板    
                        //engine.send(message, "NotifyUser.ftl", model);
    以上的 User 为用户类。
    
    xml 代码
    模板:
    
    <html>   
    <head>   
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">   
    <title>用户注册通知title>   
    head>   
    <body>   
    <p>${user.name} 您好,恭喜您,已经成为本站会员!p>   
    <table>   
    <tr><td>用户名:td><td>${user.name}td>tr>   
    <tr><td>密码:td><td>${user.password}td>tr>   
    table>   
    body>   
    html>
  • 相关阅读:
    MyBatis(十):MyBatis懒加载 延迟加载详解
    MyBatis(九):MyBatis类型处理器(TypeHandler)详解
    MyBatis(八):MyBatis插件机制详解
    MyBatis(七):MyBatis缓存详解(一级缓存/二级缓存)
    MyBatis(六):MyBatis复杂映射
    内省概念及详解(MethodDescriptor、PropertyDescriptor)
    反射概念及详解
    sublime3 + chrome + xdebug调试php代码
    php的文件系统和程序执行函数整理
    php编程趣事:乘法口诀、百钱买百鸡、过桥等问题
  • 原文地址:https://www.cnblogs.com/Jeely/p/11224343.html
Copyright © 2011-2022 走看看