性能文章>借助SpotBugs将程序错误扼杀在摇篮中>

借助SpotBugs将程序错误扼杀在摇篮中原创

1月前
255413

背景

最近一年多在一家toB业务的公司,部门主要做的是建筑行业的招投标业务信息化,希望借助软件来达到“阳光、降本、提效”的目的,我刚入职时大概30多家客户,截止现在已经超过100家,发展势头迅猛,随之而来也暴露出来一个很严重的问题:“质量差,线上反馈多。”

大伙每天疲于处理线上反馈,需求响应缓慢,交付速度跟不上市场节奏,长此下去对团队士气是一种打击也会激化研发和业务的矛盾,鉴于此我们对线上反馈进行了逐个分析、打标,最终的结论是:“大部分反馈是由于客户一些个性化需求导致逻辑冲突,太多的开关充斥在代码中有时会顾此失彼,还有一部分完全是编码层面的低级问题,空指针、跨类型的equals等”,对于需求问题由产品经理和研发经理把控,不接稀里糊涂的需求,对于低级别的编码问题由我牵头出具一些优化方案。

 

研发流程现状

“开发-》测试-》上线-》线上环境复测”,很简洁的一个流程吧,在当初业务量小、团队小的时候确实没什么问题,响应也快,但是现在人员规模30+的团队还是用这种简单的流程,已然是不合适了,必须增加一些手段来保证开发质量,让一些低级错误扼杀在摇篮中,首当其要的是改善现有的研发流程,增加一些必要的自检和code review,初步改善以后为:“拉取私有分支-》开发-》自测-》提交 Pull Request合流-》组内同事code review-》测试-》上线-》线上环境复测”,在初期确实是收到了一些收益,一些低级别的问题通过“人肉”确实能发现不少,但是到了大家都赶进度的时候就完全靠不住,原因你懂得

 

上工具
引用我之前公司CTO的一句话:“靠人终归是靠不住的,最好靠遵循规则的机器

人都是有惰性的,靠管理制度来提高研发质量这种精神值得鼓励但是不提倡,找到顺手的工具才是我们的解决之道,将工具和管理制度结合起来,接下来就是我们本篇的重点SpotBugs,一个静态代码扫描工具。

 

静态代码扫描概念

静态源代码扫描是近年被人提及较多的软件应用安全解决方案之一。它是指在软件工程中,程序员在写好源代码后,无需经过编译器编译,而直接使用一些扫描工具对其进行扫描,找出代码当中存在的一些语义缺陷、安全漏洞的解决方案。静态扫描技术已经从90年代时候的,编码规则匹配这种由编译技术拓展过来的分析技术向程序模拟全路径执行的方向发展,由此,这种模拟执行相对的执行路径比动态执行更多,能够发现很多动态测试难以发现的缺陷。

静态代码扫描优点

这个方案的优点在于,无需进行编译、也无需去搭建运行环境,就可以对程序员所写的源代码进行扫描。可以节省大量的人力和时间成本,提高开发效率,并且能够发现很多靠人力无法发现的安全漏洞,站在黑客的角度上去审查程序员的代码,大大降低项目中的安全风险,提高软件质量。

 

静态代码扫描缺点

传统的静态分析,传统的静态分析都是基于语法解析或者编译器,这些方式分析代码的缺陷是以代码所匹配的规则模式(patterns)去评估代码,只要模式匹配或者相似就报出来。需要人工去分辨出其中的真假,主要存在的问题:

– False positive(误报)

– False negative(漏报)

以上内容来源于百度百科

 

案例

案例一 空指针

这真的是一个很低级的问题了,不见得是能力问题,稍不留神很容易出,||写成&&,少加个!之类的。

List<Map<String, Object>> resultMap = getBasedao().queryList(sql, paramMap);
if (resultMap == null && resultMap.isEmpty()) {
}

 

修复方法1:

if (resultMap == null || resultMap.isEmpty())

修复方法2:

if (CollectionUtils.isEmpty(resultMap ))

推荐使用工具类的判断方法,将重复性的事情交给工具类来干,减少出错的可能性,而且代码的描述性更强,isEmpty,isNotEmpty等等。

案例二 返回值被忽略

String querySql = "select * from biz_table where id=xxx";
Map<String, Object> query = getBasedao().query(querySql);
if (query == null) { 
    querySql.replace("biz_table ","biz_table _history");   
    query = getBasedao().query(querySql);
}

修复方法:

接收返回值,querySql = querySql.replace

案例三 equals两个不相关的对象

这个绝对是能力问题了,“钢筋”和“混凝土”怎么能equals呢,Class都不一样。

 

List<Map<String, Object>> maps = getBasedao().queryList(sql, map);
if(CollectionUtils.isEmpty(maps)) { 
   return null;
}

if(maps.get(0).equals("")) { 
   return null;
}

 

案例四  keySet方式遍历Map比entrySet方式低效

public static String getXmlDataV3(Map<String,Object> params) {
StringBuffer buf = new StringBuffer();
buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
buf.append("<root>");buf.append("<request>");
for (String key:params.keySet()) { 
         buf.append("<"+key+">"+params.get(key)+"</"+key+">");
}

buf.append("</request>");
buf.append("</root>");

return buf.toString();}

修复方法

直接使用entrySet遍历,减少不必要的读取

for(Map.Entry<String,Object> me : params.entrySet()){ 
     buf.append("<"+me.getKey()+">"+me.getValue()+"</"+me.getKey()+">");
}

 

这些问题泄漏出去不知道会引起多少反馈呢?

 

案例回顾

通过这七个案例可以初步体验静态代码扫描的强大之处,在发布之前帮助发现一些简单但是出错频率很高的问题,将错误扼杀在摇篮中,错误带到生产环境的代价是巨大的,本来是一个2分钟解决的空指针问题,最后可能会发酵成客户的投诉函,整个链条上的同事都要一起填坑。

 

SpotBugs安装

直接在idea插件市场安装,安装完以后需要重启。

 

SpotBugs介绍

https://spotbugs.readthedocs.io/en/latest/introduction.html

SpotBugs使用

1.扫描整个项目

    1.1 选中某个模块,比如demo-web

   1.2 右键SpotBugs→Analyze Module Files Not including Test Sources(这里只是举例,具体使用哪种扫描策略根据自己的实际情况),如下图所示:

    1.3 等待

    1.4 查看扫描结果

 

    1.5 查看明细

重点关注Correctness(正确性),逻辑错误,空指针之类的。

2.扫描某个类

对于新写的代码,强烈建议大家扫描,可以发现不少问题

    2.1 选中具体的类

    2.2 右键SpotBugs→Analyze Selected File

接下来的步骤和之前没有区别,就不啰嗦了。

3.按等级查看bug

SpotBugs将bug按不同维度进行了划分,比如“正确性、性能、坏味道”这是bug类型的维度,还有一个维度是bug等级,SpotBugs将bug分成了四个等级,分别是:

  1. scariest  最可怕

  2. scary   可怕

  3. troubling  令人不安

  4. of concern 令人担忧

点击扫描结果左下角的Group by Bug Rank,就会按等级呈现,真的是一个很酷的功能,工具已经帮我们分组,而且以颜**分,看到的scariestscary的你就立马点进去修改一波吧

 

SpotBugs和Pull Request结合

前面我们对最初的研发流程做了一些优化,增加了自测、Pull Request、Code Review等步骤,短期内确实收到了一些收益,但是时间一长人的惰性再加上时间紧的时候这些流程就开始应付了,鉴于此我们引入工具帮我们发现一些低级别的问题,嵌入到Create Pull Request流程中,合并代码必须提供SpotBugs的扫描结果图,不允许包含scariestscary级别的问题,这样我们就把工具和管理手段结合起来保障研发质量。

 

推荐阅读

FindBugs - Wikipedia

https://github.com/spotbugs/spotbugs

https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html

 

写在最后

工具用的好,下班没烦恼。

 

 

 

 

💥看到这里的你,如果对于我写的内容很感兴趣,有任何疑问,欢迎在下面留言📥,会第一次时间给大家解答,谢谢! 

分类:标签:
请先登录,查看1条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

Kafka的生产者优秀架构设计
前言 Kafka 是一个高吞吐量的分布式的发布订阅消息系统,在全世界都很流行,在大数据项目里面使用尤其频繁。笔者看过多个大数据开源产品的源码,感觉 Kafka 的源码是其中质量比较上乘的一个,这得益于
这么流行的ZooKeeper,原来是这样设计的!
为什么会有ZooKeeper 我们知道要写一个分布式应用是非常困难的,主要原因就是局部故障。一个消息通过网络在两个节点之间传递时,网络如果发生故障,发送方并不知道接收方是否接收到了这个消息。有可能是收
微服务架构何去何从?
前言微服务架构模式经过5年多的发展,在各行各业如火如荼地应用和实践。如何在企业中优雅地设计微服务架构?是企业面对的一个重要问题。本文将讲述微服务架构1.0设计与实践以及面临问题和破局,最后讲述微服务架
数学,离一个程序员有多近?
作者:小傅哥博客:[https://bugstack.cn](https://bugstack.cn) 沉淀、分享、成长,让自己和他人都能有所收获!😄 一、前言`数学离程序员有多近?`ifelse也
笔记整理:技术架构涵盖内容和演变过程总结
前言架构,说的是开发用的框架吗?对于刚接触编程的新人来说,可能并不能很清楚的知道架构是怎么来的,都包括什么内容。如果非得说什么架构,那么可能就是目前在 IDEA 中打开的工程就是架构。抛开技术圈内的架
90%的程序员,都没用过多线程和锁,怎么成为架构师?
作者:小傅哥博客:[https://bugstack.cn](https://bugstack.cn)Github:[https://github.com/fuzhengwei/CodeGuide/w
RocketMQ 在使用上的一些排坑和优化
前言:RocketMQ 在我们的项目中使用非常广泛,在使用的过程中,也遇到了很多的问题。比如没有多环境的隔离,在多个版本同时开发送测的情况下,互相干扰严重。RocketMQ 的投递可能会失败,导致丢失
年纪轻轻,为什么要搞中间件开发?“路怎么走,让你们自己挑”
作者:小傅哥博客:[https://bugstack.cn](https://bugstack.cn)沉淀、分享、成长,让自己和他人都能有所收获!😄 一、前言`年纪轻轻,为什么要搞中间件开发?`五年