在开始具体的编码之前先来看一下项目的主要结构图:
代码的主要结构如上图所示,下面对每一部分逐一介绍:
- EasyCodeStarter 工具启动的入口,main方法声明类,主要调用XmlParser解析配置文件,GenerationOrganizer进行代码生成。
- XmlParser 配置文件的解析类,所有解析后的信息都将被存放到EasyCodeContext类中保存。
- GenerationOrganizer具体的代码生成工具组织者,外围的一些操作如数据库连接的获取、关闭等统一由该类完成。
- EasyCodeContext 存放配置文件解析后的信息,供代码生成时获取使用,该类没有任务业务逻辑。
- DBUtils 主要负责数据库连接的获取和关闭。
- DatabaseProvider 数据库提供者接口,实现表的元信息查询,可以有不同的数据库实现。
- EasyCodeGenerator 具体的代码生成接口,里面只有一个方法doGenerate。
- AbstractEasyCodeGenerator 代码生成接口的抽象实现,使用了模板模式。例如每一份代码的生成都需要在最后创建文件,像这类通用的操作在这里完成,具体的代码内容构建则由继承它的子类完成,实现它的抽象方法generate。另外在这里进行代码构建之前会先调用插件(虽然这功能比较鸡肋)。
- DefaultCodeGenerator 默认提供的代码生成内容构建实现,常规的代码生成需求一般都可以满足。
- FileUtils 代码文件的创建。
了解了它的结构,对于下一步要进行什么应该很清楚了吧。当然是进行配置文件的解析,创建我们的XmlParser类,因为一切任务的开始都要依赖于它。
在进行解析之前有必要说明一下,配置文件是xml格式的,目前对于xml的解析强大的第三方实现有很多,但是鉴于代码生成工具本身的特殊性以及它的使用场景,我们并不需要多么强大的功能,相反我们希望它依赖的包越少越好,因此这里使用了jdk自带的一些简单工具类,并没有使用第三方的实现如dom4j、Digester等。
下面是XmlParser类的源码:
/** * XML配置文件解析类 * * User: liyd * Date: 13-11-20 * Time: 下午2:53 */ public class XmlParser { /** 日志对象 */ private static final Logger LOG = LoggerFactory.getLogger(XmlParser.class); /** 文档构建对象 */ private static DocumentBuilder documentBuilder; /** * 获取doc builder */ private synchronized static DocumentBuilder getDocBuilder() { try { if (documentBuilder == null) { //创建xml解析doc对象 documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } return documentBuilder; } catch (ParserConfigurationException e) { LOG.error("创建xml解析对象失败", e); throw new EasyCodeException("创建xml解析对象失败"); } } /** * 解析配置文件 * * @param configFile */ public static void parseConfigXml(String configFile) { LOG.info("开始解析配置文件{}", configFile); Document doc; try { //创建xml解析doc对象 doc = getDocBuilder().parse( Thread.currentThread().getContextClassLoader().getResourceAsStream(configFile)); //解析常量 Map<String, String> constantMap = parseConstant(doc); EasyCodeContext.addConstant(constantMap); //解析配置文件中的常量使用 String context = VelocityUtils.parseTemplate(configFile, EasyCodeContext.getAllConstant()); //重新构建处理过的配置文件doc对象 doc = getDocBuilder().parse(new ByteArrayInputStream(context.getBytes())); NodeList childNodes = doc.getDocumentElement().getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node.getNodeType() == Node.TEXT_NODE) { continue; } if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.JDBC_TAG)) { //解析数据库配置 String[] strings = parseJdbcConfig(node); EasyCodeContext.addJdbcConfig(strings[0], strings[1]); } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.CONVERT_TAG)) { //解析数据转换配置 String[] strings = parseConvertConfig(node); ConvertType convertType = new ConvertType(); convertType.setDbType(strings[0]); convertType.setJdbcType(strings[1]); convertType.setJavaClass(strings[2]); EasyCodeContext.addDataConvertType(strings[0], convertType); } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.INCLUDE_TAG)) { //解析include标签 parseIncludeFile(node); } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TABLE_TAG)) { //解析表配置 Table table = parseTableConfig(node); EasyCodeContext.addTable(table.getName(), table); } else if (StringUtils.equalsIgnoreCase(node.getNodeName(), XmlTag.TASKS_TAG)) { //解析任务配置 Map<String, Task> taskMap = parseTaskConfig(node); EasyCodeContext.addTask(taskMap); } } } catch (Exception e) { LOG.error("配置文件解析失败", e); throw new EasyCodeException("配置文件解析失败"); } } /** * 解析任务配置 * * @param node the node * @return the map */ private static Map<String, Task> parseTaskConfig(Node node) { NodeList taskList = node.getChildNodes(); if (taskList == null || taskList.getLength() == 0) { LOG.info("没有定义代码生成任务"); throw new EasyCodeException("没有定义代码生成任务"); } Map<String, Task> taskMap = new HashMap<String, Task>(); for (int i = 0; i < taskList.getLength(); i++) { Node taskNode = taskList.item(i); if (taskNode.getNodeType() == Node.TEXT_NODE) { continue; } NamedNodeMap namedNodeMap = taskNode.getAttributes(); String taskName = namedNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); String clazz = namedNodeMap.getNamedItem(XmlTag.CLASS_ATTR).getNodeValue(); Task task = new Task(); task.setName(taskName); task.setClazz(clazz); task.setClassInstance(ClassUtils.getGeneratorInstance(clazz)); //解析子标签配置信息 parseChildConfig(taskNode, task); taskMap.put(taskName, task); } return taskMap; } /** * 解析属性 * * @param node the node * @param task the task */ private static void parseChildConfig(Node node, Task task) { NodeList nodeList = node.getChildNodes(); Map<String, Property> propertyMap = new HashMap<String, Property>(); Map<String, EasyCodePlugin> pluginMap = new HashMap<String, EasyCodePlugin>(); task.setProperties(propertyMap); task.setPluginMap(pluginMap); if (nodeList == null || nodeList.getLength() == 0) { return; } //处理属性配置信息 for (int i = 0; i < nodeList.getLength(); i++) { Node childNode = nodeList.item(i); if (childNode.getNodeType() == Node.TEXT_NODE) { continue; } NamedNodeMap propertyNodeMap = childNode.getAttributes(); //属性名称 String propertyName = propertyNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); //属性值 String propertyValue = propertyNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue(); if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PLUGIN_TAG)) { //插件对象 EasyCodePlugin pluginInstance = ClassUtils.getPluginInstance(propertyValue); pluginMap.put(propertyName, pluginInstance); } else if (StringUtils.equalsIgnoreCase(childNode.getNodeName(), XmlTag.PROPERTY_TAG)) { //属性对象 Property property = new Property(); property.setName(propertyName); property.setValue(propertyValue); propertyMap.put(propertyName, property); } } } /** * 解析表配置 * * @param node the node * @return the table */ private static Table parseTableConfig(Node node) { NamedNodeMap tableNodeMap = node.getAttributes(); //表名称 String tableName = tableNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); String tableDesc = tableNodeMap.getNamedItem(XmlTag.DESC_ATTR).getNodeValue(); if (StringUtils.isBlank(tableName)) { LOG.info("没有指定表名"); throw new EasyCodeException("没有指定表名"); } Table table = new Table(); table.setName(tableName); table.setDesc(tableDesc); //解析表的子属性配置信息 parseTableChildConfig(node, table); return table; } /** * 解析表的子属性配置信息 * * @param tableNode * @param table */ private static void parseTableChildConfig(Node tableNode, Table table) { //表的子标签列表 NodeList childNodes = tableNode.getChildNodes(); if (childNodes == null || childNodes.getLength() == 0) { LOG.info("没有配置表的具体生成信息"); return; } for (int i = 0; i < childNodes.getLength(); i++) { //子节点 Node node = childNodes.item(i); String nodeName = node.getNodeName(); if (StringUtils.equalsIgnoreCase(XmlTag.TASKS_TAG, nodeName)) { parseTasks(node, table); } } } /** * 解析表的tasks * * @param tasksNode * @param table */ private static void parseTasks(Node tasksNode, Table table) { //task子标签 NodeList taskNodes = tasksNode.getChildNodes(); if (taskNodes == null || taskNodes.getLength() == 0) { LOG.info("没有配置表的任务信息"); return; } for (int i = 0; i < taskNodes.getLength(); i++) { //节点 Node node = taskNodes.item(i); if (node.getNodeType() == Node.TEXT_NODE) { continue; } String nodeName = node.getNodeName(); NamedNodeMap columnNodeMap = node.getAttributes(); String taskName = columnNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); if (StringUtils.equalsIgnoreCase(XmlTag.TASK_TAG, nodeName)) { table.addTask(taskName); } } } /** * 解析数据库配置 * * @param node the node */ private static String[] parseJdbcConfig(Node node) { NamedNodeMap dbNodeMap = node.getAttributes(); String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue(); return new String[] { name, value }; } /** * 解析数据转换配置 * * @param node * @return */ private static String[] parseConvertConfig(Node node) { NamedNodeMap dbNodeMap = node.getAttributes(); String dbType = dbNodeMap.getNamedItem(XmlTag.DB_TYPE_ATTR).getNodeValue(); String jdbcType = dbNodeMap.getNamedItem(XmlTag.JDBC_TYPE_ATTR).getNodeValue(); String javaType = dbNodeMap.getNamedItem(XmlTag.JAVA_TYPE_ATTR).getNodeValue(); return new String[] { dbType, jdbcType, javaType }; } /** * 解析常量配置 * * @param doc */ private static Map<String, String> parseConstant(Document doc) { Map<String, String> constantMap = new HashMap<String, String>(); NodeList jdbcConfigList = doc.getElementsByTagName(XmlTag.CONSTANT_TAG); if (jdbcConfigList == null || jdbcConfigList.getLength() == 0) { return constantMap; } for (int i = 0; i < jdbcConfigList.getLength(); i++) { Node dbNode = jdbcConfigList.item(i); NamedNodeMap dbNodeMap = dbNode.getAttributes(); String name = dbNodeMap.getNamedItem(XmlTag.NAME_ATTR).getNodeValue(); String value = dbNodeMap.getNamedItem(XmlTag.VALUE_ATTR).getNodeValue(); constantMap.put(name, value); } return constantMap; } /** * 解析include的xml * * @param node */ private static void parseIncludeFile(Node node) { //处理include文件 NamedNodeMap includeNodeMap = node.getAttributes(); String includeFile = includeNodeMap.getNamedItem(XmlTag.FILE_ATTR).getNodeValue(); //递归解析 parseConfigXml(includeFile); } }
需要说明的是常量的解析因为要支持常量的定义使用,所以这里是先解析出常量,之后用velocity引擎对xml进行一次常量解析并返回解析后的内容,把该内容构建成Document对象后进行具体的任务解析。
最终的解析结果都存放在EasyCodeContext类中,以供后面代码生成时取用,这中间建立了几个实体对象Table、Column、Property、Task、ConvertType用来方便保存数据。
解析的工作完成,下面就要编写代码生成的组织者GenerationOrganizer了,待续,,,