代码规范及多年编程心得体会,常用工具类说明及分层套路指南,规范代码改bug 赏心悦目,不规范脑阔疼改bug
总结:一直苦寻代码规范,规范的代码读起来优雅,bug 改起来快,自己读起来也很舒服。优雅的代码易于维护,可塑性强。
具体很多可以参考 阿里巴巴规范,我这里整理的就是个人心得。
1. 分层领域模型规约
- DO(Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
- BO(Business Object):业务对象。由Service层输出的封装业务逻辑的对象。
- AO(Application Object):应用对象。在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
- VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
- Query:数据查询对象,各层接收上层的查询请求。注意超过5个参数的查询封装,禁止使用Map类来传输
DAO 层
入参,使用 DO(Data Object)。
出参,使用 DO(Data Object)。
Service 层
入参,使用 DTO(Data Transfer Object)。需要加上 Bean Validation 注解,从而校验参数。需要加上 Swagger API 注解,因为后续 Controller 很大可能性会使用到它,
出参,使用 BO(Business Object)。本来考虑使用 DTO ,考虑到区分,所以使用了 BO 。 当然,返回是使用 DTO 的,问题不大。需要加上 Swagger API 注解,原因同 DTO 。
Controller 层
入参,使用 DTO(Data Transfer Object)。(建议入参超过5个必须强制封装成dto 对象)Query 对象 也行 但是考虑不要搞那么死 dto 足够 不需要刻意区分太清楚
出参,统一结果 ResponseResult 可参考我的代码
异常统一全局处理 封装成 统一对象返回 可参考 我的代码 ExceptionsHandler 全局处理 ,service 一般错误逻辑 直接抛出 自定义ServiceException 异常,因为有全局处理 Controller 一般有异常就要捕获了 虽然有全局异常处理。
2. 下面 讲一下 常用的工具类用法吧
2.1:对象对象copy 主要对比了一下 spring 和 commons BeanUtils 的用法 功能上差不多
/**
* 从一个entity中把属性复制进另外一个entity中
* spring BeanUtils 不会抛异常 使用上都差不多 (他们copy 原目标和目标对象位置不一样)
* 从结果 看 都是全复制 类似于深度 clone
* @throws Exception
*/
@Test
public void testCopyNewBean() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
User source = new User();
source.setId(0);
source.setName("11");
source.setAge(0);
source.setPthone("1329794655");
source.setEmail("98400620@qq.com");
source.setDate(new Date());
//完全一样
User target = new User();
org.springframework.beans.BeanUtils.copyProperties(source, target);
System.out.println(target.toString());
//名字一样类型不一样 不报错 类型不一样 不会复制(必须类型名字都一样才会 复制)
User2 user2 = new User2();
org.springframework.beans.BeanUtils.copyProperties(source, user2);
System.out.println(user2.toString());
//===================================== common-bean 下面
//Object o = BeanUtils.cloneBean(source);
//System.out.println(o);
User user1 = new User();
//完全一样
BeanUtils.copyProperties(user1,source);
System.out.println(user1);
//不完全一样
User2 user21 = new User2();
BeanUtils.copyProperties(user21,source);
System.out.println(user21);
//=============================复杂对象
System.out.println("==================================复杂对象");
Dept dept = new Dept();
dept.setId(0);
dept.setDeptName("aaaaa");
dept.setUsers(Arrays.asList(new User(1,"2",3,"ass","emael",new Date()),new User()));
dept.setCreateDate("2019-02-01");
dept.setUpdateTime(new Date());
dept.setIds(new Integer[]{1,2,5});
Dept dept2 = new Dept();
Dept2 dept21 = new Dept2();
System.out.println(dept);
// org.springframework.beans.BeanUtils.copyProperties(dept,dept2);
BeanUtils.copyProperties(dept2,dept);
// org.springframework.beans.BeanUtils.copyProperties(dept,dept21);
BeanUtils.copyProperties(dept21,dept);
System.out.println(dept2);
System.out.println(dept21);
}
2.2 base64 编解码 的简单使用 主要介绍了 自带Base64 和common 包下 的用法
/**
* base64 编解码
* 这里主要 说明一下Url 安全的base 64 可以用于正常传参 数据不会丢失 普通base64 可能回参数丢失 因为特殊字符 入 + 传过去变空格
* https://www.cnblogs.com/smalldark/p/6496675.html
* Url 安全的base 64 其实就是 对特殊字符 做了替换 然后还原又 替换回来
* @throws Exception
*/
@Test
public void testBase64Code() {
String name1 = "wwww.baidu.com";
System.out.println("Before: " + name1);
String name2 = Base64.encodeBase64String(name1.getBytes());
System.out.println("After encode: " + name2);
String name3 = new String(Base64.decodeBase64(name2));
System.out.println("After decode: " + name3);
String url1 = "wwww.baidu.com";
System.out.println("URL Before: " + url1);
//url 安全
Base64 base64 = new Base64(true);//都可以和下面的一种
String url2 = Base64.encodeBase64URLSafeString(url1.getBytes());
System.out.println("URL After decode: " + url2);
String url3 = new String(Base64.decodeBase64(url2));
System.out.println("URL After decode: " + url3);
//============================ 其实java 也提供了
///url 安全
java.util.Base64.getUrlEncoder().encode("wwww".getBytes());
//普通
java.util.Base64.getEncoder().encode("wwww".getBytes());
}
2.3 commons 下 collection 工具包 介绍了一下 特殊的数据结构
/**
* commons 下 collection 工具包
*
* @throws Exception
*/
@Test
public void testCollection() throws Exception {
// ==================== 提供前后 遍历的工具类
OrderedMap<String, Object> om = new LinkedMap<String, Object>();
om.put("one", 1);
om.put("two", "2");
om.put("three", "three");
om.put("fore", 4);
om.put("five", "5");
System.out.println(om.firstKey());
System.out.println(om.nextKey("fore"));
System.out.println(om.previousKey("five"));
System.out.println("==============================");
//====================双向 取值 根据key 取value 根据value 取key 还提供了 key 和 value 互换
BidiMap bm = new TreeBidiMap();
bm.put("three", "3");
bm.put("five", "isfive");
System.out.println(bm.getKey("isfive").toString());
System.out.println(bm.get("three"));
// 交换key和value
BidiMap newMap = bm.inverseBidiMap();
System.out.println(newMap);
System.out.println("==============================");
//============== 这种数据结构 提供 一次加 几个数据到 包里(有点意思)
// 具体方法可以参考 https://blog.csdn.net/jianggujin/article/details/51069087
Bag<Object> bag = new HashBag<Object>();
bag.add("abc");
bag.add("abc");
bag.add("def", 3);
bag.add("ghi", 5);
System.out.println(bag);
System.out.println(bag.size());
// 过滤重复元素
Set<Object> onlyU = bag.uniqueSet();
Iterator<Object> i = onlyU.iterator();
while(i.hasNext()){
Object o = i.next();
System.out.println(o.toString());
}
}
2.4 spring lang3 guava 里面的一种实现 还有各种骚操作 https://www.jianshu.com/p/97778b21bd00 使用Google Guava快乐编程
/**
* 字符串 常用工具类 主要抛转引玉
* 随便讲了了一下 spring lang3 guava 里面的一种实现 还有各种骚操作
*/
@Test
public void testString() {
//=========== spring 下面 各种好东西 StringUtils==============集合字符串互转
List<String> coll = Arrays.asList("1","2","3");
String result = org.springframework.util.StringUtils.collectionToDelimitedString(coll, "-");
System.out.println(result);
List<String> list = Arrays.asList(result.split("-"));
System.out.println(list);
//=======================lang3 下面
String result1 = org.apache.commons.lang3.StringUtils.join(coll,";");
System.out.println(result1);
String a1[] = {"1", "2", "3"};
String a2[] = {"a", "b", "c"};
// 合并数组
String a3[] = (String[]) ArrayUtils.addAll(a1, a2);
for (String s : a3) {
System.out.println(s);
}
System.out.println("==============================");
String str = "hello, my name is hanmeimei! what's your name? name";
// 出现第一个和第二个name之间的string
String s1 = StringUtils.substringBetween(str, "name");
System.out.println("s1: " + s1);
// 截取第一次出现的字符串之间的string
String s2 = StringUtils.substringBetween(str, "name", "your");
System.out.println("s2: " + s2);
// StringUtils.substringAfter(str, separator)
// StringUtils.substringBefore(str, separator)
System.out.println("==============================");
// 判断该字符串是不是为数字(0~9)组成,如果是,返回true 但该方法不识别有小数点
System.out.println(StringUtils.isNumeric("454534"));
System.out.println("==============================");
System.out.println(ClassUtils.getShortClassName(Test.class));
System.out.println(ClassUtils.getPackageName(Test.class));
System.out.println("==============================");
// 判断该字符串是不是为数字(0~9)组成,如果是,返回true 可以识别有小数点
System.out.println(NumberUtils.isNumber("12334.11"));
// 不建议使用,可以使用 Integer.valueOf("[number]")
System.out.println(NumberUtils.toInt("33"));
System.out.println(Integer.valueOf("33"));
// 五位的随机字母和数字
System.out.println(RandomStringUtils.randomAlphanumeric(5));
System.out.println(StringEscapeUtils.escapeHtml("<html>"));
System.out.println(StringEscapeUtils.escapeJava("String"));
// StringUtils,判断是否是空格字符
System.out.println(StringUtils.isBlank(" "));
// StringUtils.isEmpty("");
// 将数组中的内容以,分隔
System.out.println(StringUtils.join(a3, ","));
// 在右边加下字符,使之总长度为6
System.out.println(StringUtils.rightPad("abc", 6, 'T'));
// 首字母大写
System.out.println(StringUtils.capitalize("abc"));
// Deletes all whitespaces from a String 删除所有空格
System.out.println(StringUtils.deleteWhitespace(" ab c "));
// 判断是否包含这个字符
System.out.println(StringUtils.contains("abc", "ba"));
// 表示左边两个字符
System.out.println(StringUtils.left("abc", 2));
//=======================guava
String result2 = Joiner.on(".").join(coll);
System.out.println(result2);
List<String> list3 = Splitter.on('.').limit(2).splitToList(result2);
System.out.println(list3);
}
2.5 读取配置文件 简单方式
/**
* Apache Commons Configuration
* 读取配置文件 配置件 随便举例 读者可以自己写 写法也很多 load() 载入取值等
* 还有spring 注入取值 等操作各种各样 骚操作 我这里 不一一举例了
* @throws Exception
*/
@Test
public void testConfig() throws ConfigurationException {
PropertiesConfiguration p = new PropertiesConfiguration("application.properties");
System.out.println(p.getString("spring.jackson.time-zone"));
System.out.println(p.getInt("server.port"));
p.setHeader("##this is a new string##");
p.setProperty("new.string", "newString");
// 保存在编译后的目录中
p.save();
p.save("new.properties");
}
3.0 对象 拷贝 除了 beanUtils.copy 方式(源码都是通过反射 效率不是很高) 这里介绍一种(MapStruct) 效率更高 用起来也蛮爽的 方式 解决 各种对象互相 转换 的难题 从此 搜easy 再也不用为对象转换烦恼了
原理其实和 lombok 差不多 编译的时候 给你生成了类 定义一个接口 打上注解 和 mybatis 注解 不是同一个
@Mapper
public interface Student2StudentBOMapper {
Student2StudentBOMapper MAPPER = Mappers.getMapper(Student2StudentBOMapper.class);
@Mappings({})
StudentBO get(Student student);
//,expression = "java(java.lang.String.valueOf(student.getSex()))"
@Mappings({
@Mapping(source = "id",target = "id")
})
Student1BO get1(Student student);
@Mappings({})
Student1BO get3(Student student);
@Mappings({
@Mapping(source = "id",target = "id"),
@Mapping(source = "date",target = "date",dateFormat = "yyyy-MM-dd"),
//表达式expression 不要设置 sources 不然报错
@Mapping(expression = "java(com.example.demo.utils.SexUtils.getSex(student.getSex()))",target = "sex")
})
Student2BO get2(Student student);
/**
* 会直接参照 对象的模式 写 @Mapping 会报错 想想也是 为啥要重复设置关联关系呢?
* @param student
* @return
*/
@Mappings({})
List<Student2BO> get4(List<Student> student);
}
@Test
public void testOne(){
Student student = new Student();
student.setId(0);
student.setName("aaaaaaaaaaaaaaaa");
student.setSex(0);
student.setDate(new Date());
System.out.println(student);
StudentBO studentBo = Student2StudentBOMapper.MAPPER.get(student);
System.out.println(studentBo);
System.out.println("================================");
// id Long 可以 自动转型(没有给你自动 转型 需要自己 写maping)
Student1BO student3Bo = Student2StudentBOMapper.MAPPER.get3(student);
System.out.println(student3Bo);
System.out.println("================================");
Student1BO student1Bo = Student2StudentBOMapper.MAPPER.get1(student);
//多参数 没有为null 类型不一样 需要 写maping
Student2BO student2Bo = Student2StudentBOMapper.MAPPER.get2(student);
System.out.println(student2Bo);
System.out.println("================================");
List<Student> students = new ArrayList<>();
Student student1 = new Student();
student1.setId(0);
student1.setName("bbbbbbbbbb");
student1.setSex(1);
student1.setDate(new Date());
students.add(student);
students.add(student1);
List<Student2BO> student2BO = Student2StudentBOMapper.MAPPER.get4(students);
System.out.println(student2BO);
}
其他的
一. org.apache.commons.io.IOUtils
-
closeQuietly:关闭一个IO流、socket、或者selector且不抛出异常,通常放在finally块
-
toString:转换IO流、 Uri、 byte[]为String
-
copy:IO流数据复制,从输入流写到输出流中,最大支持2GB
-
toByteArray:从输入流、URI获取byte[]
-
write:把字节. 字符等写入输出流
-
toInputStream:把字符转换为输入流
-
readLines:从输入流中读取多行数据,返回List<String>
-
copyLarge:同copy,支持2GB以上数据的复制
-
lineIterator:从输入流返回一个迭代器,根据参数要求读取的数据量,全部读取,如果数据不够,则失败
二. org.apache.commons.io.FileUtils
-
deleteDirectory:删除文件夹
-
readFileToString:以字符形式读取文件内容
-
deleteQueitly:删除文件或文件夹且不会抛出异常
-
copyFile:复制文件
-
writeStringToFile:把字符写到目标文件,如果文件不存在,则创建
-
forceMkdir:强制创建文件夹,如果该文件夹父级目录不存在,则创建父级
-
write:把字符写到指定文件中
-
listFiles:列举某个目录下的文件(根据过滤器)
-
copyDirectory:复制文件夹
-
forceDelete:强制删除文件
三. org.apache.commons.lang.StringUtils
-
isBlank:字符串是否为空 (trim后判断)
-
isEmpty:字符串是否为空 (不trim并判断)
-
equals:字符串是否相等
-
join:合并数组为单一字符串,可传分隔符
-
split:分割字符串
-
EMPTY:返回空字符串
-
trimToNull:trim后为空字符串则转换为null
-
replace:替换字符串
四. org.apache.http.util.EntityUtils
-
toString:把Entity转换为字符串
-
consume:确保Entity中的内容全部被消费。可以看到源码里又一次消费了Entity的内容,假如用户没有消费,那调用Entity时候将会把它消费掉
-
toByteArray:把Entity转换为字节流
-
consumeQuietly:和consume一样,但不抛异常
-
getContentCharset:获取内容的编码
五. org.apache.commons.lang3.StringUtils
-
isBlank:字符串是否为空 (trim后判断)
-
isEmpty:字符串是否为空 (不trim并判断)
-
equals:字符串是否相等
-
join:合并数组为单一字符串,可传分隔符
-
split:分割字符串
-
EMPTY:返回空字符串
-
replace:替换字符串
-
capitalize:首字符大写
六. org.apache.commons.io.FilenameUtils
-
getExtension:返回文件后缀名
-
getBaseName:返回文件名,不包含后缀名
-
getName:返回文件全名
-
concat:按命令行风格组合文件路径(详见方法注释)
-
removeExtension:删除后缀名
-
normalize:使路径正常化
-
wildcardMatch:匹配通配符
-
seperatorToUnix:路径分隔符改成unix系统格式的,即/
-
getFullPath:获取文件路径,不包括文件名
-
isExtension:检查文件后缀名是不是传入参数(List<String>)中的一个
七. org.springframework.util.StringUtils
-
hasText:检查字符串中是否包含文本
-
hasLength:检测字符串是否长度大于0
-
isEmpty:检测字符串是否为空(若传入为对象,则判断对象是否为null)
-
commaDelimitedStringToArray:逗号分隔的String转换为数组
-
collectionToDelimitedString:把集合转为CSV格式字符串
-
replace 替换字符串
-
delimitedListToStringArray:相当于split
-
uncapitalize:首字母小写
-
collectionToDelimitedCommaString:把集合转为CSV格式字符串
-
tokenizeToStringArray:和split基本一样,但能自动去掉空白的单词
八. org.apache.commons.lang.ArrayUtils
-
contains:是否包含某字符串
-
addAll:添加整个数组
-
clone:克隆一个数组
-
isEmpty:是否空数组
-
add:向数组添加元素
-
subarray:截取数组
-
indexOf:查找某个元素的下标
-
isEquals:比较数组是否相等
-
toObject:基础类型数据数组转换为对应的Object数组
九. org.apache.commons.lang.StringEscapeUtils
-
参考十五:
org.apache.commons.lang3.StringEscapeUtils
十. org.apache.http.client.utils.URLEncodedUtils
-
format:格式化参数,返回一个HTTP POST或者HTTP PUT可用application/x-www-form-urlencoded字符串
-
parse:把String或者URI等转换为List<NameValuePair>
十一. org.apache.commons.codec.digest.DigestUtils
-
md5Hex:MD5加密,返回32位字符串
-
sha1Hex:SHA-1加密
-
sha256Hex:SHA-256加密
-
sha512Hex:SHA-512加密
-
md5:MD5加密,返回16位字符串
十二. org.apache.commons.collections.CollectionUtils
-
isEmpty:是否为空
-
select:根据条件筛选集合元素
-
transform:根据指定方法处理集合元素,类似List的map()
-
filter:过滤元素,雷瑟List的filter()
-
find:基本和select一样
-
collect:和transform 差不多一样,但是返回新数组
-
forAllDo:调用每个元素的指定方法
-
isEqualCollection:判断两个集合是否一致
十三. org.apache.commons.lang3.ArrayUtils
-
contains:是否包含某个字符串
-
addAll:添加整个数组
-
clone:克隆一个数组
-
isEmpty:是否空数组
-
add:向数组添加元素
-
subarray:截取数组
-
indexOf:查找某个元素的下标
-
isEquals:比较数组是否相等
-
toObject:基础类型数据数组转换为对应的Object数组
十四. org.apache.commons.beanutils.PropertyUtils
-
getProperty:获取对象属性值
-
setProperty:设置对象属性值
-
getPropertyDiscriptor:获取属性描述器
-
isReadable:检查属性是否可访问
-
copyProperties:复制属性值,从一个对象到另一个对象
-
getPropertyDiscriptors:获取所有属性描述器
-
isWriteable:检查属性是否可写
-
getPropertyType:获取对象属性类型
十五. org.apache.commons.lang3.StringEscapeUtils
-
unescapeHtml4:转义html
-
escapeHtml4:反转义html
-
escapeXml:转义xml
-
unescapeXml:反转义xml
-
escapeJava:转义unicode编码
-
escapeEcmaScript:转义EcmaScript字符
-
unescapeJava:反转义unicode编码
-
escapeJson:转义json字符
-
escapeXml10:转义Xml10
这个现在已经废弃了,建议使用commons-text包里面的方法。
十六. org.apache.commons.beanutils.BeanUtils
-
copyPeoperties:复制属性值,从一个对象到另一个对象
-
getProperty:获取对象属性值
-
setProperty:设置对象属性值
-
populate:根据Map给属性复制
-
copyPeoperty:复制单个值,从一个对象到另一个对象
-
cloneBean:克隆bean实例
现在你只要了解了以上16种最流行的工具类方法,你就不必要再自己写工具类了,不必重复造轮子。大部分工具类方法通过其名字就能明白其用途,如果不清楚的,可以看下别人是怎么用的,或者去网上查询其用法。
另外,工具类,根据阿里开发手册,包名如果要使用util不能带s,工具类命名为 XxxUtils