本文目录导读:

我来给你一个完整的Java静态代码分析案例,包含场景、工具配置和详细分析过程。
项目场景设定
// 待分析的示例项目:电商订单处理系统
package com.example.order;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class OrderService {
// 问题1:硬编码SQL,存在SQL注入风险
public List<Order> getOrdersByUser(String userId) {
List<Order> orders = new ArrayList<>();
String sql = "SELECT * FROM orders WHERE user_id = '" + userId + "'";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Order order = new Order();
order.setId(rs.getLong("id"));
order.setAmount(rs.getBigDecimal("amount"));
orders.add(order);
}
} catch (SQLException e) {
e.printStackTrace(); // 问题2:打印堆栈而不是使用日志框架
}
return orders;
}
// 问题3:资源未正确关闭
public void saveOrder(Order order) {
Connection conn = null;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO orders (id, amount, user_id) VALUES (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setLong(1, order.getId());
ps.setBigDecimal(2, order.getAmount());
ps.setString(3, order.getUserId());
ps.executeUpdate();
} catch (SQLException e) {
System.out.println("Error: " + e.getMessage()); // 使用System.out
} finally {
// 没有关闭PreparedStatement
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.out.println("Close error"); // 忽略异常
}
}
}
}
// 问题4:空指针风险
public String getOrderStatus(Order order) {
if (order.getStatus() != null) {
return order.getStatus().toUpperCase();
}
return order.getStatus(); // 可能返回null
}
// 问题5:魔法数字和硬编码
public boolean isExpired(Order order) {
long currentTime = System.currentTimeMillis();
long expireTime = 24 * 60 * 60 * 1000; // 魔法数字
return (currentTime - order.getCreateTime()) > expireTime;
}
}
class Order {
private Long id;
private BigDecimal amount;
private String userId;
private String status;
private long createTime;
// getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public long getCreateTime() { return createTime; }
public void setCreateTime(long createTime) { this.createTime = createTime; }
}
使用Maven配置静态分析工具
<!-- pom.xml -->
<project>
<build>
<plugins>
<!-- Checkstyle 检查代码规范 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<configLocation>google_checks.xml</configLocation>
<failOnViolation>false</failOnViolation>
</configuration>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<!-- SpotBugs 检测Bug -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.7.3.2</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
</configuration>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<!-- PMD 检查代码缺陷 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.19.0</version>
<configuration>
<rulesets>
<ruleset>rulesets/java/quickstart.xml</ruleset>
</rulesets>
<failOnViolation>false</failOnViolation>
</configuration>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
分析报告示例
1 Checkstyle 检查结果
Checkstyle violations found:
[ERROR] OrderService.java:12:9 - SQL injection vulnerability: concatenation of user input
[ERROR] OrderService.java:15:16 - Line length exceeds 120 characters
[ERROR] OrderService.java:18:13 - Variable 'ps' should be declared final
[WARN] OrderService.java:35:13 - Avoid using printStackTrace()
2 SpotBugs 检查结果
Bug Report:
[High] SQL_INJECTION: SQL statement constructed from user input
- In OrderService.getOrdersByUser(String userId)
- Repair: Use PreparedStatement with parameters
[Medium] NP_NULL_ON_SOME_PATH: Possible null pointer dereference
- In OrderService.getOrderStatus(Order order)
- When order.getStatus() returns null
[Medium] DM_EXIT: Method invokes System.exit()
- In OrderService.saveOrder(Order order)
- Avoid using System.exit() in business logic
3 PMD 检查结果
PMD Violations:
[High] AvoidUsingHardCodedIP - Hardcoded values in database connection
[Medium] AvoidInstantiatingObjectsInLoops - Objects instantiated in loops
[Low] SystemPrintln - Avoid using System.out.println for logging
[High] CloseResource - PreparedStatement not closed properly
修复后的代码
package com.example.order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class OrderServiceFixed {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceFixed.class);
private static final long EXPIRE_TIME_MS = 24 * 60 * 60 * 1000L; // 24小时
/** 使用参数化查询防止SQL注入 */
public List<Order> getOrdersByUser(String userId) {
List<Order> orders = new ArrayList<>();
String sql = "SELECT * FROM orders WHERE user_id = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, userId); // 使用参数绑定
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
Order order = new Order();
order.setId(rs.getLong("id"));
order.setAmount(rs.getBigDecimal("amount"));
orders.add(order);
}
}
} catch (SQLException e) {
LOGGER.error("Failed to get orders for user: {}", userId, e); // 使用日志框架
}
return orders;
}
/** 正确关闭所有资源 */
public void saveOrder(Order order) {
String sql = "INSERT INTO orders (id, amount, user_id) VALUES (?, ?, ?)";
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, order.getId());
ps.setBigDecimal(2, order.getAmount());
ps.setString(3, order.getUserId());
ps.executeUpdate();
} catch (SQLException e) {
LOGGER.error("Failed to save order: {}", order.getId(), e);
}
}
/** 处理空值情况 */
public String getOrderStatus(Order order) {
String status = order.getStatus();
return (status != null) ? status.toUpperCase() : "UNKNOWN"; // 提供默认值
}
/** 使用具名常量代替魔法数字 */
public boolean isExpired(Order order) {
long currentTime = System.currentTimeMillis();
return (currentTime - order.getCreateTime()) > EXPIRE_TIME_MS;
}
}
持续集成推荐配置
# GitLab CI/CD 配置示例
stages:
- analyze
- build
- test
# 静态代码分析阶段
analyze:
stage: analyze
script:
- mvn clean verify checkstyle:checkspotbugs:check pmd:check
artifacts:
reports:
codequality: target/reports/checkstyle-result.xml
通过这个案例,你可以:
- 看到常见的代码问题被识别和修复
- 了解不同工具的检查重点
- 建立标准化的静态分析流程
- 持续改进代码质量