• 近两年项目回顾系列——velocity模板引擎


    提起模板引擎,大多数做J2EE的人第一反应是freemarker。毕竟freemarker出的时间早,使用的人也较多,常在互联网项目中使用。freemarker比起jsp来说不用编译,速度更快。但我们项目组的View层主要是Flex富客户端搞定,有模板引擎需求的时候选择了相对简单和轻量的velocity。下面回顾一下自己用velocity模板引擎写了一个代码生成器。
    业务背景:
      系统中共有千余张表。主要的申请信息、注册信息、流程信息约有400余张表。由于用户人为操作原因、录入/校对失误、系统BUG等难免会有错误数据出现。对错误数据的纠正之前总局内长期依赖于计算机处管理员用TOAD/PL SQL等工具去库里改,这种方式比较危险,且操作非常不便。所以新系统中需要一个可视化、全表信息均可修改的数据维护功能模块。
    主要问题:
      维护的表非常非常多,上面提到至少也需要维护数百张表。开发这样的模块若为没一张表开发页面则工作量非常巨大。和架构师商量,我们大体有两种思路:1、每一张表的页面动态生成,每次选择所需维护的表时,查该表所有字段,一一罗列出来即可。2、使用模板引擎,为每一张表生成一个相对合理布局的维护页面。经过讨论我们选择了后者,主要是前者动态生成的页面可能非常丑,样式、布局、拆分合并等操作在Flex的as中控制起来比较麻烦。
    最终实现:
      使用velocity模板引擎为对应的数百张表生成信息维护页面。代码包括展现部分(input、radio、checkbook、textarea、datagrid等元素组成)、事件注册、callback、对外接口等。废话不说,上代码:

    核心方法—根据模板和参数使用VelocityEngine获取字符串:

      /**
         * 根据模板取得传入参数后的字符串
         * 
         * @param tmpPath
         *            模板路径
         * @param tmpFile
         *            模板名称,如果为空则抛出异常
         * @param encoding
         *            取模板的字符集,如果为空则按照默认的utf-8
         * @param names
         *            所有变量名称集合
         * @param values
         *            所有变令对应的值
         * @return 返回生成的对应的视图的字符串
         * @throws Exception
         */
        public static synchronized String getStringByTemplateFile(String tmpPath,String tmpFile, String encoding, String[] names, Object[] values)throws Exception {
            if (tmpFile == null || tmpFile.equals(""))
                throw new Exception("取得模板文件名称为空,无法生成对应的");
            // 默认字符集为gbk
            if (encoding == null || encoding.equals(""))
                encoding = "gbk";
            int len = 0;// 设置初始化变量个数
            // 通过计算取names和values中个数少的为长度,有一个为null,则就不向里面设置变量,则得到就是视图模板
            if (names != null && values != null)
                len = names.length < values.length ? names.length : values.length;
            // 初始化环境变量
            Properties p = new Properties();
            p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, tmpPath);// 存放模板的地址
            VelocityEngine ve = new VelocityEngine();
            ve.init(p);
            // 根据模板生成对应的对象
            Template template = ve.getTemplate(tmpFile, encoding);
            VelocityContext context = new VelocityContext();
            for (int i = 0; i < len; i++)
                context.put(names[i], values[i]);
            StringWriter writer = new StringWriter();
            template.merge(context, writer);
            return writer.toString();
        }

    然后用IO将此文件写到名为“维护类型_维护表对应POJO类名.mxml”中。


    下面是一个velocity模板的代码(makeAppFormMXMLGroup.vm):
    其中有一些简单的循环、分支。

    <?xml version="1.0" encoding="utf-8"?>
    <dc:Group xmlns:fx="http://ns.adobe.com/mxml/2009" 
             xmlns:s="library://ns.adobe.com/flex/spark" 
             xmlns:mx="library://ns.adobe.com/flex/mx"
             xmlns:dc="http://digitalchina.dc.tm"
             name="数据维护-申请信息维护-$!{tab.tabComments}维护">
             
        <!-- 创建人:liujian       创建时间:$!{info.createTime} *************** -->
        
        <!-- 页面布局    ******************************************** -->
        <dc:layout>
            <s:BasicLayout/>
        </dc:layout>
        
        <!-- metada       ****************元数据********************** -->
        
        
        <!-- script       ******************AS脚本********************** -->    
        <fx:Script>
            <![CDATA[
                 
                import $!{info.modePackageAs}.$!{tab.clsName};
                import com.dc.business.tmaas.sjwh.components.SjwhUtils;
    
                [Bindable]
                public var data_$!{tab.clsName}:$!{tab.clsName} = null;            //$!{tab.tabComments}对象,以"data_"+申请书对象命名
                ##计算每一行的高度
                #set($rowNum = $!{tcList.size()}/2)
                #if($!{tcList.size()}%2 != 0)
                    #set($rowNum = $rowNum + 1)
                #end
                
                #set ($heightRate = 100/$rowNum)
                
                /**
                 * 重置本页数据
                 * */
                public function reset():void{
                    data_$!{tab.clsName} = new $!{tab.clsName}();
                    //重置表单样式,必须
                    SjwhUtils.resetTableStyle(mainTable);
                    //页面有radio、dropdownList时,重置本页的radio、dropdownList
                }
                
                /**
                 * 刷新本页数据方法,包含初始化下拉菜单等操作 
                 * */
                public function refresh():void{
                    //有dropdownList的场合,初始化dropdownList。
    //                if(data_TTmaasAppXXXXAppform.xxx == xxx){
    //                    someDropDownList.selectedIndex = xxx;
    //                }
                }
    
                /**
                 * 申请信息变化监听
                 * */
                protected function appInfoChangedHandler(labelName:String, event:Event):void{
                    if(data_$!{tab.clsName} != null){
                        parentDocument.conValueChangeHandler(event.currentTarget, data_$!{tab.clsName}, labelName);
                    }
                }
            ]]>
        </fx:Script>
        
        <!-- declarations ********************声明非可视化组件************ -->
        
        <fx:Declarations>
            <!-- 将非可视元素(例如服务、值对象)放在此处 -->
            <mx:DateFormatter id="df" formatString="YYYY-MM-DD"/>
        </fx:Declarations>
        
        <!-- UICpmponents ****************可视化组件********************* -->
        <dc:Table width="100%" height="100%" id="mainTable">
            <dc:Tr width="100%" height="$heightRate%">
    ##设置一个变量,用于记录是奇数,偶数。
    #set ($i=0)
    #foreach( $column in $tcList)
    #set($i=$i+1)
                <dc:Td horizontalAlign="right" verticalAlign="middle" styleName="tdColor" width="20%">
                    <dc:Label  text="$!{column.colComments}" styleName="headerFont"/>
                </dc:Td>
                <dc:Td horizontalAlign="left" verticalAlign="middle" #if($column.dataType != 'DATE')paddingLeft="10"#end width="40%" #if($i == $!{tcList.size()} && $!{tcList.size()}%2 != 0)colSpan="3"#end>
    #if($column.dataType == 'DATE')
                    <dc:DateField formatString="YYYY-MM-DD" text="{df.format(data_$!{tab.clsName}.$!{column.fieldName})}" name="$!{column.fieldName}" width="100%" height="100%" change="appInfoChangedHandler('$!{column.colComments}',event)"/>
    #else
                    <dc:TextArea name="$!{column.fieldName}" text="{data_$!{tab.clsName}.$!{column.fieldName}}" #if($!{column.fieldName}== 'id' || $!{column.fieldName}== 'barCode' || $!{column.fieldName}== 'appNum') editable="false"  #end borderVisible="false" width="99%" height="99%" focusOut="appInfoChangedHandler('$!{column.colComments}',event)"/>
    #end
                </dc:Td>
    ##偶数行/末尾行,关闭TR。
    #if($i % 2 == 0 || $i == $!{tcList.size()})
            </dc:Tr>
    #end
    ##偶数且非末尾行,开启新TR
    #if($i%2 == 0 && $i != $!{tcList.size()})
            <dc:Tr width="100%" height="$heightRate%">
    #end
    #end
        </dc:Table>
    </dc:Group>

    下面是生成一些文件的结果:

    然后使用一个NavigatorContent放到ViewStack中,creationPolicy="auto"自动创建。(这样在选择一个表的时候才创建页面对象,否则设置成all,数百个页面创建会内纯溢出。)


    点击时,创建维护对象:


    后来,经过引申,开发了一个功能更强大的代码生成器:
    使用velocity模板生成hibernate的POJO、mapping文件,前台actionscript的model等等:

    之后,当库结构发生改变时,用此页面重新生成POJO和mapping文件,打包即可。

  • 相关阅读:
    黑马程序员——JAVA基础之主函数main和静态static,静态代码块
    黑马程序员——JAVA基础之final this.和super.的区别
    黑马程序员——JAVA基础之构造函数,构造代码块
    黑马程序员——JAVA基础之简述 类的封装
    黑马程序员——JAVA基础之简述面向对象,类,变量,匿名对象
    NBU Rman异机恢复Oracle
    Oracle的Rman差异增量备份
    rman备份出现ORA-19625
    查询rman备份信息常用指令
    RMAN-06172: no AUTOBACKUP found or specified handle is not a valid copy or piece
  • 原文地址:https://www.cnblogs.com/radio/p/3068834.html
Copyright © 2020-2023  润新知