2021SC@SDUSC
- 一、概述
- 二、主要代码
- 2.1 DateUtils
- 2.2 GeneratorUtils
- 2.3 documentUtil
- 三、总结
在老年健康管理系统中,有很多工具类,这些工具类在项目中扮演了关键的角色,很多功能都利用这些工具类实现,经过小组成员的讨论和交流,本篇博客主要介绍老年健康管理系统中的工具类。理解这些工具类,有利于我们理解整个系统的一些实现。
本项目的工具类主要有以下几个
generater项目的utils包下的两个工具类
tools项目下的search.lucene.util包下的几个工具类
gate项目下utils包下的工具类
common项目下的几个工具类
在本篇博客中,主要针对其中的几个类进行分析,在下一篇博客中,还会对剩下的几个类进行剖析
二、主要代码 2.1 DateUtilsDateUtil类的作用比较简单,主要提供了对日期进行格式化的方法
可以看到,在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 {
public final static String DATE_PATTERN = "yyyy-MM-dd";
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 ListgetTemplates() { List templates = new ArrayList (); 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(Maptable, List
接下来我们看到下面3个方法
这三个方法在上面的方法中有被调用,第一个方法的作用是将列名转换为Java属性名。第二个方法的作用是将表名转换成Java属性名。在第二个方法中,调用了第一个方法。第三个方法的作用是获取配置信息。
第一个方法中,使用了replace方法,将表名中的_替换为空字符串。关于capitalizeFully方法,我们通过查看它源码的注释可以发现,该方法的作用是:将 String 中所有分隔符分隔的单词转换为大写单词,即每个单词由一个标题字符和一系列小写字符组成。分隔符代表一组被理解为分隔单词的字符。第一个字符串字符和分隔符后的第一个非分隔符字符将大写。
public static String columnToJava(String columnName) {
return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
}
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.${className}Biz;
import ${package}.entity.${className};
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
@RequestMapping("${secondModuleName}")
public class ${className}Controller extends baseController<${className}Biz,${className}> {
}
还有下面关于mybatis中mapper文件的配置
#foreach($column in $columns) #end
比较有意思的是关于实体类配置的.vm文件,在这个文件中,首先我们可以看到,在引入包时,通过#if判断是否含有BigDecimal,如果有,就引入java.math.BigDecimal类,在之后还对注释进行了相应的配置。在实体类的配置中,使用了对应的for-each循环,设置属性
package ${package}.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
#if(${hasBigDecimal})
import java.math.BigDecimal;
#end
@Table(name = "${tableName}")
public class ${className} implements Serializable {
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if($column.columnName == $pk.columnName)
//$column.comments
@Id
private $column.attrType $column.attrname;
#else
//$column.comments
@Column(name = "$column.columnName")
private $column.attrType $column.attrname;
#end
#end
#foreach ($column in $columns)
public void set${column.attrName}($column.attrType $column.attrname) {
this.$column.attrname = $column.attrname;
}
public $column.attrType get${column.attrName}() {
return $column.attrname;
}
#end
}
2.3 documentUtil
接下来我们把目光放在lucene包下的util工具类。关于lucene,在一开始接触的时候,其实我并不知道lucene是什么东西,但是后面通过查询资料,我渐渐了解了lucene是做什么的。lucene是一个搜索引擎工具,可以用于全文搜索和检索。
而Lucene、Solr、Elasticsearch三者的关系是:lucene提供底层的API和工具包,Solr是基于Lucene开发的企业级的搜索引擎产品,Elasticsearch同样也是基于Lucene开发的企业级的搜索引擎产品。
这个类实际上是对lucene中的document(每一条记录),进行相应的操作
首先我们看到该类包的引入
可以发现,该类引入了很多org.apache.lucene包下的类,有分析器,与搜索高亮相关的类等等
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.document.document; import org.apache.lucene.document.Field; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.TextField; import org.apache.lucene.search.highlight.Highlighter; import java.io.StringReader;
接下来我们查看这个类的IndexObject2document方法
经过阅读,可以发现本类并没有构造方法,而所有的方法都是静态方法,供程序员在其他程序中调用。比如下面的这个方法,核心的操作就是通过传入的IndexObject对象,创建并返回一个document对象。IndexObject是一个索引对象,里面实现了诸多方法,这个我们之后会分析
public static document IndexObject2document(IndexObject indexObject) {
document doc = new document();
doc.add(new StoredField("id", indexObject.getId()));
doc.add(new TextField("title",indexObject.getTitle(), Field.Store.YES));
doc.add(new TextField("summary",indexObject.getKeywords(), Field.Store.YES));
doc.add(new TextField("descripton",indexObject.getDescripton(), Field.Store.YES));
doc.add(new StoredField("postDate", indexObject.getPostDate()));
doc.add(new StoredField("url", indexObject.getUrl()));
return doc;
}
接下来我们查看document2IndexObject方法
这个方法的目的是将传入的document对象变为IndexObject(索引)对象,作用与上面的方法类似,之后我会重点讲解这两个类
public static IndexObject document2IndexObject(Analyzer analyzer, Highlighter highlighter, document doc,float score) throws Exception {
IndexObject indexObject = new IndexObject();
indexObject.setId(Long.parseLong(doc.get("id")));
indexObject.setTitle(stringFormatHighlighterOut(analyzer, highlighter,doc,"title"));
indexObject.setKeywords(stringFormatHighlighterOut(analyzer, highlighter,doc,"summary"));
indexObject.setDescripton(stringFormatHighlighterOut(analyzer, highlighter,doc,"descripton"));
indexObject.setPostDate(doc.get("postDate"));
indexObject.setUrl(doc.get("url"));
indexObject.setScore(score);
return indexObject;
}
接下来我们查看下一个方法
这个方法的主要作用是为关键字加高亮。
这个方法传入了一个document对象,还有一个field字符串,使用highlighter的getBestFragment将document对象中的有关field的字符串提供高亮效果。
private static String stringFormatHighlighterOut(Analyzer analyzer, Highlighter highlighter, document document, String field) throws Exception{
String fieldValue = document.get(field);
if(fieldValue!=null){
TokenStream tokenStream=analyzer.tokenStream(field, new StringReader(fieldValue));
return highlighter.getBestFragment(tokenStream, fieldValue);
}
return null;
}
关于IndexObject类
由于本项目中使用的搜索引擎中存在“索引”这一概念,因此本项目中使用了IndexObject这个类用于代表对应的索引。首先可以看到这个类中引入了java.io.Serializable接口,并且实现了Comparable接口,在下面的方法中,我们可以看到,这个类重写了compareTo方法,在compareTo方法中,主要对该对象的score(相似度)属性进行比较,返回1代表本对象比传入的对象小,返回0代表相等,返回-1代表本对象比传入的对象大。
本类中也为其他的属性提供了相应的get和set方法。
import java.io.Serializable; public class IndexObject implements Comparable三、总结,Serializable{ private Long id; private String title; private String keywords; private String descripton; private String postDate; public String getPostDate() { return postDate; } public void setPostDate(String postDate) { this.postDate = postDate; } public String getDescripton() { return descripton; } public void setDescripton(String descripton) { this.descripton = descripton; } private String url; private float score; public String getKeywords() { return keywords; } public void setKeywords(String keywords) { this.keywords = keywords; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public float getScore() { return score; } public void setScore(float score) { this.score = score; } public IndexObject() { super(); } public IndexObject(Long _id, String _keywords, String _descripton, String _postDate, float _score) { super(); this.id = _id; this.keywords = _keywords; this.score = _score; this.descripton=_descripton; this.postDate=_postDate; } @Override public int compareTo(IndexObject o) { if(this.score < o.getScore()){ return 1; }else if(this.score > o.getScore()){ return -1; } return 0; } }
在本篇博客中,主要对本项目中的一些工具类进行了源码分析。总的来说,还是有不少收获的。在未来的项目实训中,我希望能将这种思想渗入到我的代码中,在这个过程中,感谢老师,小组成员对我的帮助,经过我们小组的讨论,才最终成功地完成了本次源码的阅读。希望在接下来的学习中还能更近一步。



