软件工程应用与实践(11)——工具类分析
Posted 叶卡捷琳堡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件工程应用与实践(11)——工具类分析相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
文章目录
一、概述
在老年健康管理系统中,有很多工具类,这些工具类在项目中扮演了关键的角色,很多功能都利用这些工具类实现,经过小组成员的讨论和交流,本篇博客主要介绍老年健康管理系统中的工具类。理解这些工具类,有利于我们理解整个系统的一些实现。
本项目的工具类主要有以下几个
generater项目的utils包下的两个工具类
tools项目下的search.lucene.util包下的几个工具类
gate项目下utils包下的工具类
common项目下的几个工具类
在本篇博客中,主要针对其中的几个类进行分析,在下一篇博客中,还会对剩下的几个类进行剖析
二、主要代码
2.1 DateUtils
DateUtil类的作用比较简单,主要提供了对日期进行格式化的方法
可以看到,在DataUtil类中定义了两个用final修饰符修饰的常量,一个是DATE_PATTERN,一个是DATE_TIME_PATTERN。分别规定了日期和含时间日期的类型
定义了两个方法(重载的方法),一个是format(Date date)方法,另一个是format(Date date, String pattern),即在其他程序中,既可以调用已经准备好的样式对日期进行格式化,也可根据具体的需要调用程序。
这个类底层的实现使用了Java的Date类
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtils
/** 时间格式(yyyy-MM-dd) */
public final static String DATE_PATTERN = "yyyy-MM-dd";
/** 时间格式(yyyy-MM-dd HH:mm:ss) */
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static String format(Date date)
return format(date, DATE_PATTERN);
public static String format(Date date, String pattern)
if(date != null)
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
return null;
2.2 GeneratorUtils
这个类的主要作用是,将项目中的.vm文件生成对应的代码。.vm文件是velocity模板引擎的一种页面控制文件,而velocity是一个基于Java的模板引擎,他可以让程序员使用简单的模板语言引用Java代码定义的对象,实现界面和 Java 代码的分离。在本项目中,velocity模板主要用于生成Java代码
首先我们可以看到,该类中引入了Java中关于IO读取和Zip读取的几个核心类
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
接下来我们查看构造方法
在构造方法中,首先创建了一个ArrayList对象,接着将.vm文件的路径加入这个List集合中,在对应的路径下,我们可以看到这些.vm文件
public static List<String> getTemplates()
List<String> templates = new ArrayList<String>();
templates.add("template/index.js.vm");
templates.add("template/index.vue.vm");
templates.add("template/mapper.xml.vm");
templates.add("template/biz.java.vm");
templates.add("template/entity.java.vm");
templates.add("template/mapper.java.vm");
templates.add("template/controller.java.vm");
return templates;
接下来我们看到generatorCode方法
在这个方法里需要传入表名的Map,列名等信息,在这个方法中,首先对这些信息(表信息,列信息等)进行了初始化操作,接着设置velocity资源加载器,并将对应的信息添加到模板中。关于方法中具体语句的细节,有相应的注释进行说明。
/**
* 生成代码
*/
public static void generatorCode(Map<String, String> table,
List<Map<String, String>> columns, ZipOutputStream zip, String author, String path, String mainModule, String tablePrefix)
//配置信息
Configuration config = getConfig();
//表信息
TableEntity tableEntity = new TableEntity();
tableEntity.setTableName(table.get("tableName"));
tableEntity.setComments(table.get("tableComment"));
//表名转换成Java类名
String className = tableToJava(tableEntity.getTableName(), StringUtils.isBlank(tablePrefix) ? config.getString("tablePrefix") : tablePrefix);
tableEntity.setClassName(className);
tableEntity.setClassname(StringUtils.uncapitalize(className));
//列信息
List<ColumnEntity> columsList = new ArrayList<>();
for (Map<String, String> column : columns)
ColumnEntity columnEntity = new ColumnEntity();
columnEntity.setColumnName(column.get("columnName"));
columnEntity.setDataType(column.get("dataType"));
columnEntity.setComments(column.get("columnComment"));
columnEntity.setExtra(column.get("extra"));
//列名转换成Java属性名
String attrName = columnToJava(columnEntity.getColumnName());
columnEntity.setAttrName(attrName);
columnEntity.setAttrname(StringUtils.uncapitalize(attrName));
//列的数据类型,转换成Java类型
String attrType = config.getString(columnEntity.getDataType(), "unknowType");
columnEntity.setAttrType(attrType);
//是否主键
if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null)
tableEntity.setPk(columnEntity);
columsList.add(columnEntity);
tableEntity.setColumns(columsList);
//没主键,则第一个字段为主键
if (tableEntity.getPk() == null)
tableEntity.setPk(tableEntity.getColumns().get(0));
//设置velocity资源加载器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封装模板数据
Map<String, Object> map = new HashMap<>();
map.put("tableName", tableEntity.getTableName());
map.put("comments", tableEntity.getComments());
map.put("pk", tableEntity.getPk());
map.put("className", tableEntity.getClassName());
map.put("classname", tableEntity.getClassname());
map.put("pathName", tableEntity.getClassname().toLowerCase());
map.put("columns", tableEntity.getColumns());
map.put("package", path);
map.put("author", author);
map.put("email", config.getString("email"));
map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
map.put("moduleName", mainModule);
map.put("secondModuleName", toLowerCaseFirstOne(className));
VelocityContext context = new VelocityContext(map);
//获取模板列表
List<String> templates = getTemplates();
for (String template : templates)
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
try
//添加到zip
zip.putNextEntry(new ZipEntry(getFileName(template, tableEntity.getClassName(), path, mainModule)));
IOUtils.write(sw.toString(), zip, "UTF-8");
IOUtils.closeQuietly(sw);
zip.closeEntry();
catch (IOException e)
throw new RuntimeException("渲染模板失败,表名:" + tableEntity.getTableName(), e);
接下来我们看到下面3个方法
这三个方法在上面的方法中有被调用,第一个方法的作用是将列名转换为Java属性名。第二个方法的作用是将表名转换成Java属性名。在第二个方法中,调用了第一个方法。第三个方法的作用是获取配置信息。
第一个方法中,使用了replace方法,将表名中的_替换为空字符串。关于capitalizeFully方法,我们通过查看它源码的注释可以发现,该方法的作用是:将 String 中所有分隔符分隔的单词转换为大写单词,即每个单词由一个标题字符和一系列小写字符组成。分隔符代表一组被理解为分隔单词的字符。第一个字符串字符和分隔符后的第一个非分隔符字符将大写。
/**
* 列名转换成Java属性名
*/
public static String columnToJava(String columnName)
return WordUtils.capitalizeFully(columnName, new char[]'_').replace("_", "");
/**
* 表名转换成Java类名
*/
public static String tableToJava(String tableName, String tablePrefix)
if (StringUtils.isNotBlank(tablePrefix))
tableName = tableName.replace(tablePrefix, "");
return columnToJava(tableName);
/**
* 获取配置信息
*/
public static Configuration getConfig()
try
return new PropertiesConfiguration("generator.properties");
catch (ConfigurationException e)
throw new RuntimeException("获取配置文件失败,", e);
接下来这个方法的作用是获取文件名
这里的template,对应的就是构造方法中对应的template链表的一个对象,这个方法,在上面的generatorCode方法中被调用,目的是获取文件名。
在本方法中,源码首先使用字符串拼接的方式,用File.separator拼接出对应的包名,接着又拼接出包的前缀。之后通过isNotBlank判断包名是否为空(或者只有空格),接着通过if语句,判断该字符串的具体内容,根据对应的情况返回不同的文件名
/**
* 获取文件名
*/
public static String getFileName(String template, String className, String packageName, String moduleName)
String packagePath = "main" + File.separator + "java" + File.separator;
String frontPath = "ui" + File.separator;
if (StringUtils.isNotBlank(packageName))
packagePath += packageName.replace(".", File.separator) + File.separator;
if (template.contains("index.js.vm"))
return frontPath + "api" + File.separator + moduleName + File.separator + toLowerCaseFirstOne(className) + File.separator + "index.js";
if (template.contains("index.vue.vm"))
return frontPath + "views" + File.separator + moduleName + File.separator + toLowerCaseFirstOne(className) + File.separator + "index.vue";
if (template.contains("biz.java.vm"))
return packagePath + "biz" + File.separator + className + "Biz.java";
if (template.contains("mapper.java.vm"))
return packagePath + "mapper" + File.separator + className + "Mapper.java";
if (template.contains("entity.java.vm"))
return packagePath + "entity" + File.separator + className + ".java";
if (template.contains("controller.java.vm"))
return packagePath + "rest" + File.separator + className + "Controller.java";
if (template.contains("mapper.xml.vm"))
return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + className + "Mapper.xml";
return null;
本类中的最后一个方法
本类中的最后一个方法是首字母转小写的方法,本方法调用了Java.lang.String类中的toLowerCase方法。如果已经为小写,则不需要再转小写。同时利用StringBuilder类的append方法拼接字符串。这比直接使用String类的效率要高一些
//首字母转小写
public static String toLowerCaseFirstOne(String s)
if (Character.isLowerCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
关于.vm文件
本类涉及对vm文件的读取,在本项目中,我个人认为,vm文件相当于规定了一些java代码的基本结构,比如下面的controller.java.vm,里面固定了Controller类(接口类)的基本形态
import $package.biz.$classNameBiz;
import $package.entity.$className;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@RequestMapping("$secondModuleName")
public class $classNameController extends BaseController<$classNameBiz,$className>
还有下面关于mybatis中mapper文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="$package.mapper.$classNameMapper">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="$package.entity.$className" id="$classnameMap">
#foreach($column in $columns)
<result property="$column.attrname" column="$column.columnName"/>
#end
</resultMap>
</mapper>
比较有意思的是关于实体类配置的.vm文件,在这个文件中,首先我们可以看到,在引入包时,通过#if判断是否含有BigDecimal,如果有,就引入java.math.BigDecimal类,在之后还对注释进行了相应的配置。在实体类的配置中,使用了对应的for-each
以上是关于软件工程应用与实践(11)——工具类分析的主要内容,如果未能解决你的问题,请参考以下文章