SSMプロジェクト構築とSpringMVC高度機能実装:統一レスポンス、例外処理、前後端連携、フィルタチェーン

1. SSMフレームワーク統合

1.1 開発フローの概要

SSM(Spring+SpringMVC+MyBatis)統合プロジェクトでは、各層の設定を明確に分離し、依存関係をSpringコンテナで管理します。構築手順は以下の通りです。

1.2 統合設定の詳細

プロジェクト初期構成により、Maven依存管理下での各ライブラリバージョン指定と、パッケージ構成の標準化が行われます。依存関係は以下为主体:

  • Web機能(spring-webmvc)
  • JDBC連携(spring-jdbc、mybatis-spring)
  • DB接続(mysql-connector, druid)
  • サーバー実行(tomcat7-maven-plugin)
  • JSON変換(jackson-databind)

Spring由来の設定はコンフィグクラスで管理され、分離された各構成クラス(JdbcConfig, MybatisConfig, SpringConfig)が@Importで統合されます。以下は主な構成例です:

@Configuration
@ComponentScan("priv.dandelion.service")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {}

データソース管理ではDruidDataSourceが使用され、トランザクションマネージャ設定によりJDBC層の整合性が担保されます。MyBatis側ではSqlSessionFactoryBeanMapperScannerConfigurerを用いて、実装互換性のあるマッピング設定がなされます。

1.3 ビジネスロジック実装

DAOにはMapperアノテーション(@Select, @Insertなど)を直接付与し、CRUDを実装します。Service実装では@Transactionalアノテーションをインタフェースに付与し、宣言的トランザクションを有効化します。

@Service
@Transactional
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Override
    public boolean save(Book book) {
        return bookDao.save(book) > 0;
    }
    // 省略
}

Controller層は@RestControllerで定義し、各HTTPメソッドに対応するメソッドを実装します。

1.4 テスティング戦略

ユニットテストではSpringJUnit4ClassRunnerを使用し、@ContextConfigurationSpringConfigクラスをロードすることで、実環境に近いコンテキストを再現します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
    @Autowired
    private BookService service;
}

PostmanによるE2Eテストは後段の統合検証に割愛。


2. 前後端統一レスポンスフォーマット定義

2.1 JSON構造の統一規約

フロントエンドとのデータ交換では、ステータスコード、ペイロード、エラーメッセージを整合性のある構造で返却します。これにより、通信状態の可視化とエラーハンドリングの標準化が可能になります。

// 成功時(データあり)
{
  "code": 20011,
  "data": { ... },
  "msg": ""
}

// 成功時(データなし,変化の説明)
{
  "code": 20021,
  "data": true,
  "msg": ""
}

// 失敗時
{
  "code": 20010,
  "data": null,
  "msg": "入力データ不正"
}

2.2 統一結果クラス実装

レスポンスDTOとしてResultクラスを定義し、Codeクラスに業務/システム別ステータスコードを記述します。

public class Result {
    private Object data;
    private Integer code;
    private String msg;
    // コンストラクタ・getter/setter
}
public class Code {
    public static final Integer SAVE_OK = 20011;
    public static final Integer UPDATE_ERR = 20030;
    // ... 業務ロジックのそれぞれに割当
}

@PostMappingメソッドはResultラッパーを返却し、発生した状態に応じて動的コードを適用します。


3. グローバル例外処理戦略

3.1 例外分類と対応方針

例外の種類は以下の3つに分類し、それぞれ異なる処理フローを適用します。

  • 業務例外(BusinessException):入力チェック失敗、データ無効など、ユーザー向け通知
  • システム例外(SystemException):タイムアウト、DB接続エラーなど、運用担当者向けアラート+デバッグ情報記録
  • その他の予期せぬ例外:構成ミスなど、開発者向け報告+基本エラーメッセージを表示

3.2 コードによる例外表現

カスタム例外クラスはRuntimeExceptionを継承し、カスタムコードとメッセージを保持します。

public class SystemException extends RuntimeException {
    private Integer code;
    public SystemException(Integer code, String message) { ... }
    // getter/setter/code
}

service層では、原始的なArithmeticExceptionやnullチェックを包装し、業務的意味のある例外へ昇華します。

try {
    return bookDao.getById(id);
} catch (ArithmeticException e) {
    throw new SystemException(50002, "サーバー接続タイムアウト");
}

3.3 @RestControllerAdviceによるグローバルハンドラ

統一例外ハンドラは@RestControllerAdviceで定義され、スレッド分離されたエラーロギングやメール送信もここで実行可能です。

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(SystemException.class)
    public Result handleSysError(SystemException e) {
        log.error("System exception: {} [code: {}]", e.getMessage(), e.getCode());
        return new Result(e.getCode(), null, e.getMessage());
    }

    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessError(BusinessException e) {
        return new Result(e.getCode(), null, e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result handleUndefinedError(Exception e) {
        return new Result(59999, null, "システムエラーが発生しました");
    }
}

4. 前後端連携実装

4.1 ビュー環境設定

フロントエンドはVue.js+Element UI+Axiosで構成され、静的リソースはSpringMVCで直接配信されます。以下設定により、/pages/**以下のHTMLファイルが解放されます。

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        // 他の静的リソース同様
    }
}

4.2 各機能の連携フロー

  • 一覧表示axios.get("/books")で全件取得、レスポンスのdata.dataをバインド
  • 追加:フォーム送信→POST /books→成功時コード20011ならモーダルをクローズし一覧再取得
  • 編集GET /books/:idで既存データをformDataへ展開→PUT /booksで更新
  • 削除DELETE /books/:idにより物理削除。ユーザー確認($confirm)を経由
getAll() {
    axios.get("/books").then(res => {
        this.dataList = res.data.data;
    });
},
handleAdd() {
    axios.post("/books", this.formData).then(res => {
        if (res.data.code === 20011) {
            this.dialogFormVisible = false;
            this.$message.success("追加成功");
        } else {
            this.$message.error(res.data.msg || "追加エラー");
        }
        this.getAll();
    });
}

5. SpringMVCフィルタチェーン(インターセプタ)

5.1 フィルタ・サーキット動作

インターセプタはHandlerInterceptorを実装し、メソッドコール前・後・完了時にフックします。メソッドの実行 덩어리를 캡처하여 요청 기반 인증, Log 수집, 요청 후속 처리를 처리합니다。Interstitial許可否かはpreHandleの返り値で操作可能です。

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(...) {
        String authHeader = request.getHeader("Authorization");
        return StringUtils.hasText(authHeader);
    }

    @Override
    public void postHandle(...) { ... }

    @Override
    public void afterCompletion(...) { ... }
}

5.2 登録とパス指定

インターセプタはWebMvcConfigurer#addInterceptorsで登録し、パスパターンで対象を絞ります。

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private AuthInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor)
                .addPathPatterns("/books/**")
                .excludePathPatterns("/login");
    }
}

5.3 複数インターセプタの動作解析

連続登録するとスタック構造で実行され、前処理は宣言順、後処理は逆順で実行されます。preHandleがfalseを返すと、その段階で以降の前処理と本体処理を中断し、直前まで通過したインターセプタのafterCompletionのみ呼び出されます。


付録 内部コード一覧(統一状態コード例)

グループ コード範囲 各代号例
登録 20011/20010 成功/失敗
更新 20031/20030 成功/失敗
削除 20021/20020 成功/失敗
照会 20041/20040 成功/失敗(データ存在性)
システムエラー 50001~59999 タイムアウト、未知エラー
業務エラー 60001~69999 入力不正、権限拒否

8月4日 21:08 投稿