Java中使用FreeMaker實現(xiàn)模板渲染

一岂丘、引言

1.1 freemarker簡介

FreeMarker是一個用Java語言編寫的模板引擎储矩,它基于模板來生成文本輸出植影。FreeMarker與Web容器無關(guān),即在Web運行時颜价,它并不知道Servlet或HTTP。它不僅可以用作表現(xiàn)層的實現(xiàn)技術(shù)饵较,而且還可以用于生成XML拍嵌,JSP或Java 等。目前企業(yè)中循诉,主要用Freemarker做靜態(tài)頁面或是頁面展示横辆。

模板編寫為FreeMarker Template Language (FTL)。它是簡單的茄猫,專用的語言狈蚤, 不是 像PHP那樣成熟的編程語言。 那就意味著要準備數(shù)據(jù)在真實編程語言中來顯示划纽,比如數(shù)據(jù)庫查詢和業(yè)務(wù)運算脆侮, 之后模板顯示已經(jīng)準備好的數(shù)據(jù)。在模板中勇劣,你可以專注于如何展現(xiàn)數(shù)據(jù)靖避, 而在模板之外可以專注于要展示什么數(shù)據(jù)。

這種方式通常被稱為 MVC (模型 視圖 控制器) 模式比默,對于動態(tài)網(wǎng)頁來說幻捏,是一種特別流行的模式。 它幫助從開發(fā)人員(Java 程序員)中分離出網(wǎng)頁設(shè)計師(HTML設(shè)計師)命咐。設(shè)計師無需面對模板中的復(fù)雜邏輯篡九, 在沒有程序員來修改或重新編譯代碼時,也可以修改頁面的樣式醋奠。

而FreeMarker最初的設(shè)計榛臼,是被用來在MVC模式的Web開發(fā)框架中生成HTML頁面的伊佃,它沒有被綁定到 Servlet或HTML或任意Web相關(guān)的東西上。它也可以用于非Web應(yīng)用環(huán)境中沛善。

1.2 Freemarker的使用方法

把freemarker的jar包添加到工程中航揉。

1.2.1 Maven工程

添加依賴:

<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.23</version>
</dependency>

1.2.2 非 Maven工程

加入相應(yīng)jar包到build path中

1.3 Freemarker的實現(xiàn)原理

freemaker overview.png

1.4 使用步驟

第一步:創(chuàng)建一個Configuration對象,直接new一個對象金刁。構(gòu)造方法的參數(shù)就是freemarker對于的版本號迷捧。
第二步:設(shè)置模板文件所在的路徑。
第三步:設(shè)置模板文件使用的字符集胀葱。一般就是utf-8.
第四步:加載一個模板漠秋,創(chuàng)建一個模板對象。
第五步:創(chuàng)建一個模板使用的數(shù)據(jù)集抵屿,可以是pojo也可以是map庆锦。一般是Map。
第六步:創(chuàng)建一個Writer對象轧葛,一般創(chuàng)建一FileWriter對象搂抒,指定生成的文件名。
第七步:調(diào)用模板對象的process方法輸出文件尿扯。
第八步:關(guān)閉流求晶。

public void test() throws IOException, TemplateException { 
    // 第一步:創(chuàng)建一個Configuration對象,直接new一個對象衷笋。構(gòu)造方法的參數(shù)就是freemarker對于的版本號芳杏。 
    Configuration configuration = new Configuration(Configuration.getVersion()); 

    // 第二步:設(shè)置模板文件所在的路徑。 
    configuration.setDirectoryForTemplateLoading(new File("D:\\Java\\Eclipse\\workspace_Test\\FreeMarker\\src\\main\\webapp\\WEB-INF\\ftl")); 
    
    // 第三步:設(shè)置模板文件使用的字符集辟宗。一般就是utf-8. 
    configuration.setDefaultEncoding("utf-8"); 
    
    // 第四步:加載一個模板爵赵,創(chuàng)建一個模板對象。 Template template = configuration.getTemplate("hello.ftl"); 
    
    // 第五步:創(chuàng)建一個模板使用的數(shù)據(jù)集泊脐,可以是pojo也可以是map空幻。一般是Map。 
    Map dataModel = new HashMap(); 
    // 向數(shù)據(jù)集中添加數(shù)據(jù) 
    dataModel.put("hello", "this is my first freemarker test."); 
    
    // 第六步:創(chuàng)建一個Writer對象容客,一般創(chuàng)建一FileWriter對象秕铛,指定生成的文件名。 
    Writer out = new FileWriter(new File("D:\\Java\\Eclipse\\workspace_Test\\FreeMarker\\out\\hello.html")); 
    
    // 第七步:調(diào)用模板對象的process方法輸出文件缩挑。 
    template.process(dataModel, out); 

    // 第八步:關(guān)閉流但两。 
    out.close(); 
}

1.5 模板的基本語法

1.5.1 訪問map中的key

${key}

1.5.2 訪問pojo中的屬性

Student對象。學(xué)號调煎、姓名镜遣、年齡
${key.property}

<body>
  取學(xué)生的信息:</br>
<label>學(xué)號:</label>${stu.id}<br>
<label>姓名:</label>${stu.name}<br>
<label>年齡 :</label>${stu.age}<br>
</body>

1.5.3 取集合中的數(shù)據(jù)

循環(huán)使用格式:

<#list 要循環(huán)的數(shù)據(jù) as 循環(huán)后的數(shù)據(jù)>
</#list>

例如:

<body>
<table boder=1>
<#list studentlist as student>
  <tr>
        <td>${student.id}</td>
        <td>${student.name}</td>
        <td>${student.age}</td>
  </tr>
</list>
</table>
</body>
List<Student> studentList=new ArrayList<>();
studentList.add(new Student(1,"張三",20));
studentList.add(new Student(2,"張三2",21));
studentList.add(new Student(3,"張三3",22));
studentList.add(new Student(4,"張三4",23));
studentList.add(new Student(5,"張三4",24));
modelMap.put("studentList",studentList);
Writer out=new FileWrite(new File("D://temp/first.html"));

1.5.4 取循環(huán)中的下標

<#list studentList as student>
    ${student_index}
</#list>

例如:

<body>
<table boder=1>
<#list studentlist as student>
  <tr>
        <td>${student_index}</td>
        <td>${student.id}</td>
        <td>${student.name}</td>
        <td>${student.age}</td>
  </tr>
</list>
</table>
</body>

1.5.5 判斷

<#if student_index % 2 == 0>
<#else>
</#if>

例如:

<body>
<table boder=1>
<#list studentlist as student>
  <#if student_index %2 ==0>
       <tr bgcolor="blue">
 <#else>
       <tr bgcolor="red">
</list>
</table>
</body>

可以利用索引加上判斷己肮,實現(xiàn)表格中奇數(shù)和偶數(shù)行以不同的顏色來進行標識士袄。

1.5.6 日期類型格式化

直接取值:${date}(date是屬性名)如果傳來的是一個Date型數(shù)據(jù)會報錯

${date?date} //2016-9-13
${date?time} //17:53:55
${date?datetime} //2016-9-13 17:53:55

如果感覺freemaker提供的功能太弱悲关,可以在java中格式化好之后再通過普通字符串的形式傳入進來。

1.5.7 Null值的處理

如果直接取一個不存在的值(值為null)時會報異常

${aaa}

如果允許變量可以為空娄柳,并且不希望報出異常寓辱,可以在變量后面加上一個!

${aaa!}

1.5.8 Include標簽

如果希望在某個FTL文件中引入另外一個FTL文件赤拒,可以使用FTL關(guān)鍵字秫筏。格式如下:

<#include “模板名稱”> //(相當于jstl中的包含)

例如:

<#include "hello.ftl">

注意:上述引用方式表明hello.ftl與當前文件處于同一個目錄下,如果兩個文件在不同的目錄下挎挖,需要使用相對路徑这敬。

二、需求描述

需要實現(xiàn)一個定時發(fā)送監(jiān)控數(shù)據(jù)報表的功能蕉朵,技術(shù)實現(xiàn)上考慮使用freemaker來實現(xiàn)郵件內(nèi)容的渲染崔涂。

三、實戰(zhàn)

3.1 引入包依賴

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.19</version>
 </dependency>

3.2 編寫郵件模板

編寫的郵件模板report.ftl文件放在resources/report/report.ftl路徑下始衅。模板的完整內(nèi)容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <style type="text/css">
        /* What it does: Remove spaces around the email design added by some email clients. */
        /* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */
        html,
        body {
            margin: 0;
            padding: 0;
            height: 100% !important;
            width: 100% !important;
        }

        /* What it does: Stops email clients resizing small text. */
        * {
            -ms-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%;
        }

        /* What it does: Forces Outlook.com to display emails full width. */
        .ExternalClass {
            width: 100%;
        }

        /* What it does: Stops Outlook from adding extra spacing to tables. */
        table,
        td {
            mso-table-lspace: 0pt;
            mso-table-rspace: 0pt;
        }

        /* What it does: Fixes webkit padding issue. */
        table {
            border-spacing: 0 !important;
        }

        /* What it does: Fixes Outlook.com line height. */
        .ExternalClass,
        .ExternalClass * {
            line-height: 100%;
        }

        /* What it does: Fix for Yahoo mail table alignment bug. Applies table-layout to the first 2 tables then removes for anything nested deeper. */
        table {
            border-collapse: collapse;
            margin: 0 auto;
        }

        /* What it does: Uses a better rendering method when resizing images in IE. */
        img {
            -ms-interpolation-mode: bicubic;
        }

        /* What it does: Overrides styles added when Yahoo's auto-senses a link. */
        .yshortcuts a {
            border-bottom: none !important;
        }

        /* What it does: Overrides blue, underlined links auto-detected by iOS Mail. */
        /* More Info: https://litmus.com/blog/update-banning-blue-links-on-ios-devices */
        .mobile-link--footer a {
            color: #666666 !important;
        }

        /* What it does: Overrides styles added images. */
        img {
            border: 0 !important;
            outline: none !important;
            text-decoration: none !important;
        }

        /* What it does: Apple Mail doesn't support max-width, so a media query constrains the email container width. */
        @media only screen and (min-width: 801px) {
            .email-container {
                width: 960px !important;
            }
        }

        /* What it does: Apple Mail doesn't support max-width, so a media query constrains the email container width. */
        @media only screen and (max-width: 800px) {
            .email-container {
                width: 100% !important;
                max-width: none !important;
            }
        }

        table tr td .head {
            padding: 10px 0;
            font-family: "Microsoft YaHei", sans-serif;
            text-align: center;
            font-size: 12px;
            line-height: 1.3;
            color: #333333;
            font-weight: bold;
            word-wrap: break-word;
            border-bottom: 1px solid #cccccc;
        }

        table tr td .text {
            padding: 10px 0;
            font-family: "Microsoft YaHei", sans-serif;
            text-align: center;
            font-size: 12px;
            line-height: 1.3;
            color: #333333;
            border-bottom: 1px solid #eeeeee;
        }

        table tr td .htext {
            padding: 10px 0;
            font-family: "Microsoft YaHei", sans-serif;
            text-align: center;
            font-size: 12px;
            line-height: 1.3;
            color: #333333;
            font-weight: bold;
            border-bottom: 1px solid #eeeeee;
        }

    </style>
</head>

<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" bgcolor="#222222" style="margin:0; padding:0; -webkit-text-size-adjust:none; -ms-text-size-adjust:none;">
<table cellpadding="0" cellspacing="0" border="0" height="100%" width="100%" bgcolor="#222222"
       style="border-collapse:collapse;">
    <tr>
        <td>
            <!-- Beginning of Outlook-specific wrapper : BEGIN -->
            <!--[if (gte mso 9)|(IE)]>
            <table width="1000" align="center" cellpadding="0" cellspacing="0" border="0">
                <tr>
                    <td>
            <![endif]-->
            <!-- Beginning of Outlook-specific wrapper : END -->

            <!-- Email wrapper : BEGIN -->
            <table border="0" width="100%" cellpadding="0" cellspacing="0" align="center"
                   style="max-width:960px; margin:auto;" class="email-container">
                <tbody>
                <tr>
                    <td>
                        <table border="0" width="100%" cellpadding="0" cellspacing="0">
                            <tbody>
                            <tr>
                                <td valign="middle"
                                    style="padding:10px 0; padding-left: 2%; text-align:left; font-family: 'Microsoft YaHei', sans-serif; font-size: 16px; color: #d6d6d6;"
                                    width="135">
                                ${reportNamespaceDesc}
                                </td>
                                <td valign="middle"
                                    style="padding:10px 0; padding-right: 2%; text-align:right; line-height:1.1; font-family: 'Microsoft YaHei', sans-serif; font-size: 16px; color: #d6d6d6;">
                                ${reportDate}
                                </td>
                            </tr>
                            </tbody>
                        </table>

                        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#222222"
                               style="padding:2%">
                            <tbody>
                            <tr>
                                <td align="center"
                                    style="padding-top:5%; padding-bottom:0; font-family: 'Microsoft YaHei', sans-serif; font-size: 24px; line-height: 1.3; color: #222;"></td>
                            </tr>
                            </tbody>
                        </table>
                        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#ffffff"
                               style="padding:2%">
                            <tbody>
                            <tr>
                                <td align="center"
                                    style="padding-top:4%; padding-bottom:0; font-family: 'Microsoft YaHei', sans-serif; font-size: 26px; font-weight:600; line-height: 1.3; color: #444444;">
                                ${reportTitle}
                                </td>
                            </tr>
                            </tbody>
                        </table>
                        <!-- Service 統(tǒng)計數(shù)據(jù) BEGIN-->
                    <#list reportTableList as reportTable>
                        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#ffffff">
                            <tbody>
                            <tr>
                                <td style="padding-left: 4%; padding-top: 2%; padding-bottom: 0; font-family:'Microsoft YaHei', sans-serif; font-size: 16px; line-height: 1.3; color: #666666;">
                                ${reportTable.dimensionName}:
                                    <span style="color: #ff1d33;font-weight: bold;">${reportTable.dimension}</span>
                                </td>
                            </tr>
                            <tr>
                                <td style="padding-left: 4%; padding-top: 2%; padding-bottom: 0; font-family:'Microsoft YaHei', sans-serif; font-size: 16px; line-height: 1.3; color: #666666;">
                                    說明:統(tǒng)計周期:最近一天冷蚂,統(tǒng)計方式:${reportTable.statistics}
                                </td>
                            </tr>
                            <tr>
                                <td style="padding: 4%; padding-top: 2%; padding-bottom: 2%">
                                    <table cellspacing="0" cellpadding="0" border="0" width="100%" style="">
                                        <tbody>
                                        <!-- 服務(wù)報表 title BEGIN -->
                                        <tr>
                                            <#list reportTable.serviceDataList as serviceData>
                                                <td valign="top" align="left"
                                                    style="padding: 10px 0;font-family: 'Microsoft YaHei', sans-serif; text-align: center; font-size: 12px; line-height: 1.3; color: #333333; font-weight: bold; border-bottom: 1px solid #cccccc;">
                                                ${serviceData.metricName}(${serviceData.metricUnit})
                                                </td>
                                            </#list>
                                        </tr>
                                        <!-- 服務(wù)報表 title END -->

                                        <!-- 服務(wù)報表 數(shù)據(jù) BEGIN-->
                                        <tr>
                                            <#list reportTable.serviceDataList as serviceData>
                                                <td valign="top" align="left"
                                                    style="padding: 10px 0;font-family: 'Microsoft YaHei', sans-serif; text-align: center; font-size: 12px; line-height: 1.3; color: #333333; font-weight: bold; border-bottom: 1px solid #eeeeee;">
                                                ${serviceData.metricValue}
                                                </td>
                                            </#list>
                                        </tr>
                                        <!-- 服務(wù)報表 數(shù)據(jù) END-->
                                        </tbody>
                                    </table>
                                </td>
                            </tr>
                            </tbody>
                        </table>
                    </#list>
                        <!-- Service 統(tǒng)計數(shù)據(jù) END-->
                    </td>
                </tr>

                <!-- Footer : BEGIN -->
                <tr>
                    <td style="text-align:center; padding:4% 0; font-family:'Microsoft YaHei', sans-serif; font-size:13px; line-height:1.2; color:#666666;">
                        收到該郵件是因為您在 XXX上訂閱了 XXX 的相關(guān)數(shù)據(jù),可登錄 XXX 更新訂閱配置汛闸。
                    </td>
                </tr>
                <!-- Footer : END -->

                </tbody>
            </table>
            <!-- Email wrapper : END -->
        </td>
    </tr>
</table>
</body>
</html>

3.3 編寫模板渲染工具類

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Map;

/**
 * 郵件模板生成工具類
 */
public class TemplateUtil {
    /**
     *
     * @param ftl FTL文件地址
     * @param params 填充參數(shù)
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    public static String generateTemplate(String ftl, Map<String, Object> params)
            throws IOException, TemplateException {
        Configuration configuration = new Configuration();
        configuration.setClassForTemplateLoading(TemplateUtil.class, "/");
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

        Template template = configuration.getTemplate(ftl);
        Writer stringWriter = new StringWriter();

        template.process(params, stringWriter);

        return stringWriter.toString();
    }
}

3.4 編寫定時任務(wù)

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class DailyReportTask extends Task {
    private static final Logger logger = Logger.getLogger(DailyReportTask.class);

    //TFL模板文件的相對位置
    private static final String FTL_FILE = "report/report.ftl";

    private static final Integer RECENT_HOURS = 24;

    private static final String TIME_PATTERN = "HH:mm";

    private static final String DATE_PATTERN = "yyyy年MM月dd日";

    private static final FastDateFormat TIME_FORMAT = FastDateFormat.getInstance(TIME_PATTERN, TimeZone.getTimeZone("Asia/Shanghai"));

    @Override
    public Set<String> generateShards() {
        return getSingleShard();
    }

    // 報表發(fā)送任務(wù)的線程池
    private ExecutorService executorService = new ThreadPoolExecutor(32, 32, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(512), new ThreadFactory() {
        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(new ThreadGroup("cm-task"), r, "daily-report-send-"
                    + count.getAndIncrement());
            return thread;
        }
    }, new ThreadPoolExecutor.DiscardPolicy());

    /**
     * 報表
     */
    public class ReportTable {
        String dimensionName;

        String dimension;

        String statistics;

        List<ServiceData> serviceDataList;

        public String getDimensionName() {
            return dimensionName;
        }

        public void setDimensionName(String dimensionName) {
            this.dimensionName = dimensionName;
        }

        public String getDimension() {
            return dimension;
        }

        public void setDimension(String dimension) {
            this.dimension = dimension;
        }

        public String getStatistics() {
            return statistics;
        }

        public void setStatistics(String statistics) {
            this.statistics = statistics;
        }

        public List<ServiceData> getServiceDataList() {
            return serviceDataList;
        }

        public void setServiceDataList(List<ServiceData> serviceDataList) {
            this.serviceDataList = serviceDataList;
        }
    }

    /**
     * 報表中的數(shù)據(jù)
     */
    public class ServiceData {
        String metricName;

        String metricUnit;

        Double metricValue;

        public String getMetricName() {
            return metricName;
        }

        public void setMetricName(String metricName) {
            this.metricName = metricName;
        }

        public String getMetricUnit() {
            return metricUnit;
        }

        public void setMetricUnit(String metricUnit) {
            this.metricUnit = metricUnit;
        }

        public Double getMetricValue() {
            return metricValue;
        }

        public void setMetricValue(Double metricValue) {
            this.metricValue = metricValue;
        }
    }

    @Override
    public boolean process(String taskShard) {
        try {
            List<ReportSetting> reportSettingList = reportSettingService.getReportSettingList();
            // 沒有任務(wù)則直接返回
            if (reportSettingList.size() == 0) {
                return true;
            }
            logger.debug("reportSettingList size=" + reportSettingList.size());

            for (ReportSetting reportSetting : reportSettingList) {
                String projectId = reportSetting.getProjectId();
                String sendTime = reportSetting.getSendTime();
                String title = reportSetting.getTitle();
                String namespace = reportSetting.getNamespace();
                List<String> reportEmailList = reportSetting.getReportEmailList();
                ReportTemplete reportTemplete = reportTempleteService.getReportTemplete(reportSetting.getTempletId());
                String statistics = reportTemplete.getStatistics();

                // 檢查是否到報表發(fā)送時間
                if (checkSend(sendTime)) {
                    String[] metricNameKeyArray = reportTemplete.getMetricNameIdList().split(",");
                    FastDateFormat dateFormat = FastDateFormat.getInstance(DATE_PATTERN, TimeZone.getTimeZone("Asia/Shanghai"));
                    // 定義一封郵件的參數(shù)
                    Map<String, Object> params = new HashMap<>();
                    params.put("reportNamespaceDesc", descService.getNamespaceDesc(namespace));
                    params.put("reportTitle", title);
                    params.put("reportDate", dateFormat.format(new Date()));
                    List<ReportTable> reportTableList = new ArrayList<>();

                    String dimensionName;
                    if (metricNameKeyArray.length > 0) {
                        dimensionName = metricNameKeyArray[0].split("##")[2];
                    } else {
                        continue;
                    }
                    // 獲取用戶的實例資源列表
                    List<String> dimensionList = metricService.getActiveDimensionList(projectId, namespace, dimensionName, RECENT_HOURS);
                    if (dimensionList.size() == 0) {
                        continue;
                    }

                    // 每個實例標識一張表
                    for (String dimension : dimensionList) {
                        ReportTable reportTable = new ReportTable();
                        reportTable.setDimensionName("");
                        reportTable.setDimension(dimension);
                        reportTable.setStatistics(statistics);
                        List<ServiceData> serviceDataList = new ArrayList<>();
                        for (String metricNameKey : metricNameKeyArray) {
                            // 獲取監(jiān)控項的描述蝙茶、單位和數(shù)值
                            String metricNameDesc = "XXX";
                            String unitDesc = "XXX";
                            Double dimensionValue = 12.0;
                            ServiceData serviceData = new ServiceData();
                            serviceData.setMetricName(metricNameDesc);
                            serviceData.setMetricUnit(unitDesc);
                            serviceData.setMetricValue(dimensionValue);

                            serviceDataList.add(serviceData);
                        }

                        reportTable.setServiceDataList(serviceDataList);
                        reportTableList.add(reportTable);
                    }

                    params.put("reportTableList", reportTableList);
                    // 利用freemaker模板生成報表郵件內(nèi)容
                    String htmlContent = TemplateUtil.generateTemplate(FTL_FILE, params);

                    logger.info("htmlContent:" + htmlContent);

                    // 發(fā)送郵件
                    for (String email : reportEmailList) {
                        // 改順序發(fā)送為線程池調(diào)度發(fā)送
//                      boolean res = EmailUtils.SendEmail(email, title, htmlContent);
//                      if (!res){
//                          logger.error("郵件發(fā)送失敗");
//                      }
                        executorService.submit(new EmailSendThread(email, title, htmlContent));
                    }
                }
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 檢查是否需要發(fā)送報表
     *
     * @param sendTime
     * @return
     */
    private boolean checkSend(String sendTime) {
        //略去部分細節(jié)
    }

}

4.5 測試結(jié)果

收到的測試郵件內(nèi)容如下圖所示:


郵件.jpg

五、其他

郵件發(fā)送的內(nèi)容需要用到郵件服務(wù)器以及編寫郵件發(fā)送工具類诸老,這一細節(jié)不是本文的重點隆夯,后面考慮通過另外一篇文章單獨進行介紹,這里忽略别伏。

后續(xù)文章可能還會考慮介紹另外一種同樣比較常用的模板渲染引擎Velocity吮廉,敬請期待。

六畸肆、參考資料

  1. FreeMaker中文在線用戶手冊
  2. Java中FreeMaker的使用
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宦芦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子轴脐,更是在濱河造成了極大的恐慌调卑,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件大咱,死亡現(xiàn)場離奇詭異恬涧,居然都是意外死亡,警方通過查閱死者的電腦和手機碴巾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門溯捆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厦瓢,你說我怎么就攤上這事提揍∑≡拢” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵劳跃,是天一觀的道長谎仲。 經(jīng)常有香客問我,道長刨仑,這世上最難降的妖魔是什么郑诺? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮杉武,結(jié)果婚禮上辙诞,老公的妹妹穿的比我還像新娘。我一直安慰自己轻抱,他們只是感情好倘要,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著十拣,像睡著了一般封拧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夭问,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天泽西,我揣著相機與錄音,去河邊找鬼缰趋。 笑死捧杉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的秘血。 我是一名探鬼主播味抖,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灰粮!你這毒婦竟也來了仔涩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤粘舟,失蹤者是張志新(化名)和其女友劉穎熔脂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柑肴,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡霞揉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晰骑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片适秩。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秽荞,到底是詐尸還是另有隱情骤公,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布蚂会,位于F島的核電站,受9級特大地震影響耗式,放射性物質(zhì)發(fā)生泄漏胁住。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一刊咳、第九天 我趴在偏房一處隱蔽的房頂上張望彪见。 院中可真熱鬧,春花似錦娱挨、人聲如沸余指。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酵镜。三九已至,卻和暖如春柴钻,著一層夾襖步出監(jiān)牢的瞬間淮韭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工贴届, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留靠粪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓毫蚓,卻偏偏與公主長得像占键,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子元潘,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容