superword中的模板抽取實踐 原 薦

superword這個項目,全使用JAVA8新特性: https://github.com/ysc/superword ,一開始只是我的一個英語單詞分析工具,用於生成HTML片段然後發到博客中,後來功能越來越強於是我就做成一個項目了,再後來有人跟我說自己不是計算機專業的不會用這個軟件,於是我就改造成了一個WEB項目,這個項目現在有點需要改進的地方,就是把JAVA代碼生成HTML的這個邏輯改成使用FREEMARKER的方式

我們首先來看在org.apdplat.superword.system.AntiRobotFilter類中的原來的JAVA代碼生成HTML的邏輯:

StringBuilder html = new StringBuilder();
html.append("<h1>The meaning of red color font is your answer, but the right answer is the meaning of blue color font for the word <font color=\"red\">")
        .append(quizItem.getWord().getWord())
        .append(":</font></h1>");
html.append("<h2><ul>");
for(String option : quizItem.getMeanings()){
    html.append("<li>");
    if(option.equals(_answer)) {
        html.append("<font color=\"red\">").append(option).append("</font>");
    }else if(option.equals(quizItem.getWord().getMeaning())){
        html.append("<font color=\"blue\">").append(option).append("</font>");
    }else{
        html.append(option);
    }
    html.append("</li>\n");
}
html.append("</ul></h2>\n<h1><a href=\"")
        .append(servletContext.getContextPath())
        .append("\">Continue...</a></h1>\n");

這樣的代碼對JAVA開發人員來說,第一次寫的時候很爽很方便,用於原型開發快速驗證功能是可以的,不過如果隔的時間長了自己再回頭來看或者其他人來看這段代碼,就會很吃力,因爲這裏糾纏了JAVA和HTML,糾纏了業務邏輯、數據處理邏輯以及顯示邏輯,所以,如果代碼需要持續維護的話就需要重構,下面我們就使用FREEMARKER來重構。

第一步,在pom.xml中引入FREEMARKER的依賴:

<!-- html模板引擎 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>${freemarker.version}</version>
</dependency>
<freemarker.version>2.3.24-incubating</freemarker.version>

第二步,在類路徑下的template/freemarker/identify_quiz.ftlh文件中定義HTML模板:

<h1>
    The meaning of red color font is your answer, but the right answer is the meaning of blue color font for the word <font color="red">${quizItem.word.word}:</font>
</h1>
<h2>
    <ul>
<#list quizItem.meanings as meaning>
    <#if meaning == answer>
    <#--  用戶答案 -->
        <li><font color="red">${meaning}</font></li>
    <#elseif meaning == quizItem.word.meaning>
    <#--  正確答案 -->
        <li><font color="blue">${meaning}</font></li>
    <#else>
    <#--  其他選項 -->
        <li>${meaning}</li>
    </#if>
</#list>
    </ul>
</h2>
<h1>
    <a href="">Continue...</a>
</h1>

第三步,org.apdplat.superword.system.AntiRobotFilter類中準備模板需要的數據:

Map<String, Object> data = new HashMap<>();
data.put("quizItem", quizItem);
data.put("answer", _answer);

第四步,編寫一個工具類org.apdplat.superword.freemarker.TemplateUtils,將模板和數據融合生成最終的HTML代碼:

package org.apdplat.superword.freemarker;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import org.apdplat.superword.model.QuizItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * 模板工具, 用於生成html代碼
 * Created by ysc on 4/2/16.
 */
public class TemplateUtils {
    private TemplateUtils(){}
    private static final Logger LOGGER = LoggerFactory.getLogger(TemplateUtils.class);
    private static final Configuration CFG = new Configuration(Configuration.VERSION_2_3_23);

    static{
        LOGGER.info("開始初始化模板配置");
        CFG.setClassLoaderForTemplateLoading(TemplateUtils.class.getClassLoader(), "/template/freemarker/");
        CFG.setDefaultEncoding("UTF-8");
        if(LOGGER.isDebugEnabled()) {
            CFG.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
        }else{
            CFG.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        }
        CFG.setLogTemplateExceptions(false);
        LOGGER.info("模板配置初始化完畢");
    }

    /**
     * 在識別用戶是否是機器人的測試中, 如果用戶測試失敗, 則向用戶顯示這裏生成的HTML代碼
     * @param data 需要兩個數據項, 一是測試數據集quizItem, 二是用戶的回答answer
     * @return 測試結果HTML代碼
     */
    public static String getIdentifyQuiz(Map<String, Object> data){
        try {
            Template template = CFG.getTemplate("identify_quiz.ftlh");
            Writer out = new StringWriter();
            template.process(data, out);
            return out.toString();
        }catch (Exception e){
            LOGGER.error("generate authentication template failed", e);
        }
        return "";
    }

    public static void main(String[] args) {
        Map<String, Object> data = new HashMap<>();
        QuizItem quizItem = QuizItem.buildIdentifyHumanQuiz(12);
        data.put("quizItem", quizItem);
        data.put("answer", "random answer");
        System.out.println(TemplateUtils.getIdentifyQuiz(data));
    }
}

第五步,org.apdplat.superword.system.AntiRobotFilter類中刪除JAVA代碼生成HTML的邏輯,轉而使用如下代碼:

Map<String, Object> data = new HashMap<>();
data.put("quizItem", quizItem);
data.put("answer", _answer);
String html = TemplateUtils.getIdentifyQuiz(data);


大功告成!看一下頁面輸出效果:


最後看一下模板引擎的日誌輸出,第一次訪問:

開始初始化模板配置
模板配置初始化完畢
0    DEBUG [2016-04-02 22:04:25]  Couldn't find template in cache for "identify_quiz.ftlh"("en_US", UTF-8, parsed); will try to load it.
1    DEBUG [2016-04-02 22:04:25]  TemplateLoader.findTemplateSource("identify_quiz_en_US.ftlh"): Not found
2    DEBUG [2016-04-02 22:04:25]  TemplateLoader.findTemplateSource("identify_quiz_en.ftlh"): Not found
2    DEBUG [2016-04-02 22:04:25]  TemplateLoader.findTemplateSource("identify_quiz.ftlh"): Found
2    DEBUG [2016-04-02 22:04:25]  Loading template for "identify_quiz.ftlh"("en_US", UTF-8, parsed) from "file:/Users/ysc/workspace/superword/target/superword-1.0/WEB-INF/classes/template/freemarker/identify_quiz.ftlh"

第二次:

5324 DEBUG [2016-04-02 22:04:30]  TemplateLoader.findTemplateSource("identify_quiz_en_US.ftlh"): Not found
5325 DEBUG [2016-04-02 22:04:30]  TemplateLoader.findTemplateSource("identify_quiz_en.ftlh"): Not found
5325 DEBUG [2016-04-02 22:04:30]  TemplateLoader.findTemplateSource("identify_quiz.ftlh"): Found
5325 DEBUG [2016-04-02 22:04:30]  "identify_quiz.ftlh"("en_US", UTF-8, parsed): using cached since file:/Users/ysc/workspace/superword/target/superword-1.0/WEB-INF/classes/template/freemarker/identify_quiz.ftlh hasn't changed.

第三次:

81642 DEBUG [2016-04-02 22:05:47]  TemplateLoader.findTemplateSource("identify_quiz_en_US.ftlh"): Not found
81643 DEBUG [2016-04-02 22:05:47]  TemplateLoader.findTemplateSource("identify_quiz_en.ftlh"): Not found
81643 DEBUG [2016-04-02 22:05:47]  TemplateLoader.findTemplateSource("identify_quiz.ftlh"): Found
81643 DEBUG [2016-04-02 22:05:47]  "identify_quiz.ftlh"("en_US", UTF-8, parsed): using cached since file:/Users/ysc/workspace/superword/target/superword-1.0/WEB-INF/classes/template/freemarker/identify_quiz.ftlh hasn't changed.


這次重構的完整代碼見:https://github.com/ysc/superword/commit/a46b48a352106143ce3a10964b1a98f45a961944,superword中還有一些地方需要做類似的重構,有興趣的同學可以嘗試一下,測試成功後歡迎在github上面給我發pull request.






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章