本文目录导读:

我将为您提供一个完整的Java代码质量门禁案例,包括配置、实现和示例。
项目结构
quality-gate/
├── pom.xml
├── src/
│ ├── main/
│ │ └── java/
│ │ └── com/example/qualitygate/
│ │ ├── QualityGateConfig.java
│ │ ├── QualityGateRunner.java
│ │ └── QualityGateResult.java
│ └── test/
│ └── java/
│ └── com/example/qualitygate/
│ └── QualityGateRunnerTest.java
└── quality-gate-config.yml
核心实现代码
1 质量门禁配置类
package com.example.qualitygate;
import java.util.HashMap;
import java.util.Map;
public class QualityGateConfig {
private Map<String, Object> thresholds = new HashMap<>();
private boolean failOnError = true;
// 质量门禁阈值配置
public static QualityGateConfig defaultConfig() {
QualityGateConfig config = new QualityGateConfig();
config.thresholds.put("complexity.max", 10);
config.thresholds.put("coverage.min", 80.0);
config.thresholds.put("duplication.max", 5.0);
config.thresholds.put("bugs.max", 0);
config.thresholds.put("vulnerabilities.max", 0);
config.thresholds.put("codeSmells.max", 100);
config.thresholds.put("testRatio.min", 0.8);
config.thresholds.put("linesOfCode.max", 2000);
config.thresholds.put("methods.max", 50);
config.thresholds.put("classes.max", 30);
return config;
}
public Object getThreshold(String key) {
return thresholds.get(key);
}
public void setThreshold(String key, Object value) {
thresholds.put(key, value);
}
public boolean isFailOnError() {
return failOnError;
}
public void setFailOnError(boolean failOnError) {
this.failOnError = failOnError;
}
}
2 质量门禁结果类
package com.example.qualitygate;
import java.util.ArrayList;
import java.util.List;
public class QualityGateResult {
private boolean passed = true;
private List<String> violations = new ArrayList<>();
private String gateName;
private long timestamp;
private Map<String, Object> metrics = new HashMap<>();
public void addViolation(String violation) {
violations.add(violation);
passed = false;
}
public boolean isPassed() {
return passed;
}
public List<String> getViolations() {
return new ArrayList<>(violations);
}
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("Quality Gate: ").append(gateName).append("\n");
sb.append("Timestamp: ").append(timestamp).append("\n");
sb.append("Status: ").append(passed ? "PASSED" : "FAILED").append("\n");
if (!violations.isEmpty()) {
sb.append("Violations:\n");
for (String violation : violations) {
sb.append(" - ").append(violation).append("\n");
}
}
return sb.toString();
}
public void addMetric(String name, Object value) {
metrics.put(name, value);
}
}
3 质量门禁执行器
package com.example.qualitygate;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
public class QualityGateRunner {
private QualityGateConfig config;
private List<CodeMetric> metrics = new ArrayList<>();
public QualityGateRunner(QualityGateConfig config) {
this.config = config;
initializeMetrics();
}
private void initializeMetrics() {
metrics.add(new CodeSizeMetric());
metrics.add(new ComplexityMetric());
metrics.add(new CoverageMetric());
metrics.add(new DuplicationMetric());
metrics.add(new SecurityMetric());
metrics.add(new TestQualityMetric());
}
public QualityGateResult execute(String sourceDirectory, String testDirectory) {
QualityGateResult result = new QualityGateResult();
result.gateName = "Java Code Quality Gate";
result.timestamp = System.currentTimeMillis();
Map<String, Object> analysisResults = analyzeCode(sourceDirectory, testDirectory);
// 执行各项检查
checkCodeSize(analysisResults, result);
checkComplexity(analysisResults, result);
checkCoverage(analysisResults, result);
checkDuplication(analysisResults, result);
checkSecurity(analysisResults, result);
checkTestQuality(analysisResults, result);
return result;
}
private Map<String, Object> analyzeCode(String sourceDir, String testDir) {
Map<String, Object> results = new HashMap<>();
List<Path> javaFiles = collectJavaFiles(sourceDir);
List<Path> testFiles = collectJavaFiles(testDir);
// 收集基本指标
results.put("linesOfCode", calculateLines(javaFiles));
results.put("methods", countMethods(javaFiles));
results.put("classes", countClasses(javaFiles));
results.put("complexity", calculateAverageComplexity(javaFiles));
results.put("testRatio", calculateTestRatio(javaFiles, testFiles));
return results;
}
private List<Path> collectJavaFiles(String directory) {
List<Path> javaFiles = new ArrayList<>();
if (directory == null || directory.isEmpty()) {
return javaFiles;
}
Path root = Paths.get(directory);
if (!Files.exists(root)) {
System.err.println("Directory not found: " + directory);
return javaFiles;
}
try {
Files.walk(root)
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(javaFiles::add);
} catch (Exception e) {
System.err.println("Error scanning directory: " + directory);
}
return javaFiles;
}
private int calculateLines(List<Path> files) {
int totalLines = 0;
for (Path file : files) {
try {
long count = Files.lines(file).count();
totalLines += count;
} catch (IOException e) {
System.err.println("Error reading file: " + file);
}
}
return totalLines;
}
private int countMethods(List<Path> files) {
int methodCount = 0;
Pattern methodPattern = Pattern.compile(
"(public|private|protected|static|final|synchronized|abstract)\\s+" +
"(<[^>]+>\\s*)?" +
"[\\w\\[\\]\\<\\>\\?]+\\s+" +
"\\w+\\s*\\("
);
for (Path file : files) {
try {
String content = new String(Files.readAllBytes(file));
Matcher matcher = methodPattern.matcher(content);
while (matcher.find()) {
methodCount++;
}
} catch (IOException e) {
// ignore
}
}
return methodCount;
}
private int countClasses(List<Path> files) {
int classCount = 0;
Pattern classPattern = Pattern.compile(
"(public|abstract|final)?\\s*class\\s+\\w+"
);
for (Path file : files) {
try {
String content = new String(Files.readAllBytes(file));
Matcher matcher = classPattern.matcher(content);
while (matcher.find()) {
classCount++;
}
} catch (IOException e) {
// ignore
}
}
return classCount;
}
private double calculateAverageComplexity(List<Path> files) {
int totalComplexity = 0;
int methodCount = 0;
for (Path file : files) {
try {
String content = new String(Files.readAllBytes(file));
methodCount += countMethods(Collections.singletonList(file));
totalComplexity += calculateCyclomaticComplexity(content);
} catch (IOException e) {
// ignore
}
}
return methodCount > 0 ? (double) totalComplexity / methodCount : 0;
}
private int calculateCyclomaticComplexity(String code) {
int complexity = 1;
String[] keywords = {"if", "else if", "for", "while", "case", "catch", "&&", "||", "?"};
for (String keyword : keywords) {
int count = countOccurrences(code, keyword);
complexity += count;
}
return complexity;
}
private int countOccurrences(String text, String word) {
int count = 0;
int index = 0;
while ((index = text.indexOf(word, index)) != -1) {
count++;
index += word.length();
}
return count;
}
private double calculateTestRatio(List<Path> sourceFiles, List<Path> testFiles) {
if (sourceFiles.isEmpty()) {
return 0;
}
return (double) testFiles.size() / sourceFiles.size();
}
// 各检查方法
private void checkCodeSize(Map<String, Object> results, QualityGateResult result) {
int lines = (int) results.get("linesOfCode");
int maxLines = (int) config.getThreshold("linesOfCode.max");
result.addMetric("linesOfCode", lines);
if (lines > maxLines) {
result.addViolation("Lines of code exceed limit: " + lines + " > " + maxLines);
}
}
private void checkComplexity(Map<String, Object> results, QualityGateResult result) {
double complexity = (double) results.get("complexity");
int maxComplexity = (int) config.getThreshold("complexity.max");
result.addMetric("averageComplexity", complexity);
if (complexity > maxComplexity) {
result.addViolation("Average cyclomatic complexity too high: " +
String.format("%.2f", complexity) + " > " + maxComplexity);
}
}
private void checkCoverage(Map<String, Object> results, QualityGateResult result) {
// 简化版本,实际应集成 JaCoCo 等工具
double coverage = calculateMockCoverage();
double minCoverage = (double) config.getThreshold("coverage.min");
result.addMetric("coverage", coverage);
if (coverage < minCoverage) {
result.addViolation("Test coverage too low: " +
String.format("%.2f%%", coverage) + " < " + minCoverage + "%");
}
}
private void checkDuplication(Map<String, Object> results, QualityGateResult result) {
// 简化版本,实际应集成 PMD 或 SonarQube
double duplication = calculateMockDuplication();
double maxDuplication = (double) config.getThreshold("duplication.max");
result.addMetric("duplication", duplication);
if (duplication > maxDuplication) {
result.addViolation("Code duplication too high: " +
String.format("%.2f%%", duplication) + " > " + maxDuplication + "%");
}
}
private void checkSecurity(Map<String, Object> results, QualityGateResult result) {
// 简化版本,实际应集成 FindBugs 或 SpotBugs
int bugs = 0;
int vulnerabilities = 0;
try {
// 模拟安全检查
Pattern dangerous = Pattern.compile("ProcessBuilder|Runtime\\.exec|System\\.getProperty");
// 实际文件扫描逻辑
} catch (Exception e) {
// ignore
}
result.addMetric("bugs", bugs);
result.addMetric("vulnerabilities", vulnerabilities);
if (bugs > (int) config.getThreshold("bugs.max")) {
result.addViolation("Too many bugs found: " + bugs);
}
if (vulnerabilities > (int) config.getThreshold("vulnerabilities.max")) {
result.addViolation("Too many vulnerabilities found: " + vulnerabilities);
}
}
private void checkTestQuality(Map<String, Object> results, QualityGateResult result) {
double testRatio = (double) results.get("testRatio");
double minTestRatio = (double) config.getThreshold("testRatio.min");
result.addMetric("testRatio", testRatio);
if (testRatio < minTestRatio) {
result.addViolation("Test to source ratio too low: " +
String.format("%.2f", testRatio) + " < " + minTestRatio);
}
}
private double calculateMockCoverage() {
// 模拟覆盖率,实际应集成 JaCoCo
return 85.0; // 示例值
}
private double calculateMockDuplication() {
// 模拟重复率,实际应集成 PMD
return 3.5; // 示例值
}
}
4 CI/CD 集成示例
<!-- pom.xml 中的配置 -->
<project>
<properties>
<!-- 质量门禁配置 -->
<quality.gate.max.coverage>80</quality.gate.max.coverage>
<quality.gate.min.complexity>10</quality.gate.min.complexity>
<quality.gate.max.duplication>5</quality.gate.max.duplication>
</properties>
<build>
<plugins>
<!-- Checkstyle 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity>
</configuration>
</plugin>
<!-- PMD 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<failOnViolation>true</failOnViolation>
<rulesets>
<ruleset>rulesets/java/braces.xml</ruleset>
<ruleset>rulesets/java/naming.xml</ruleset>
</rulesets>
</configuration>
</plugin>
<!-- JaCoCo 覆盖率插件 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<configuration>
<excludes>
<exclude>**/qualitygate/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>${quality.gate.max.coverage}</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<!-- SpotBugs 插件 -->
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.2.0</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<failOnError>true</failOnError>
</configuration>
</plugin>
</plugins>
</build>
</project>
5 门禁执行工具类
package com.example.qualitygate;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class QualityGateExecutor {
public static void main(String[] args) {
if (args.length < 2) {
System.err.println("Usage: QualityGateExecutor <sourceDir> <testDir>");
System.exit(1);
}
String sourceDir = args[0];
String testDir = args[1];
// 1. 加载配置
QualityGateConfig config = QualityGateConfig.defaultConfig();
loadCustomConfig(config);
// 2. 创建执行器
QualityGateRunner runner = new QualityGateRunner(config);
// 3. 执行质量检查
QualityGateResult result = runner.execute(sourceDir, testDir);
// 4. 输出结果
System.out.println(result.getSummary());
// 5. 退出码
if (!result.isPassed() && config.isFailOnError()) {
System.exit(1);
}
}
private static void loadCustomConfig(QualityGateConfig config) {
// 从配置文件或环境变量加载自定义配置
Properties props = new Properties();
try (InputStream in = QualityGateExecutor.class
.getResourceAsStream("/quality-gate.properties")) {
if (in != null) {
props.load(in);
// 解析并覆盖默认配置
props.forEach((key, value) -> {
String metricKey = key.toString().replace("quality.","");
String[] keyParts = metricKey.split("\\.");
if (keyParts.length == 2) {
config.setThreshold(keyParts[0] + "." + keyParts[1],
parseValue(value.toString()));
}
});
}
} catch (IOException e) {
System.err.println("Error loading quality gate configuration: " + e.getMessage());
}
}
private static Object parseValue(String value) {
if (value.contains(".")) {
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return value;
}
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
return Boolean.parseBoolean(value);
}
return value;
}
}
}
6 测试类
package com.example.qualitygate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class QualityGateRunnerTest {
private QualityGateConfig config;
private QualityGateRunner runner;
@BeforeEach
void setUp() {
config = QualityGateConfig.defaultConfig();
runner = new QualityGateRunner(config);
}
@Test
void testPassingQualityGate() {
// 使用高质量代码目录
QualityGateResult result = runner.execute(
"src/test/resources/good-code",
"src/test/resources/good-code-test"
);
assertTrue(result.isPassed(), "Quality gate should pass for good code");
assertTrue(result.getViolations().isEmpty());
}
@Test
void testFailingQualityGate() {
// 使用低质量代码目录
QualityGateResult result = runner.execute(
"src/test/resources/bad-code",
"src/test/resources/bad-code-test"
);
assertFalse(result.isPassed(), "Quality gate should fail for bad code");
assertFalse(result.getViolations().isEmpty());
}
@Test
void testCustomThreshold() {
config.setThreshold("complexity.max", 5); // 更严格的复杂度限制
runner = new QualityGateRunner(config);
QualityGateResult result = runner.execute(
"src/test/resources/complex-code",
"src/test/resources/complex-code-test"
);
assertFalse(result.isPassed());
assertTrue(result.getViolations().stream()
.anyMatch(v -> v.contains("complexity")));
}
@Test
void testEmptySource() {
QualityGateResult result = runner.execute("", "");
assertFalse(result.isPassed());
}
}
配置示例 (quality-gate.properties)
# 质量门禁配置 # 代码规模 quality.linesOfCode.max=2000 quality.classes.max=30 quality.methods.max=50 # 复杂度 quality.complexity.max=10 # 覆盖率 quality.coverage.min=80.0 # 重复率 quality.duplication.max=5.0 # 质量指标 quality.bugs.max=0 quality.vulnerabilities.max=0 quality.codeSmells.max=100 # 测试指标 quality.testRatio.min=0.8 # 其他设置 quality.failOnError=true
CI 集成 (GitLab CI)
# .gitlab-ci.yml
stages:
- build
- test
- quality
- deploy
quality-gate:
stage: quality
script:
- mvn verify
- mvn checkstyle:check
- mvn pmd:check
- mvn spotbugs:check
- java -jar quality-gate.jar ./src ./test
artifacts:
when: always
paths:
- target/surefire-reports/
reports:
junit:
- target/surefire-reports/TEST-*.xml
使用说明
构建和运行
# 编译项目 mvn clean package # 运行质量门禁 java -jar target/quality-gate.jar ./src/main/java ./src/test/java # 执行完整质量检查 mvn verify mvn checkstyle:check mvn pmd:check mvn spotbugs:check
集成到 CI/CD
-
Jenkins Pipeline
pipeline { stages { stage('Quality Gate') { steps { sh 'mvn verify' sh 'mvn checkstyle:check pmd:check spotbugs:check' } } } } -
GitHub Actions
name: Quality Gate on: [push, pull_request] jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up JDK uses: actions/setup-java@v2 with: java-version: '11' - name: Run quality gate run: | mvn verify mvn checkstyle:check mvn pmd:check
最佳实践
- 分级门禁: 根据项目阶段设置不同严格度的门禁
- 渐进式改进: 逐步提高质量标准
- 持续集成: 将门禁集成到CI流程
- 定制规则: 根据团队情况自定义规则
- 自动修复: 集成自动格式化工具
- 定期审查: 定期审查和更新质量规则
这个案例提供了一个完整的Java代码质量门禁实现,可以根据实际需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播