発生した問題
SpringのBeanのスコープ理解不足から発生した問題。
複数ユーザーが同時にPDFファイルをダウンロードした際に、異なるユーザーのファイルが取得される事象が発生。
問題の影響
- ユーザーA: 発注書をダウンロード要求
 - ユーザーB: 見積書をダウンロード要求(同時刻)
 - 結果: ユーザーAに見積書が誤って配信
 
原因: スコープ設定の誤り
問題のあるコード構造
@Component  // デフォルトでsingleton
public class FileBean {
    // クラス変数でファイルパスを保持
    private String downloadFilePath;  // スレッドセーフでない
    public void setDownloadFilePath(String path) {
        this.downloadFilePath = path;
    }
    public void downloadFile() {
        // downloadFilePathを使用してファイルをダウンロード
    }
}
設計上の問題点
- 
シングルトンBeanでの状態保持
- デフォルトのシングルトンスコープを使用
 - クラス変数で状態を保持
 - 複数スレッドで共有される変数が保護されていない
 
 - 
スレッドセーフ性の欠如
- 並行アクセスに対する考慮がない
 - 変数の可視性が適切に制御されていない
 
 
SpringのBeanスコープ解説
| スコープ | 説明 | 用途 | 
|---|---|---|
| singleton | コンテナごとに1インスタンス | 状態を持たないサービス | 
| prototype | 取得ごとに新規インスタンス | 状態を持つ必要がある場合 | 
| request | HTTPリクエストごとに1インスタンス | リクエスト固有の処理 | 
| session | HTTPセッションごとに1インスタンス | ユーザー固有の処理 | 
| application | ServletContextごとに1インスタンス | アプリケーション共通処理 | 
正しい実装パターン
1. スコープの適切な設定
@Component
@Scope("prototype")  // または @RequestScope
public class FileBean {
    private String downloadFilePath;
    // ... 他のメソッド
}
2. スレッドローカル変数の使用
@Component
public class FileBean {
    private ThreadLocal<String> downloadFilePath = new ThreadLocal<>();
    
    public void setDownloadFilePath(String path) {
        downloadFilePath.set(path);
    }
    
    public String getDownloadFilePath() {
        return downloadFilePath.get();
    }
    
    public void clearDownloadFilePath() {
        downloadFilePath.remove();  // 重要: リソースの解放
    }
}
3. メソッドパラメータでの受け渡し
@Component
public class FileBean {
    public void downloadFile(String filePath) {
        // filePathを直接使用
    }
}
実装時の注意点
- 
シングルトンBeanの原則
- 状態を持たない設計を心がける
 - 必要な状態は適切なスコープで管理
 
 - 
スレッドセーフ性の確保
- 共有リソースへのアクセスを保護
 - 適切な同期メカニズムの使用
 - 不変オブジェクトの活用
 
 - 
リソース管理
- ThreadLocalを使用する場合は確実な解放
 - メモリリークの防止
 
 
参考文献