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)
 \n Line feed (u000A)
 \r Carriage return (u000D)
 \t Horizontal tabulation (a.k.a. tab) (u0009)
 \b 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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM