最近在学习Groovy,发现它提供的XML操作API非常方法。在看《卓有成效的程序员》一书时,有一节是通过用Groovy读取数据库中表的MetaData生成对应的Bean和iBatis文件,其中那段Groovy代码让我印象深刻。最近在看Groovy Recipes时刚好也看到了XML这一段,于是想着自己也尝试写个脚本来自生成配置文件这类非常繁琐的事情。
最近在开发时发现在写完一个Bean之后总要手动去更新Spring的配置文件,而且更新时经常来回拷贝,虽然有用多剪切板的功能,但是仍然觉得很繁琐。于是就有了将写一个自动生成更新配置文件的脚本的想法。刚好Groovy友好的XML API能胜任这一项工作。
因为公司里面写代码有很多的模式化的东西,因为在处理如何找到这些Bean简单,并不需要去考虑很多扩展性的问题。这个脚本的执行过程如下:
- 搜索特定目录列表下的类文件,根据统一的命名格式,确认这个类是不是一个Bean
- 生成Bean的名称及对应的Class全名的Map
- 针对Map中的数据生成Bean的配置文件
在写Groovy脚本时,用的是Intellij IDEA,提供语法高亮的支持,代码补全功能太弱,于是只能不断地查API文档。历经三个小时,终于写成了这个脚本,初步测试,可用性尚可。(发现我是那种没有IDE支持就啥也写不出来的烂程序员,伤心啊:()
下面是Groovy生成XML的代码,因此需要加入XML声明及DocType,因此选择了StreamingMarkupBuilder来生成XML,这个类生成的文件有些难看,不过在Eclipse下格式化一下,就美观了。生成XML的代码如下:
import java.util.regex.Pattern
import groovy.xml.StreamingMarkupBuilder
class XmlConfigBuilder {
static final DIRS = ["manager": "biz/core/src/java/",
"dao": "biz/dal/src/java/",
"ao": "biz/home/src/java/",
"xml": "bundle/war/src/webroot/WEB-INF/biz/bean"]
def ao = [:]
def manager = [:]
def dao = [:]
def findClasses(projPath) {
File aoFile = new File(projPath, DIRS.ao)
aoFile.eachFileRecurse {file ->
if (file.name.matches("Default[\\w]*AO.java")) {
def classPath = file.getPath().minus(aoFile.getPath())
ao.put(beanName(file.name), fullClassName(classPath))
}
}
File mfile = new File(projPath, DIRS.manager)
mfile.eachFileRecurse {file->
if (file.name.matches("Default[\\w]*Manager.java")) {
def classPath = file.getPath().minus(mfile.getPath())
manager.put(beanName(file.name), fullClassName(classPath))
}
}
File daoFile = new File(projPath, DIRS.dao)
daoFile.eachFileRecurse {file ->
if (file.name.matches("Ibatis[\\w]*DAO.java")) {
def classPath = file.getPath().minus(daoFile.getPath())
dao.put(beanName(file.name), fullClassName(classPath))
}
}
}
def beanName(fileName) {
fileName.substring(7, 8).toLowerCase() +
fileName.substring(8,fileName.length()-5)
}
def fullClassName(filePath) {
filePath.substring(1,filePath.length()-5).replaceAll(Pattern.quote(File.separator),
".")
}
def doctype = """< !DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">"""
def buildConfigXML(path) {
findClasses(path)
buildXml(path, "biz-ao.xml", ao)
buildXml(path, "biz-dao.xml", dao)
buildXml(path, "biz-manager.xml", manager)
}
private def buildXml(path, filename, map) {
def xmlWriter = new FileWriter("${path}/${filename}")
def xmlBuilder = new StreamingMarkupBuilder()
xmlBuilder.encoding = "GB2312"
def beans = {
mkp.xmlDeclaration()
unescaped < < doctype
beans("default-autowire": "byName") {
map.each {k, v ->
bean(id: k, class: v)
}
}
}
xmlWriter < < xmlBuilder.bind(beans)
}
}