superword中的模板抽取实践

阅读更多

superword这个项目,全使用JAVA8新特性: https://github.com/ysc/superword ,一开始只是我的一个英语单词分析工具,用于生成HTML片段然后发到博客中,后来功能越来越强于是我就做成一个项目了,再后来有人跟我说自己不是计算机专业的不会用这个软件,于是我就改造成了一个WEB项目,这个项目现在有点需要改进的地方,就是把JAVA代码生成HTML的这个逻辑改成使用FREEMARKER的方式。

我们首先来看在org.apdplat.superword.system.AntiRobotFilter类中的原来的JAVA代码生成HTML的逻辑:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
StringBuilder html =  new  StringBuilder();
html.append( "

The meaning of red color font is your answer, but the right answer is the meaning of blue color font for the word " )

         .append(quizItem.getWord().getWord())
         .append( ":" );
html.append( "

    " );

for (String option : quizItem.getMeanings()){
     html.append( "
  • " );
  •      if (option.equals(_answer)) {
             html.append( "" ).append(option).append( "" );
         } else  if (option.equals(quizItem.getWord().getMeaning())){
             html.append( "" ).append(option).append( "" );
         } else {
             html.append(option);
         }
         html.append( "\n" );
    }
    html.append( "\n

    )

             .append(servletContext.getContextPath())
             .append( "\">Continue...\n" );

    这样的代码对JAVA开发人员来说,第一次写的时候很爽很方便,用于原型开发快速验证功能是可以的,不过如果隔的时间长了自己再回头来看或者其他人来看这段代码,就会很吃力,因为这里纠缠了JAVA和HTML,纠缠了业务逻辑、数据处理逻辑以及显示逻辑,所以,如果代码需要持续维护的话就需要重构,下面我们就使用FREEMARKER来重构。

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

    ?
    1
    2
    3
    4
    5
    6
    < dependency >
         < groupId >org.freemarker groupId >
         < artifactId >freemarker artifactId >
         < version >${freemarker.version} version >
    dependency >
    ?
    1
    < freemarker.version >2.3.24-incubating freemarker.version >

    第二步,在类路径下的template/freemarker/identify_quiz.ftlh文件中定义HTML模板:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    < 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 >
        
         ul >
    h2 >
    < h1 >
         < a  href = "" >Continue... a >
    h1 >

    第三步,org.apdplat.superword.system.AntiRobotFilter类中准备模板需要的数据:

    ?
    1
    2
    3
    Map data =  new  HashMap<>();
    data.put( "quizItem" , quizItem);
    data.put( "answer" , _answer);

    第四步,编写一个工具类org.apdplat.superword.freemarker.TemplateUtils,将模板和数据融合生成最终的HTML代码:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    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 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 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的逻辑,转而使用如下代码:

    ?
    1
    2
    3
    4
    Map data =  new  HashMap<>();
    data.put( "quizItem" , quizItem);
    data.put( "answer" , _answer);
    String html = TemplateUtils.getIdentifyQuiz(data);

     

    大功告成!看一下页面输出效果:

    superword中的模板抽取实践_第1张图片

     

    最后看一下模板引擎的日志输出,第一次访问:

    ?
    1
    2
    3
    4
    5
    6
    7
    开始初始化模板配置
    模板配置初始化完毕
    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"

    第二次:

    ?
    1
    2
    3
    4
    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.

    第三次:

    ?
    1
    2
    3
    4
    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.

     

     

     

     

     

    你可能感兴趣的:(superword,重构,模板,freemarker)