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側ではSqlSessionFactoryBeanとMapperScannerConfigurerを用いて、実装互換性のあるマッピング設定がなされます。
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を使用し、@ContextConfigurationでSpringConfigクラスをロードすることで、実環境に近いコンテキストを再現します。
@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 | 入力不正、権限拒否 |