Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое SonarQube
SonarQube — это открытая платформа для статического анализа кода (Code Quality and Security), которая автоматически проверяет исходный код на ошибки, уязвимости, дублирование кода, нарушения стиля и помогает измерить техдолг проекта. Это стандартный инструмент в CI/CD пайплайнах крупных Java проектов.
Что проверяет SonarQube
1. Bugs (Ошибки) — потенциальные баги
// SonarQube обнаружит:
// Ошибка 1: Проверка null после использования
String name = getName();
if (name.length() > 0) { // Может быть NullPointerException
System.out.println(name);
}
// Ошибка 2: Сравнение объектов по ==
String a = new String("hello");
String b = new String("hello");
if (a == b) { // Неправильно! Используй .equals()
System.out.println("Same");
}
// Ошибка 3: Нечитаемый код
public int calculate(int a, int b) {
return a+=b; // Опасный compound assignment в return
}
// Ошибка 4: Пустой catch
try {
doSomething();
} catch (Exception e) {
// Пустой блок — ошибка! Нужно логировать или обрабатывать
}
2. Vulnerabilities (Уязвимости) — проблемы безопасности
// SonarQube обнаружит:
// Уязвимость 1: SQL Injection
String query = "SELECT * FROM users WHERE id = " + userId; // ОПАСНО!
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
// Исправление:
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setInt(1, userId); // Параметризованный запрос
// Уязвимость 2: Hardcoded пароли
public class Database {
private String password = "admin123"; // ОПАСНО!
}
// Уязвимость 3: Использование слабого шифрования
MessageDigest md = MessageDigest.getInstance("MD5"); // Слабое! Используй SHA-256
// Уязвимость 4: Небезопасная randomization
Random random = new Random(); // Не криптографически безопасно!
int token = random.nextInt();
// Исправление:
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[32];
secureRandom.nextBytes(token);
3. Code Smells (Неприятные запахи кода) — плохой дизайн
// Code Smell 1: Слишком большой класс
public class UserService { // 5000 строк кода в одном классе!
public void createUser() { }
public void updateUser() { }
public void deleteUser() { }
public void sendEmail() { } // Email логика в UserService?
public void processPayment() { } // Payment логика здесь?
// ... 100 методов...
}
// Code Smell 2: Слишком длинный метод
public void processOrder(Order order) { // 500 строк!
// Много кода
// Нужно разбить на подметоды
}
// Code Smell 3: Дублирование кода
public String getFullNameUser1() {
return user1.getFirstName() + " " + user1.getLastName();
}
public String getFullNameUser2() {
return user2.getFirstName() + " " + user2.getLastName(); // Дублирование!
}
// Исправление:
public String getFullName(User user) {
return user.getFirstName() + " " + user.getLastName();
}
// Code Smell 4: Использование Magic Numbers
public double calculateDiscount(int loyaltyPoints) {
return loyaltyPoints * 0.01; // Что означает 0.01? Не очевидно
}
// Исправление:
private static final double LOYALTY_POINT_VALUE = 0.01;
public double calculateDiscount(int loyaltyPoints) {
return loyaltyPoints * LOYALTY_POINT_VALUE;
}
4. Code Duplication (Дублирование) — повторяющийся код
// SonarQube обнаружит дублированные блоки кода:
public class DataProcessor {
public List<Integer> processOddNumbers(List<Integer> numbers) {
List<Integer> result = new ArrayList<>();
for (Integer num : numbers) {
if (num % 2 != 0) {
result.add(num);
}
}
return result;
}
public List<Integer> processEvenNumbers(List<Integer> numbers) {
List<Integer> result = new ArrayList<>();
for (Integer num : numbers) {
if (num % 2 == 0) { // Дублирование логики фильтрации
result.add(num);
}
}
return result;
}
}
// Исправление: используй Stream API
List<Integer> oddNumbers = numbers.stream()
.filter(n -> n % 2 != 0)
.collect(Collectors.toList());
Установка и использование SonarQube
Вариант 1: Docker (быстро)
# Запуск SonarQube в Docker
docker run -d --name sonarqube \
-p 9000:9000 \
sonarqube:latest
# После запуска, доступен на http://localhost:9000
# Логин: admin / admin
Вариант 2: Docker Compose
version: '3.8'
services:
sonarqube:
image: sonarqube:latest
ports:
- "9000:9000"
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_DB: sonarqube
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
volumes:
- postgresql_data:/var/lib/postgresql/data
volumes:
postgresql_data:
Анализ проекта Maven
Способ 1: Через Maven плагин
# pom.xml
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
# Команда для анализа
mvn clean verify sonar:sonar \
-Dsonar.projectKey=my-java-project \
-Dsonar.sources=src \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your_token_here
Способ 2: Через SonarScanner (универсально)
# Скачать SonarScanner
# https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/scanners/sonarscanner/
# sonar-project.properties
sonar.projectKey=my-java-project
sonar.projectName=My Java Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.java.binaries=target/classes
# Запуск анализа
sonar-scanner
Способ 3: Через Gradle
// build.gradle
plugins {
id "org.sonarqube" version "4.0.0.2929"
}
sonarqube {
properties {
property "sonar.projectKey", "my-java-project"
property "sonar.projectName", "My Java Project"
property "sonar.sources", "src"
}
}
// Команда
// ./gradlew sonarqube
Интеграция с CI/CD (GitHub Actions)
# .github/workflows/sonarqube.yml
name: SonarQube Analysis
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: '11'
- name: Run SonarQube Analysis
run: |
mvn clean verify sonar:sonar \
-Dsonar.projectKey=my-project \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }}
- name: Check Quality Gate
run: |
# Убедиться, что качество прошло Quality Gate
Quality Gate (Ворота качества)
SonarQube позволяет установить критерии, при которых сборка считается passed/failed:
// Типичные критерии Quality Gate:
// 1. Coverage (покрытие тестами) >= 80%
if (coverage < 80) {
qualityGate = "FAILED";
}
// 2. Bugs <= 10
if (bugs > 10) {
qualityGate = "FAILED";
}
// 3. Vulnerabilities = 0
if (vulnerabilities > 0) {
qualityGate = "FAILED";
}
// 4. Code Smells (A rating = хорошо)
if (maintenanceRating != 'A') {
qualityGate = "FAILED";
}
// 5. Duplication < 3%
if (duplication >= 3.0) {
qualityGate = "FAILED";
}
Примеры отчётов SonarQube
Проект: my-java-project
ОБЩАЯ СТАТИСТИКА:
- Lines of Code: 25,347
- Duplication: 2.3%
- Coverage: 87%
- Bugs: 5 (👎 5 критических)
- Vulnerabilities: 2 (🔴 2 SONAR Security Hotspots)
- Code Smells: 34
RATINGS:
- Reliability: B (есть bugs)
- Security: C (есть уязвимости)
- Maintainability: A (хороший код)
- Security Hotspot Review: 50% (половина проверена)
RECENT ISSUES:
1. CWE-89 (SQL Injection) — Line 345 в UserRepository.java
2. NullPointerException Risk — Line 567 в OrderService.java
3. Duplicate Code Block — 12 lines in UserValidator.java и AdminValidator.java
4. Too Many Parameters — processOrder() method has 8 parameters
5. Magic Number 0.01 — should be replaced with constant
Правила в SonarQube
SonarQube поставляется с сотнями встроенных правил, но можно кастомизировать:
// Примеры встроенных правил
// S1104: Classes should not have public mutable fields
public class BadClass {
public String name; // VIOLATION: должно быть private с getter/setter
}
// S3457: Number and string arguments should be used in when giving messages
try {
doSomething();
} catch (Exception e) {
logger.error("Error occurred", e); // OK
logger.error(e.toString()); // VIOLATION: не подходящее сообщение
}
// S1602: Nested ternary operators should not be used
int level = (age < 13) ? 0 : (age < 18) ? 1 : 2; // VIOLATION: слишком сложно
// S1125: Boolean expressions should not be gratuitously complex
if (value == true) { // VIOLATION: just use (value)
// do something
}
Метрики SonarQube
Maintainability Rating (Оценка поддерживаемости):
A = 0-5% технического долга
B = 5-10% технического долга
C = 10-20% технического долга
D = 20-50% технического долга
E = > 50% технического долга
Reliability Rating (Надёжность):
A = 0 bugs
B = 1-3 bugs на 1000 LOC
C = 4-10 bugs на 1000 LOC
D = 11-25 bugs на 1000 LOC
E = > 25 bugs на 1000 LOC
Security Rating (Безопасность):
A = 0 уязвимостей
B = 1-3 уязвимостей
C = 4-10 уязвимостей
D = 11-25 уязвимостей
E = > 25 уязвимостей
Практический пример в CI/CD
#!/bin/bash
# build.sh — скрипт сборки проекта с анализом качества
set -e
echo "1. Compile and test"
mvn clean test -q
echo "2. Run SonarQube analysis"
mvn sonar:sonar \
-Dsonar.projectKey=backend \
-Dsonar.host.url=http://sonarqube:9000 \
-Dsonar.login=$SONAR_TOKEN
echo "3. Build application"
mvn package -DskipTests -q
echo "4. Build Docker image"
docker build -t my-app:latest .
echo "✅ Build successful!"
Заключение
SonarQube — это essential tool для любого serious Java проекта:
- Находит баги — потенциальные NullPointerException, логические ошибки
- Обнаруживает уязвимости — SQL Injection, hardcoded пароли, слабое шифрование
- Улучшает дизайн — предупреждает о дублировании, big methods, code smells
- Измеряет качество — техдолг, покрытие, дублирование
- Встраивается в CI/CD — автоматическая проверка при каждом push
- Quality Gates — блокирует merge в main если качество упало
Для Java разработчика это значит, что почти любой production проект требует интеграции SonarQube в пайплайн. Это становится стандартом в enterprise разработке.