代码Bug太多?给新人Code Review头都大了?快来试试SpotBugs

Posted 水妖3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码Bug太多?给新人Code Review头都大了?快来试试SpotBugs相关的知识,希望对你有一定的参考价值。

如果你需要一个自动化的工具帮助你或者你的团队发现代码中的缺陷,在提升代码质量同时减少人工Code Review的成本,那这篇文章非常的适合你。

如果你需要一个自动化的工具帮助你或者你的团队发现代码中的缺陷,在提升代码质量同时减少人工Code Review的成本,那这篇文章非常的适合你。本文围绕SpotBugs与Gradle集成,将相关配置和使用进行了详细介绍,并提供了各种能够为你的项目定制化配置的手段。来源和出处都已在文中关键处以超链接给出,尽情享受吧。

SpotBugs是什么?

SpotBugs是一个开源的Java静态分析工具,旨在帮助开发人员检测代码中的潜在缺陷和漏洞。SpotBugs可以通过扫描Java字节码来发现潜在的问题,例如空指针引用、类型转换错误、未使用的变量等等。它还可以检测代码中的潜在安全漏洞,例如SQL注入、XSS攻击等。SpotBugs提供了一个用户友好的GUI和命令行接口,可以轻松地与各种构建工具和IDE集成,例如Ant、Maven、Gradle、Eclipse和IntelliJ IDEA。SpotBugs还支持插件和自定义规则,使得开发人员可以根据项目的特定需求和标准对其进行定制化配置。更多详细信息可以查看SpotBugs官方文档

SpotBugs是FindBugs的一个分支,它在FindBugs的基础上进行了改进和升级,它使用了更先进的算法和技术来提高分析的准确性和效率。SpotBugs还添加了对新的Java版本的支持,例如Java 8和Java 11。SpotBugs还提供了更好的用户界面和命令行界面,并支持更多的构建工具和IDE集成。

FindBugs也是一款非常流行的Java静态分析工具,但是已经停止更新。停更的原因似乎是项目拥有者不愿继续在这个项目上花时间,代码贡献者因为没有权限也无法继续迭代和发布新的版本,在FindBugs GitHub首页README文件中提供了两个文档项目状态 2016-November项目状态 2017-September,里面讲述了代码贡献者当时的一些担忧和无奈。

SpotBugs与Gradle集成

开始之前先简单介绍下本文中将会提到的几个依赖以及它们之间的关系,以便于理解后续内容:

  • com.github.spotbugs.snom:spotbugs-gradle-plugin是一个Gradle插件,它将SpotBugs集成到 Gradle构建中,生成SpotBugsTask并可以通过配置属性来扩展。
  • com.github.spotbugs:spotbugs这个依赖基本包含了所有SpotBugs检测器,这些检测器实现了Bug descriptions中提到的相关Bug检测项的逻辑。
  • com.h3xstream.findsecbugs:findsecbugs-plugincom.github.spotbugs:spotbugs的基础上增加了安全相关的检查器也就是Bug descriptions#Security中描述的相关检查项。
  • com.github.spotbugs:spotbugs-annotations是Spotbugs的一个扩展/辅助工具,开发者使用这里面注解可以让Spotbugs按照我们的意图来检查代码。

spotbugs-gradle-plugin

spotbugs-gradle-plugin是一个Gradle插件,它将SpotBugs集成到Gradle构建中,生成SpotBugsTask并提供相关配置来扩展,运行SpotBugsTask就可以执行检查,生成报告等。

默认情况下,这个插件里面已经包含了一个spotbugs,所以应用了这个插件后一般无需在另外添加com.github.spotbugs:spotbugs。从SpotBugs version mapping中可以知道spotbugs-gradle-plugin不同版本中默认包含的spotbugs版本之间的关系,比如:com.github.spotbugs.snom:spotbugs-gradle-plugin:5.0.13包含了com.github.spotbugs:spotbugs:4.7.3

SpotBugs Gradle插件要求Gradle版本为7.0或者更高,JDK版本为1.8或者更高。

这个插件会为每个sourceSets生成SpotBugsTask。例如项目中有两个sourceSets main和test,插件将生成两个SpotBugsTask(spotbugsMain和spotbugsTest)。

如果不想自动生成SpotBugsTask,可以使用SpotBugs Base插件从头开始配置,参考com.github.spotbugs-base

生成的SpotBugsTask执行时需要编译后的.class文件作为输入,因此它们将在Java编译后运行。SpotBugs Gradle增加了check相关的任务依赖,所以简单的运行./gradlew check也会执行生成的SpotBugsTask。

参考com.github.spotbugs在build.gradle文件中增加以下内容来引入SpotBugs Gradle插件:

buildscript 
  repositories 
    maven 
      url "https://plugins.gradle.org/m2/"
    
  
  dependencies 
    classpath "com.github.spotbugs.snom:spotbugs-gradle-plugin:5.0.13"
  


apply plugin: "com.github.spotbugs"

插件里面提供了很多可选的属性来配置或者扩展插件的行为。

下面展示了在build.gradle文件增加spotbugs相关属性的例子,详细信息可以参考SpotBugsExtension,这里面有每个属性作用、可指定的值和其他相关信息。spotbugs中指定的大部分属性将作为生成的SpotBugsTask配置的默认值,意味着可以通过spotbugs给所有SpotBugsTask配置属性。

spotbugs 
    ignoreFailures = false
    showStackTraces = true
    showProgress = false
    reportLevel = \'default\'
    effort = \'default\'
    visitors = [ \'FindSqlInjection\', \'SwitchFallthrough\' ]
    omitVisitors = [ \'FindNonShortCircuit\' ]
    reportsDir = file("$buildDir/reports/spotbugs")
    includeFilter = file(\'spotbugs-include.xml\')
    excludeFilter = file(\'spotbugs-exclude.xml\')
    onlyAnalyze = [\'com.foobar.MyClass\', \'com.foobar.mypkg.*\']
    projectName = name
    release = version
    extraArgs = [ \'-nested:false\' ]
    jvmArgs = [ \'-Duser.language=ja\' ]
    maxHeapSize = \'512m\'

除了上述方式外,还可直接配置SpotBugsTask,以设置某个任务特定的属性,比如下面为名为spotbugsMain的任务单独进行了设置,跟spotbugs同名的属性将被覆盖,详细信息参考SpotBugsTask

spotbugsMain 
    sourceDirs = sourceSets.main.allSource.srcDirs
    classDirs = sourceSets.main.output
    auxClassPaths = sourceSets.main.compileClasspath
  	reports 
        html 
            required = true
            outputLocation = file("$buildDir/reports/spotbugs/main/spotbugs.html")
            stylesheet = \'fancy-hist.xsl\'
        
    

    ignoreFailures = false
    showStackTraces = true
    showProgress = false
    reportLevel = \'default\'
    effort = \'default\'
    visitors = [ \'FindSqlInjection\', \'SwitchFallthrough\' ]
    omitVisitors = [ \'FindNonShortCircuit\' ]
    reportsDir = file("$buildDir/reports/spotbugs")
    includeFilter = file(\'spotbugs-include.xml\')
    excludeFilter = file(\'spotbugs-exclude.xml\')
    baselineFile = file(\'spotbugs-baseline.xml\')
    onlyAnalyze = [\'com.foobar.MyClass\', \'com.foobar.mypkg.*\']
    projectName = name
    release = version
    extraArgs = [ \'-nested:false\' ]
    jvmArgs = [ \'-Duser.language=ja\' ]
    maxHeapSize = \'512m\'

后面将会有一段描述比较常用或者重要的属性。

com.github.spotbugs:spotbugs

因为spotbugs-gradle-plugin中已经包含了spotbugs,所以一般情况下不需要再单独引入这个依赖。不过可能因为默认带的版本有Bug、需要跟IDEA的SpotBugs插件使用相同的版本,又或者新版本的检测器新加了实用的检测项等原因我们会需要单独指定版本,下面提供了两种方式实现。

在配置spotbugs-gradle-plugin的时候通过toolVersion指定spotbugs版本。

spotbugs 
  toolVersion = \'4.7.3\'

dependencies添加依赖并指定依赖版本。

dependencies 
    spotbugs \'com.github.spotbugs:spotbugs:4.7.3\'

findsecbugs-plugin

findsecbugs-plugin是Spotbugs的安全漏洞检测插件。它在spotbugs的基础上增加了自己的规则集,专注于检测安全相关的问题,例如密码泄漏、SQL 注入、XSS 等,也就是Bug descriptions#Security中描述的相关检查项。

dependencies 
    spotbugsPlugins \'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0\'

spotbugs-annotations

spotbugs-annotations是Spotbugs的一个扩展/辅助工具,里面提供了很多注解,我们可以在编码时将它们加在被检测代码的相应位置(如属性、方法、形参、本地变量),让Spotbugs按照我们的意图来检查代码,以便SpotBugs可以更恰当地发出警告。在Annotations中列举了所有注解,需要注意里面有很多已经标记为Deprecated表明已经弃用了。

比如下面这段代码,只是输出value到终端,即使test()传入null也不会导致空指针。Spotbugs本来不会发出警告,但是由于我们在方法上加了注解edu.umd.cs.findbugs.annotations.NonNull,Spotbugs会按照我们的意图进行入参不允许为null的校验,从而发出警告。

import edu.umd.cs.findbugs.annotations.NonNull;

public class SpotbugsAnnotationsSample 

    public static void main(String[] args) 
        test(null);
    
    
    public static void test(@NonNull Integer value) 
        // 输出到终端
        System.out.println(value);
    

下面是引入这个依赖的例子,参考Refer the version in the build script,这里面还提到从spotbugs v4版本开始,spotbugs.toolVersionString 变为 Provider<String>,所以请使用 get() 或其他方式引用实际版本。

dependencies 
    compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs.toolVersion.get()"

SpotBugs Gradle扩展属性

includeFilter和excludeFilter

过滤器(Filter file)文件可以定义匹配检测项、被检查的类和被检查方法的一套匹配规则,让我们可以为项目做定制化。文件配置好后,使用SpotBugsTask提供的属性指定过滤器文件的路径,实现包含(includeFilter)或者排除(excludeFilter)匹配的内容。

文件是xml格式的,每个匹配规则单独写在<Match></Match>中,使用Types of Match clauses描述的各种标签组合成匹配规则的内容,在Examples中给出了很多例子作为参考。

我们重点关注下这个标签,它可以通category、code、pattern来指定Bug descriptions中列出来的检查项,多个参数用逗号分隔,也可以使用正则表达式,以~字符开头。

每个检查项有唯一的pattern,比如下图中的XSS_REQUEST_PARAMETER_TO_SEND_ERRORXSS_REQUEST_PARAMETER_TO_SERVLET_WRITER;多个检查项可能属于同一个code,比如下图中的XSS;多个不同的code可能属于同一个category。这三者之间是一个清晰的层级关系,这样就可以按不同的粒度来配置。

下面给出了一个使用<Bug>简单的例子和描述,更多例子请参考Examples

<!--匹配指定的这两个检查项-->
<Match>
  <Bug pattern="XSS_REQUEST_PARAMETER_TO_SEND_ERROR,XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER" />
</Match>

<!--匹配所有code为XSS的检查项-->
<Match>
  <Bug code="XSS" />
</Match>

<!--匹配SECURITY目录下所有检查项-->
<Match>
  <Bug category="SECURITY" />
</Match>

visitors和omitVisitors

这两个属性可以分别指定使用(visitors)和禁用(omitVisitors)检查器,一个检查器会包含一个或者多个Bug descriptions中提到的检查项,所以这两个属性配置的目的和includeFilterexcludeFilter是一样的,让我们可以为项目做定制化,只是从检查器的维度进行配置。

Detectors中将Spotbugs所有的检查器都列出来了,在不配置的情况下,Standard detectors中列出来的默认使用,Disabled detectors中列出来的默认禁用。

以检查器SynchronizationOnSharedBuiltinConstant为例,从下图我们可以看到,检查器名称下面有段简短的描述,再往下将检查器包含的检查项都列出来了,可以看到上文提到的patterncode,点击可以跳转到Filter file文档相应的位置。我们配置visitorsomitVisitors的时候填检查器的名字SynchronizationOnSharedBuiltinConstant即可。

effort

effort是配置代码检测预期的级别,级别由低到高分别为min、less、more、max,级别越高计算成本越高,花费的时间也就越长,在这个文档Effort里面有表格清晰的列出了这几个级别分别包含了哪些检查内容。effort的默认值是default,等同于more

jvmArgs

jvmArgs用于设置JVM参数,因为SpotBugs是Java写的,自然要在JVM上运行。我们在例子里面看到了-Duser.language=ja,这个意思是设置语言为日语,也就是代码分析的结果展示的语音(输出到终端或者报告中)。总共有英语(en 默认),日语(ja),法语(fr)三种,在GitHub中可以看到相关展示文本的配置文件。

maxHeapSize

maxHeapSize是设置JVM最大堆内存大小,在spotbugsextension#maxHeapSizea中说了默认值为空,因此会使用Gradle的默认配置,所以一般不用管。

onlyAnalyze

onlyAnalyze指定哪些代码要被SpotBugs分析,在大型项目里面用这个属性避免没必要的分析,可能会大大减少运行分析所需的时间。可以指定类名,或者包名,指定包名的时候用.*.-的作用一样,意思是分析这个包下以及子包中的文件。

onlyAnalyze = [\'com.foobar.MyClass\', \'com.foobar.mypkg.*\']

reportLevel

SpotBugs按严重程度高到低将Bug分为了三个等级P1、P2和P3,reportLevel属性指明达到哪个等级的Bug才需要展示在报告里面,它可配置值对应有HIGH、MEDIUM、LOWDEFAULT,它们定义在Confidence.groovy中。默认值是DEFAULT等同于MEDIUM,意思是要达到P2级别,那就意味着低级别的P3 Bug将被忽略,

reports

reports配置报告的类型,有htmlxmlsariftext四种,配置在SpotBugsTask中(比如spotbugsMain ),再往里一层可配置的属性不是很多,参考SpotBugsReport。下面是html类型报告的配置示例:

spotbugsMain 
    reports 
        html 
            required = true
            outputLocation = file("$buildDir/reports/spotbugs/main/spotbugs.html")
            stylesheet = \'fancy-hist.xsl\'
        
    

最佳实践

配置

在根目录gradle.properties中配置版本。

spotbugsGradlePluginVersion=5.0.13
findsecbugsPluginVersion=1.12.0

在根目录build.gradle中增加以下配置:

buildscript 
    repositories 
        mavenLocal()
        maven 
            url "https://plugins.gradle.org/m2/"
        
        mavenCentral()
    
    dependencies 
        classpath "com.github.spotbugs.snom:spotbugs-gradle-plugin:$spotbugsGradlePlugin"
    


apply plugin: "com.github.spotbugs"

repositories 
    mavenLocal()
    maven 
        url "https://plugins.gradle.org/m2/"
    
    mavenCentral()


dependencies 
    compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs.toolVersion.get()"
    spotbugsPlugins "com.h3xstream.findsecbugs:findsecbugs-plugin:$findsecbugsPluginVersion"


spotbugs 
    ignoreFailures = false
    showStackTraces = false
    showProgress = false
    excludeFilter = file("$project.rootDir/code-analysis/spotbugs/exclude-filter.xml")
    extraArgs = [ \'-nested:false\' ]


spotbugsMain 
    reports 
        html 
            required = true
            stylesheet = \'fancy-hist.xsl\'
        
    

如果是多模块项目按这种方式配置:

buildscript 
    repositories 
        mavenLocal()
        maven 
            url "https://plugins.gradle.org/m2/"
        
        mavenCentral()
    
    dependencies 
        classpath "com.github.spotbugs.snom:spotbugs-gradle-plugin:$spotbugsGradlePlugin"
    


allprojects 
		apply plugin: "com.github.spotbugs"
		
    repositories 
        mavenLocal()
        maven 
            url "https://plugins.gradle.org/m2/"
        
        mavenCentral()
    

    dependencies 
        compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugs.toolVersion.get()"
        spotbugsPlugins "com.h3xstream.findsecbugs:findsecbugs-plugin:$findsecbugsPluginVersion"
    

    spotbugs 
        ignoreFailures = false
        showStackTraces = false
        showProgress = false
        excludeFilter = file("$project.rootDir/code-analysis/spotbugs/exclude-filter.xml")
        extraArgs = [\'-nested:false\']
    

    spotbugsMain 
        reports 
            html 
                required = true
                stylesheet = \'fancy-hist.xsl\'
            
        
    

在项目根目录下创建排除检查项的文件/code-analysis/spotbugs/exclude-filter.xml,后期再根据需要配置。

<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter
        xmlns="https://github.com/spotbugs/filter/3.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">

</FindBugsFilter>

使用

在Gradle窗口中双击运行spotbugsMain任务即可,检测完成会在Run窗口中打印出报告地址。

也可以在Terminal窗口中执行./gradlew spotbugsMain,可能需要先执行chmod +x gradlew给gradlew文件执行权限。

如果执行过程中发生类似下面这种异常可以不用管,只是有部分相关的分析会无法执行,并不会中断整个过程,其他不相关的部分都会正常检查完成。在GitHub上issues#527很多人反映了这个问题,但是暂时没有完美的解决。

The following classes needed for analysis were missing:
apply
test
accept

解读报告

报告概要(Summary)按包和BUG等级统计了数量。

按目录浏览(Browse by Categories)将发现的缺陷代码按检查项中的category、code、pattern分层展示。

按包名浏览(Browse by Packages)就是换了个角度,按照包名 > 类名 > 检查项pattern分层展示。

最后一个窗口Info展示分析的代码文件(Analyzed Files)、分析的源文件夹(Source Files)使用的依赖(Used Libraries)、使用到的SpotBugs插件(Plugins)和分析中产生的异常(Analysis Errors)。

过滤配置

通过includeFilterexcludeFilter指定过滤器(Filter file)文件是最灵活的,基本什么维度都可以控制。但是建议这里面只配置要包含或者排除哪些检查项,使用category、code、pattern配置不同的粒度。

从整体缩小被检查的代码范围使用onlyAnalyze,如果有必要的话。如果要忽略不检查具体的类或者方法可以使用注解@SuppressFBWarnings来标记,它来自spotbugs-annotations

其他技巧

SpotBugs Links里面列出了跟SpotBugs集成或者相似的工具,有需要可以了解下。

参考

[SpotBugs]官网

[SpotBugs]官方文档

[SpotBugs]SpotBugsTask

[SpotBugs]SpotBugsExtension

[Gradle]spotbugs

[GitHub]SpotBugs

[GitHub]FindBugs

[GitHub]spotbugs-gradle-plugin

[FindBugs]官网

Code review应该怎么做

  1. Code review 不应该承担发现代码错误的职责。Code Review主要是审核代码的质量,如可读性,可维护性,以及程序的逻辑和对需求和设计的实现。代码中的bug和错误应该由单元测试,功能测试,性能测 试,回归测试来保证的(其中主要是单元测试,因为那是最接近Bug,也是Bug没有扩散的地方)
  2. Code review 不应该成为保证代码风格和编码标准的手段。编码风格和代码规范都属于死的东西,每个程序员在把自己的代码提交团队Review的时候,代码就应该是符合规范的,这是默认值,属于每个人自己的事情,不应该交由团队来完成,否则只会浪费大家本来就不够的时间。

1. 体系结构和代码设计

  • 代码复用: 根据“三振法”, 如果代码被复制一次,虽然不喜欢这种方式,但通常没什么问题。但如果再一次被复制,就应该通过提取公共的部分来重构它。

  • 用更好的代码: 如果在一块混乱的代码做修改,添加几行代码也许更容易,但建议更进一步,用比原来更好的代码。

  • 潜在的bugs: 是否会引起的其他错误?循环是否以我们期望的方式终止?

  • 错误处理: 错误确定被优雅的修改?会导致其他错误?如果这样,修改是否有用?

  • 效率: 如果代码中包含算法,这种算法是否是高效? 例如,在字典中使用迭代,遍历一个期望的值,这是一种低效的方式。

  • 新代码与全局的架构是否保持一致?

  • 基础代码是否有结合使用了一些标准或设计样式,新的代码是否遵循当前的规范?代码是否正确迁移,或参照了因不规范而淘汰的旧代码?

  • 代码的位置是否正确?比如涉及订单的新代码是否在订单服务相关的位置?

  • 新代码是否重用了现存的代码?新代码是否可以被现有代码重用?新代码是否有重复代码?如果是的话,是否应该重构成一个更可被重用的模式,还是当前还可以接受?

  • 新代码是否被过度设计了?是否引入现在还不需要的重用设计?

2. 可读性和可维护性

  • 字段、变量、参数、方法、类的命名是否真实反映它们所代表的事物, 是否能够望文生义?

  • 是否可以通过读代码理解它做了什么?

  • 是否理解测试用例测了什么?

  • 测试是否很好地覆盖了用例的各种情况?它们是否覆盖了正常和异常用例?是否有忽略的情况?

  • 错误信息是否可被理解? log打的是否正确和足够?

  • 不清晰的代码是否被文档、注释或容易理解的测试用例所覆盖?具体可以根据团队自身的喜好决定使用哪种方式。

3. 功能

  • 代码是否真的达到了预期的目标?如果有自动化测试来确保代码的正确性,测试的代码是否真的可以验证代码达到了协定的需求?

  • 代码看上去是否包含不明显的bug,比如使用错误的变量进行检查,或误把and写成or?

  • 作者是否需要创建公共文档或修改现存的帮助文档?

  • 是否检查了面向用户的信息的正确性?

  • 是否有会在生产环境中导致应用停止运行的明显错误?代码是否会错误地指向测试数据库,是否存在应在真实服务中移除的硬编码的stub代码?

  • 你对性能的需求是什么,你是否考虑了安全问题?

    1. 这个新增或修复的功能是否会反向影响到现存的性能测试结果
    2. 外部调用很昂贵(a. 数据库调用. b. 不必要的远程调用. c. 移动或穿戴设备过频繁地调用后端程序)

4. 安全

  • 检查是否新的路径和服务需要认证
  • 数据是否需要加密
  • 密码是否被很好地控制?

    这里的密码包含密码(如用户密码、数据库密码或其他系统的密码)、秘钥、令牌等等。这些永远不应该存放在会提交到源码控制系统的代码或配置文件 中,有其他方式管理这些密码,例如通过密码服务器(secret server)。当审查代码时,要确保这些密码不会悄悄进入你的版本控制系统中

  • 代码的运行是否应该被日志记录或监控?是否正确地使用?

    日志和监控需求因各个项目而不同,一些需要合规,一些拥有比别人严格的行为、事件日志规范。如果你有规章规定哪些需要记录日志,何时、如何记录,那么作为代码审查者你应该检查提交的代码是否满足要求。如果你没有固定的规章,那么就考虑:

    • 代码是否改变了数据(如增删改操作)?是否应该记录由谁何时改变了什么?
    • 代码是否涉及关键性能的部分?是否应该在性能监控系统中记录开始时间和结束时间?
    • 每条日志的日志等级是否恰当?一个好的经验法则是“ERROR”触发一个提示发送到某处,如果你不想这些消息在凌晨3点叫醒谁,那么就将之降 级为“INFO”或“DEBUG”。当在循环中或一条数据可能产生多条输出的情况下,一般不需要将它们记录到生产日志文件中,它们更应该被放在 “DEBUG”级别。

5. 其他方面

  • 是否合理地释放了资源

    1. 是否存在内存泄漏?
    2. 是否存在内存无限增长? 例如, 如果审查者看到不断有变量被追加到list或map中, 那么就要考虑下这个list或map什么时候失效, 或清除无用数据
    3. 代码是否及时关闭了连接或数据流?
    4. 资源池配置是否是否正确? 有没有过大或者过小?
  • 异常情况是否能够正确处理?

    1. 超时是否能够正确处理?
    2. 调用接口出错的时候, 是否有出错处理逻辑, 并且处理正确?
    3. 进程意外重启后, 是否能够恢复到崩溃前的环境?
  • 正确性(主要与多线程环境关系密切)

    1. 代码是否使用了正确的适合多线程的数据结构
    2. 代码是否存在竞态条件(race conditions)?多线程环境中代码非常容易造成不明显的竞态条件。作为审查者,可以查看不是原子操作的get和set
    3. 代码是否正确使用锁?和竞态条件相关,作为审查者你应该检查被审代码是否允许多个线程修改变量导致程序崩溃。代码可能需要同步、锁、原子变量来对代码块进行控制
    4. 代码的性能测试是否有价值?很容易将小型的性能测试代码写得很糟糕,或者使用不能代表生产环境数据的测试数据,这样只会得到错误的结果
    5. 缓存:虽然缓存是一种能防止过多高消耗请求的方式,但其本身也存在一些挑战。如果审查的代码使用了缓存,你应该关注一些常见的问题,如,不正确的缓存失效方式
  • 代码级优化, 对大部分并不是要构建低延时应用的机构来说,代码级优化往往是过早优化,所以首先要知道代码级优化是否必要

    1. 代码是否在不需要的地方使用同步或锁操作?如果代码始终运行在单线程中,锁往往是不必要的
    2. 代码是否可以使用原子变量替代锁或同步操作?
    3. 代码是否使用了不必要的线程安全的数据结构?比如是否可以使用ArrayList替代Vector?
    4. 代码是否在通用的操作中使用了低性能的数据结构?如在经常需要查找某个特定元素的地方使用链表
    5. 代码是否可以使用懒加载并从中获得性能提升?
    6. 条件判断语句或其他逻辑是否可以将最高效的求值语句放在前面来使其他语句短路?
    7. 代码是否存在许多字符串格式化?是否有方法可以使之更高效?
    8. 日志语句是否使用了字符串格式化?是否先使用条件判断语句校验了日志等级,或使用延迟求值?

Code Review 的实际操作建议

  1. 代码审查是应该在互相沟通中进行讨论的,而不是相互对抗。预先确定哪些是要点哪些不是,可以减少冲突并拟定预期。

  2. 经常进行Code Review, 不要攒了1w行才让同事帮你review, 这是坑队友.

    • 要Review的代码越多,那么要重构,重写的代码就会越多。而越不被程序作者接受的建议也会越多,唾沫口水战也会越多。
    • 程序员代码写得时候越长,程序员就会在代码中加入越来越多的个人的东西。 程序员最大的问题就是“自负”,无论什么时候,什么情况下,有太多的机会会让这种“自负”澎涨开来,并开始影响团队影响整个项目,以至于听不见别人的建 议,从而让Code Review变成了口水战。
    • 越接近软件发布的最终期限,代码也就不能改得太多。
  3. Code Review不要太正式,而且要短

  4. 尽可能的让不同的人Reivew你的代码(不要超过3个人)

    • 从不同的方向评审代码总是好的。
    • 会有更多的人帮你在日后维护你的代码。
    • 这也是一个增加团队凝聚力的方法。
  5. 保持积极的正面的态度

    无论是代码作者,还是评审者,都需要一种积极向上的正面的态度,作者需要能够虚心接受别人的建议,因为别人的建议是为了让你做得更好;评审者也需要以一种积极的正面的态度向作者提意见,因为那是和你在一个战壕里的战友。记住,你不是一段代码,你是一个人!

  6. 学会享受Code Reivew

http://blog.csdn.net/wzzfeitian/article/details/54673360

以上是关于代码Bug太多?给新人Code Review头都大了?快来试试SpotBugs的主要内容,如果未能解决你的问题,请参考以下文章

记mac下配置php+nginx环境(头都大了)

vc连接access2003出错,头都大了runtime错误。。。跪求!!在线等

Why review code?(为什么要复盘代码)

Code Review 最佳实践

Code Review 程序员的寄望与哀伤转载

架构师技能5:如何做code review 代码简洁之道