任何抛开场景对设计进行的评价都是愚蠢的。一个方案只要能实现目的都叫解决方案,解决方案没有对错,但是有好坏,我这里会总结一些我们在开发过程中所做的一些不好的设计、编码,包括管理方面愚蠢的举动。我会对入围的bad case进行场景的剖析,分析当时我们为什么会这么想,当然有些是我在采访当事人之后他们给出的说法。
try catch系列
for 循环内 try catch
这里本意是 forEach 针对每一个元素进行一个操作,该操作有可能会抛出异常,为了在for循环中不阻塞后面元素的执行,因此这里进行了try catch。一旦对元素的处理成功就加到一个成功的集合中并在最后进行返回。
但是,这里忽略了一点,就是失败的数据也被认为是成功的给加入到成功的list中进去了。
修改方式之一
mysql 系列
java中自定义的sql有必要全部大写么
有同事写代码的时候,特意把sql语句全部大写了。说是Mysql会默认转大写,如果全部写了大写就不用转了,提高性能。
网上的文章有的说可以提升性能,但是我问了一下Chat,回答如下,自己评判吧
CompletableFuture 系列
exceptionally 判断 e 是否为空
// exceptionally 中捕获判断 e 是否为空
public CompletableFuture<Boolean> addSealOneAsync(Object o, SealTypeEnum sealTypeEnum, String otherTemplateId, String batchCode) {
String tenantId = TenantContext.getTenantId();
return CompletableFuture.supplyAsync(() -> {
TenantContext.setTenantId(tenantId);
return addSealOne(o, sealTypeEnum, otherTemplateId, batchCode);
}, threadPoolComponent.getSealGroupThreadPool()).exceptionally(e -> {
if (e != null) {
// 记录日志
log.error("电子印章盖章异步任务异常。证明材料:{},印章类型:{},其他模板ID:{},批次号:{}", o, sealTypeEnum, otherTemplateId, batchCode, e);
}
return false;
});
}
首先,CompletableFuture 的 .exceptionally 注册的函数其本意是 当.supplyAsync发生异常的时候需要执行的方法,并且这个方法需要明确一下在异常时候的返回值。也就是说这个.exceptionally本意就是再遇到异常的时候给程序一个默认值,而不要再抛出异常。所以 e 一定不为空,这里判空是如此一举的。如下,直接记录日志并返回默认值即可。
public CompletableFuture<Boolean> addSealOneAsync(Object o, SealTypeEnum sealTypeEnum, String otherTemplateId, String batchCode) {
String tenantId = TenantContext.getTenantId();
return CompletableFuture.supplyAsync(() -> {
TenantContext.setTenantId(tenantId);
return addSealOne(o, sealTypeEnum, otherTemplateId, batchCode);
}, threadPoolComponent.getSealGroupThreadPool()).exceptionally(e -> {
// 记录日志
log.error("电子印章盖章异步任务异常。证明材料:{},印章类型:{},其他模板ID:{},批次号:{}", o, sealTypeEnum, otherTemplateId, batchCode, e);
return false;
});
}
exceptionally 捕获并抛出一个RuntimeException异常
//exceptionally 捕获并抛出一个RuntimeException异常
private CompletableFuture<Map<String, byte[]>> asyncDownloadOssFile(@NotNull OssPO oss, @NotBlank String fileName, @NotBlank String dir) {
return CompletableFuture.supplyAsync(() -> {
Map<String, byte[]> resultMap = new HashMap<>();
resultMap.put(fileName, ossRpc.downloadRaw(oss, dir + fileName));
return resultMap;
}, threadPoolComponent.getArchiveThreadPool()).exceptionally(e -> {
if (e != null) {
throw new RuntimeException("异步下载文件失败!错误信息:" + e.getMessage(), e);
}
return null;
});
}
首先,CompletableFuture 的 .exceptionally 注册的函数其本意是 当.supplyAsync发生异常的时候需要执行的方法,并且这个方法需要明确一下在异常时候的返回值。也就是说这个.exceptionally本意就是再遇到异常的时候给程序一个默认值,而不要再抛出异常。所以 e 一定不为空,这里判空是如此一举的。 那你说我不判空了,我按照下面这么写。
private CompletableFuture<Map<String, byte[]>> asyncDownloadOssFile(@NotNull OssPO oss, @NotBlank String fileName, @NotBlank String dir) {
return CompletableFuture.supplyAsync(() -> {
Map<String, byte[]> resultMap = new HashMap<>();
resultMap.put(fileName, ossRpc.downloadRaw(oss, dir + fileName));
return resultMap;
}, threadPoolComponent.getArchiveThreadPool()).exceptionally(e -> {
throw new RuntimeException("异步下载文件失败!错误信息:" + e.getMessage(), e);
return null;
});
}
这种写法连编译都通不过。因此你说那我再遇到异常的之后直接抛出一个RuntimeException,不写返回值了,这样总不会错了吧。当然编译不会出错,但是本来人家就抛出一个RuntimeException异常,你捕获了之后又抛出了一个RuntimeException异常,这不多此一举了么
private CompletableFuture<Map<String, byte[]>> asyncDownloadOssFile(@NotNull OssPO oss, @NotBlank String fileName, @NotBlank String dir) {
return CompletableFuture.supplyAsync(() -> {
Map<String, byte[]> resultMap = new HashMap<>();
resultMap.put(fileName, ossRpc.downloadRaw(oss, dir + fileName));
return resultMap;
}, threadPoolComponent.getArchiveThreadPool()).exceptionally(e -> {
throw new RuntimeException("异步下载文件失败!错误信息:" + e.getMessage(), e);
});
}
这种写法,跟线面这种不写 exceptionally的写法本质上没有什么区别。所以结论就是,exceptionally 捕获并抛出一个RuntimeException异常 这种做法没有意义,可以不写。
private CompletableFuture<Map<String, byte[]>> asyncDownloadOssFile(@NotNull OssPO oss, @NotBlank String fileName, @NotBlank String dir) {
return CompletableFuture.supplyAsync(() -> {
Map<String, byte[]> resultMap = new HashMap<>();
resultMap.put(fileName, ossRpc.downloadRaw(oss, dir + fileName));
return resultMap;
}, threadPoolComponent.getArchiveThreadPool())
}
如果非要较真,区别就是再捕获异常之后再异常的message中加了一个 "异步下载文件失败!错误信息:"
但这个信息对定位问题并没有给程序定位带来多大的帮助。如果这里不抛出一个RuntimeException而是抛出一个特定的自定义异常,然后再上层针对这个自定义的异常进行一个业务逻辑的处理,我还能接受,否则这里没有必要捕获这个异常。当然还有一个前提是,在外层调用这个一步方法之后,需要有join或者get方法,否则不捕获异常打印日志的行为会导致异常堆栈的丢失。