代码生成工具原理:
上篇对代码生成工具的基本实现流程进行了描述,那个只是对他的流程从外层进行了简述。这里对其内部的实现细节进行描述。
举例:
根据hibernate的映射文件xx.hbm.xml文件生成EJB、DAO、Delegate、JSP、struts配置信息、EJB配置信息。
为了简便期间这里只就DAO文件的生成进行描述。
1. xx.hbm.xml
- <?xml version="1.0"?>
- <hibernate-mapping>
- <class
- name="com.verran.student"
- table="CUST_VOCA"
- >
- <id
- name=" student Id"
- type="java.lang.Integer"
- column="STUDENT_ID"
- >
- <generator class="assigned" />
- </id>
- <property
- name="name"
- type="java.lang.String"
- column="NAME"
- not-null="true"
- length="45"
- />
- </class>
- </hibernate-mapping>
2. build.xml核心部分
- <target name="generate" depends="prepare-error" if="AnakiaExtTask2.present">
- <delete dir="${code.dest}" quiet="true"/>
- <anakia2 basedir="${code.src}" destdir="${code.dest}/"
- extension=".java" style="vm/*.vsl"
- includes="hbm/*.hbm.xml"
- lastModifiedCheck="on"
- moduleName="rm"
- templatePath="./"
- />
- </target>
Extension 属性指定了默认状态下生成文件的后缀名。
Style:
This is the path (relative to Velocity's template.loader.1.template.path) to the VelocityStyleTemplate to process
Includes 指定要进行处理的文件,这里指的就是我们要操作的xx.hbm.xml
lastModifiedCheck 指定是否对最后一次修改进行记录
This turns on or off the ability to check the last modified date on files in order to determine whether or not they need to be re-rendered or not. The value of this attribute can be "true, false, yes, no". By default, it is true, meaning that the last modified date should be checked and if the original .xml file, project file, or .vsl file have not changed, then don't process the page. This accelerates processing because pages that have not changed will not get reprocessed.
moduleName 这个属性不是Anakia的标准属性,是自定义的一个属性,指定项目文件所做的模块
templatePath:
This is the path to the templateRoot which is the directory where your site.vsl file is located. This can be defined in the Velocity.properties or it can be defined here. It it an optional argument if it is defined in the Velocity properties file already. However, if defined, this value will override the path defined in the Velocity.properties file.
3. AnakiaExtTask2 ant任务的核心部分
- OutputWrapper ow = new OutputWrapper();
- //ow.setEncoding(encoding);
- // ow.setFormat(encoding);
- context.put("root", root.getRootElement());
- context.put("xmlout", ow);
- context.put("relativePath", getRelativePath(xmlFile));
- context.put("treeWalk", new TreeWalker());
- context.put("xpath", new XPathTool());
- context.put("escape", new Escape());
- context.put("date", new java.util.Date());
- context.put("moduleName", this.moduleName);
- // only put this into the context if it exists.
- if (projectDocument != null) {
- context.put("project", projectDocument.getRootElement());
- }
把从xx.hbm.xml文件中解析出来的信息置入context中以便在vsl文件中获得相关信息。。
4. vsl文件部分信息
- #macro ( generateDAO $classNode )
- ## get class name for generated filename
- #set ($fullClassName = $classNode.selectNodes("@name").get(0).getValue())
- #end
- #if ($fullClassName.lastIndexOf(".") > 0 )
- #set ($pos = $fullClassName.lastIndexOf("."))
- #set ($pos = $pos + 1)
- #set ($className = $fullClassName.substring($pos))
- #else
- #set ($className = $fullClassName)
- #end
- #set ($columnNodes = $classNode.selectNodes("property"))
- #foreach ($column in $columnNodes)
- #set ($columnName = $column.selectNodes("@name").get(0).getValue())
- #if ( $columnName == "createDate")
- #set ( $hasCreateDateCol = true)
- #elseif ( $columnName == "stsDate")
- #set ( $hasStsDateCol = true )
- #elseif ( $columnName == "sts")
- #set ( $hasStsCol = true )
- #elseif ( $columnName == "name" )
- #set ( $hasNameCol = true )
- #end
- #end
- #end
调用上面的宏
- #set ($classes = $root.selectNodes("class"))
- #foreach ( $class in $classes)
- #generateDAO($class)
- #end
generateDAO 的参数是从xx.hbm.xml文件中获得节点名为class的节点。
$root和步骤3中的context.put("root", root.getRootElement());
对应,$root表示了xx.hbm,.xml文件的跟元素。
$classNode.selectNodes("@name").get(0).getValue())
代表从名称为class的节点中获得属性名称为name并且或的第一个的值,对应上面的xml文件的话就是com.verran.student。
5. 总结
实质上这个转换过程就是利用Anakia 然后它调用Velocity,Jdom等对xml文件进行分析生成Document然后将Document中
的相关信息置入Velocity的Context中。这样vsl文件就可以利用相应的命令获得存放在context中的值了。
6. 应用
6. 应用
可能有人会问这些有什么用啊,就信息系统而言一个项目的架构确定下来后他的很多代码都是类似的,无非就是增删查改区别只是在于他们的架构的不同,对每一层的处理技术不同。
这样当架构确定下来以后,其实不同模块之间的代码没有很多的区别无非就是类明的不同,而这些类名的不同完全可以进行规范比如让他们依赖于他们所操作的表名称,这有点类似
于ror中的“命名归约”了。有了这样的一个生成工具(当然模版信息是由你去开发的)我们就可以很方便的生成模块的结构,这样开发人员的注意力主要就放在了根据原型修改页面
和进行具体的业务实现,而无需为每一个公共信息进行定义。
7. 注意
这里代码生成工具的强大与否其实主要在于你对vsl文件的定义,如果你可以定义一个通用性很强的vsl文件那么将大大提高开发效率,同事也对代码的结构进行了规范。