java-quality について
java-qualityは、静的解析ツールでJavaコードの品質を高めるためのリファレンススキルです。ツール設定やよくある問題への対処、MavenとCIでのチェック統合を扱います。
主な機能
- 01Checkstyle、SpotBugs、PMDなどの設定
- 02MavenとGradleの統合例
- 03コードスメルと修正案
- 04複合品質チェックの設定
- 05CIでの品質チェック
活用シーン
- 01Javaプロジェクトへの静的解析の導入
- 02SpotBugsの警告や誤検知の調査
- 03Mavenビルドへの品質チェック追加
- 04複雑度やコードスメルの特定
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill java-qualityインストール後、Claude Code で/java-qualityを入力して読み込まれたか確認します。
詳細
Java 品質 - クイックリファレンス
このスキルを使用しない場合
- SonarQube の一般的なセットアップ -
sonarqubeスキルを使用してください - Spring Boot のテスト - Spring Boot テストスキルを使用してください
- セキュリティスキャン -
java-securityスキルを使用してください - カバレッジレポート -
jacocoスキルを使用してください
詳しい情報:フレームワーク固有のパターンについては、テクノロジーに
spring-bootを指定してmcp__documentation__fetch_docsを使用してください。
ツールの概要
| ツール | 主な用途 | 速度 | 統合先 |
|---|---|---|---|
| Checkstyle | コードスタイル、フォーマット | 高速 | Maven/Gradle |
| SpotBugs | バグパターン、バイトコード | 中速 | Maven/Gradle |
| PMD | コードの不適切な箇所、複雑さ | 高速 | Maven/Gradle |
| SonarJava | オールインワン | 低速 | SonarQube |
| Error Prone | コンパイル時のバグ | 高速 | コンパイラープラグイン |
Checkstyle のセットアップ
Maven の設定
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<violationSeverity>warning</violationSeverity>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.12.5</version>
</dependency>
</dependencies>
</plugin>
checkstyle.xml(Google スタイルベース)
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java"/>
<module name="TreeWalker">
<module name="ConstantName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="TypeName"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<module name="LineLength">
<property name="max" value="120"/>
</module>
<module name="MethodLength">
<property name="max" value="50"/>
</module>
<module name="ParameterNumber">
<property name="max" value="5"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="10"/>
</module>
<module name="NPathComplexity">
<property name="max" value="200"/>
</module>
<module name="EmptyBlock"/>
<module name="EqualsHashCode"/>
<module name="HiddenField"/>
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
</module>
<module name="FileLength">
<property name="max" value="500"/>
</module>
<module name="NewlineAtEndOfFile"/>
</module>
コマンド
# Run Checkstyle
./mvnw checkstyle:check
# Generate report
./mvnw checkstyle:checkstyle
SpotBugs のセットアップ
Maven の設定
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.3.0</version>
<configuration>
<effort>Max</effort>
<threshold>Medium</threshold>
<failOnError>true</failOnError>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
誤検出の除外
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Package name="~.*\.generated\..*"/>
</Match>
<Match>
<Bug pattern="EI_EXPOSE_REP"/>
<Class name="~.*Dto"/>
</Match>
</FindBugsFilter>
コマンド
# Run SpotBugs
./mvnw spotbugs:check
# Generate report
./mvnw spotbugs:spotbugs
# GUI viewer
./mvnw spotbugs:gui
SpotBugs の一般的な警告
| バグ ID | 説明 | 修正方法 |
|---|---|---|
| NP_NULL_ON_SOME_PATH | null 参照の可能性 | null チェックを追加するか、Optional を使用します |
| EI_EXPOSE_REP | 可変オブジェクトを返す | 防御的コピーを返します |
| MS_SHOULD_BE_FINAL | static フィールドは final にする必要があります | final 修飾子を追加します |
| SQL_INJECTION | SQL インジェクションのリスク | パラメーター化クエリを使用します |
| DM_DEFAULT_ENCODING | デフォルトのエンコーディングを使用 | 文字セットを明示的に指定します |
PMD のセットアップ
Maven の設定
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.21.2</version>
<configuration>
<rulesets>
<ruleset>pmd-rules.xml</ruleset>
</rulesets>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
pmd-rules.xml
<?xml version="1.0"?>
<ruleset name="Custom Rules">
<description>Custom PMD ruleset</description>
<rule ref="category/java/bestpractices.xml">
<exclude name="JUnitTestContainsTooManyAsserts"/>
</rule>
<rule ref="category/java/codestyle.xml">
<exclude name="AtLeastOneConstructor"/>
<exclude name="OnlyOneReturn"/>
</rule>
<rule ref="category/java/design.xml">
<exclude name="LawOfDemeter"/>
</rule>
<rule ref="category/java/errorprone.xml"/>
<rule ref="category/java/performance.xml"/>
<rule ref="category/java/design.xml/CyclomaticComplexity">
<properties>
<property name="methodReportLevel" value="10"/>
</properties>
</rule>
<rule ref="category/java/design.xml/CognitiveComplexity">
<properties>
<property name="reportLevel" value="15"/>
</properties>
</rule>
</ruleset>
コマンド
# Run PMD
./mvnw pmd:check
# Generate report
./mvnw pmd:pmd
# Copy-paste detection
./mvnw pmd:cpd
Error Prone のセットアップ
Maven の設定
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
<configuration>
<compilerArgs>
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.24.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
統合品質プロファイル
オールインワンの Maven プロファイル
<profiles>
<profile>
<id>quality</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<executions>
<execution>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
# Run all quality checks
./mvnw verify -Pquality
よくあるコードの不適切な箇所と修正方法
1. ゴッドクラス
// BAD - Class does too much
public class OrderService {
public Order createOrder() { ... }
public void sendEmail() { ... }
public void generatePdf() { ... }
public void calculateTax() { ... }
public void updateInventory() { ... }
}
// GOOD - Single responsibility
public class OrderService {
private final EmailService emailService;
private final PdfGenerator pdfGenerator;
private final TaxCalculator taxCalculator;
private final InventoryService inventoryService;
public Order createOrder(OrderRequest request) {
Order order = buildOrder(request);
order.setTax(taxCalculator.calculate(order));
inventoryService.reserve(order.getItems());
return orderRepository.save(order);
}
}
2. 長すぎるパラメーターリスト
// BAD
public void createUser(String name, String email, String phone,
String address, String city, String country, String zipCode) { }
// GOOD - Use builder or DTO
public void createUser(CreateUserRequest request) { }
@Builder
public record CreateUserRequest(
String name,
String email,
String phone,
Address address
) {}
3. 機能への過度な依存
// BAD - Method uses another object's data excessively
public double calculateTotal(Order order) {
double total = 0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
if (item.getDiscount() > 0) {
total -= item.getPrice() * item.getQuantity() * item.getDiscount();
}
}
return total;
}
// GOOD - Move logic to Order
public class Order {
public double calculateTotal() {
return items.stream()
.mapToDouble(OrderItem::getSubtotal)
.sum();
}
}
public class OrderItem {
public double getSubtotal() {
double base = price * quantity;
return discount > 0 ? base * (1 - discount) : base;
}
}
4. プリミティブ型への過度な依存
// BAD
public void sendEmail(String email) {
if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Invalid email");
}
}
// GOOD - Value object
public record Email(String value) {
public Email {
if (!value.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
throw new IllegalArgumentException("Invalid email: " + value);
}
}
}
public void sendEmail(Email email) { ... }
5. 深いネスト
// BAD
public void process(Order order) {
if (order != null) {
if (order.isValid()) {
for (OrderItem item : order.getItems()) {
if (item.isAvailable()) {
if (item.getQuantity() > 0) {
// process
}
}
}
}
}
}
// GOOD - Guard clauses
public void process(Order order) {
if (order == null || !order.isValid()) {
return;
}
order.getItems().stream()
.filter(OrderItem::isAvailable)
.filter(item -> item.getQuantity() > 0)
.forEach(this::processItem);
}
品質メトリクスの目標値
| メトリクス | 目標値 | ツール |
|---|---|---|
| 循環的複雑度 | < 10 | Checkstyle、PMD |
| 認知的複雑度 | < 15 | PMD、SonarQube |
| メソッドの行数 | < 50 行 | Checkstyle |
| クラスの行数 | < 500 行 | Checkstyle |
| パラメーター数 | < 5 | Checkstyle |
| ネストの深さ | < 4 | PMD |
CI/CD との統合
GitHub Actions
name: Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Run quality checks
run: ./mvnw verify -Pquality
- name: Upload reports
uses: actions/upload-artifact@v4
if: always()
with:
name: quality-reports
path: |
target/checkstyle-result.xml
target/spotbugsXml.xml
target/pmd.xml
アンチパターン
| アンチパターン | 問題点 | 適切な方法 |
|---|---|---|
| すべての警告を抑制する | 実際の問題を隠してしまいます | 個別に修正するか、理由を明記します |
| CI で静的解析を行わない | 品質が時間とともに低下します | ビルドパイプラインに追加します |
| Checkstyle だけを実行する | バグや不適切なコードを見逃します | SpotBugs と PMD を組み合わせます |
| 複雑度のしきい値が高い | 保守できないコードを許容してしまいます | 循環的複雑度を < 10 に保ちます |
| パッケージ全体を除外する | 該当領域の品質を無視してしまいます | 除外対象を具体的に指定します |
簡単なトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| 生成コードで Checkstyle が失敗する | 除外パターンがない | 生成ディレクトリに <exclude> を追加します |
| DTO で SpotBugs が誤検出する | レコードで EI_EXPOSE_REP が発生する | DTO 用の除外パターンを設定します |
| PMD の処理が遅すぎる | すべてのファイルを解析している | 増分解析を設定します |
| Error Prone が競合する | バージョンが一致していない | JDK のバージョンに合わせます |
| CI で品質ゲートに失敗する | ローカルと設定が異なる | 設定ファイルをコミットします |
関連スキル
コメント
Markdown 対応