スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Spring MVC 例外ハンドリング

今回は例外ハンドリングを実装したいと思う。

Spring MVCでは、例外ハンドリングのスコープとして、
①アプリケーション単位
②コントローラ単位
で設定できるので、それぞれで動作確認まで行ってみる。

なお、題材は以前実装したファイルアップロードを対象としている。

■ファイルアップロードに関する過去の記事
Spring MVC ファイルアップロードを実装する[入力チェック編]

◎動作検証にあたっての各バージョンは以下の通り
  • SpringFramework 3.2.8.RELEASE
  • Java 1.7
  • Tomcat 7.0


1.SystemException.java

package jp.co.sample.exception;

public class SystemException extends RuntimeException {

private static final long serialVersionUID = 1L;

public SystemException(){
super();
}

public SystemException(String message) {
super(message);
}

public SystemException(String message, Throwable cause) {
super(message, cause);
}

public SystemException(Throwable cause) {
super(cause);
}
}

「①アプリケーション単位」用の例外クラスを追加。


2.BookException .java

package jp.co.sample.exception;

public class BookException extends RuntimeException {

private static final long serialVersionUID = 1L;

public BookException(){
super();
}

public BookException(String message) {
super(message);
}

public BookException(String message, Throwable cause) {
super(message, cause);
}

public BookException(Throwable cause) {
super(cause);
}
}

「②コントローラ単位」用の例外クラスを追加。


3.exception.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<link href="<c:url value="/css/bootstrap.min.css" />" rel="stylesheet">
<link href="<c:url value="/css/bootstrap-theme.min.css" />" rel="stylesheet">
<script src="<c:url value="/js/bootstrap.min.js" />"></script>
<title>exception</title>
</head>
<body>
<div class="well">
<font color="red">Exception</font>
</div>
<br>GlobalException : ${exception.message}
<br>BookException : ${message}
</body>
</html>

例外用のJSPを用意。
GlobalExceptionが「①アプリケーション単位」で例外発生した場合のメッセージエリアで、BookExceptionが「②コントローラ単位」で例外発生した場合のメッセージエリアとして、どちらで発生したのか分かるようにしている。
ちなみに${exception.message}の「exception」は、「①アプリケーション単位」で暗黙的に使えるオブジェクトとなる。

まずは、「①アプリケーション単位」の例外ハンドリング設定を行いたいと思う。

4.mvc-config.xml(関連箇所のみ掲載)




exception
exception




「①アプリケーション単位」の例外ハンドリングは、Bean定義ファイルで設定できる。
「prop」タグで例外クラスと対応するView名(今回の場合、「excepion.jsp」)を定義する。

5.BookController.java(関連箇所のみ掲載)

package jp.co.sample.book.controller;

import jp.co.sample.exception.BookException;
import jp.co.sample.exception.SystemException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
//他のimport文省略

@Controller
@RequestMapping("/book")
@SessionAttributes("bookSearchForm")
public class BookController {

//変数定義省略

@RequestMapping(value = "/edit/update", method = RequestMethod.POST)
public String update(@Valid BookForm form, BindingResult result, Model model,
@RequestParam(value="uploadFile",required=false) MultipartFile file) {

logger.info("update start");
if (file.getSize() > uploadFileSize) {
result.rejectValue("cover", "error.fileSize",
new Object[]{uploadFileSize},"File size error");
}
if (result.hasErrors()) {
return "editBook";
}

if (!file.isEmpty()) {
byte[] fileContent = null;
InputStream is = null;
try {
is = file.getInputStream();
fileContent = IOUtils.toByteArray(is);
form.setCover(fileContent);
//動作確認用に一時的に追加
//throw new IOException("test");
} catch (IOException e) {
//「SystemException」「BookException」の一方をコメント化して動作確認
//「1.アプリケーション単位」
throw new SystemException(e);
//「2.コントローラ単位」
//throw new BookException(e);
} finally {
IOUtils.closeQuietly(is);
}
}

BookEntity entity = BookUtil.copyProperties(form);
//登録・編集の判定
if (entity.getBookId() > 0) {
bookService.editEntity(entity);
} else {
bookService.addEntity(entity);
}
return "redirect:/book/edit/" + entity.getBookId() + "/update";
}

@ExceptionHandler
public ModelAndView bookException(BookException e){
logger.info("bookException start");
ModelAndView mav = new ModelAndView("exception");
mav.addObject("message", e.getMessage() + " [bookController]");
return mav;
}
//他のメソッド省略
}

39~43行目がファイルアップロードで発生した「IOException」をラッピングして、リスローしている箇所となる。(今までは「e.printStackTrace();」としていた。)

「①アプリケーション単位」の場合、41行目「throw new SystemException(e);」とすれば、「4.mvc-config.xml」の設定により、exception.jspに遷移してくれる。

「②コントローラ単位」の場合、43行目「throw new BookException(e);」とすると、59行目「@ExceptionHandlerアノテーション」を付与したbookExceptionメソッドが実行される。引数の「BookException e」は自動でバインドされる。そして、今回のポイントは、「public String bookException(BookException e, Model model)」と定義できない点となる。(「ERROR ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: public java.lang.String jp.co.sample.book.controller.BookController.bookException(jp.co.sample.exception.BookException,org.springframework.ui.Model) java.lang.IllegalStateException: No suitable resolver for argument [1] [type=org.springframework.ui.Model]」の例外が発生。) 例外メッセージを表示しないのであれば、@ExceptionHandler(BookException.class) として、「public String bookException()」とすればよいのだが、「①アプリケーション単位」は表示できて「②コントローラ単位」ができないわけがないと思い、あれこれ試してみる。

辿り着いたのが、ModelAndViewクラスとなる。62行目で、コンストラクタの引数にView名を渡して、63行目「addObject」メソッドを使って、JSP側で例外メッセージを「message」で参照するよう設定している。(Modelの「addAttribute」メソッドと同じような使い方。)なお、例外メッセージは意図的に「①アプリケーション単位」と異なるようにしている。あとは、ModelAndViewを返せばよい。以前からModelAndViewクラスの存在は知っていたが、今までString型のView名を返していたので、特に使うことがなかった。今回、Modelが使えず困っていたので、ModelAndViewクラスの便利さを知ることができた。


6.動作確認
①アプリケーション単位
Exception_1.png

②コントローラ単位
Exception_2.png

それぞれのスコープに応じて、例外ハンドリングされていることが確認できる。

■過去のSpring関連記事
Spring関連記事 Index

スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

bookmount8

Author:bookmount8
システムエンジニア。サーバーサイドでjavaを扱うことが多い。最近は、ミドルやフロント周りも関心あり。

最新記事
カテゴリ
検索フォーム
最新コメント
月別アーカイブ
これまでの訪問者数
ブロとも申請フォーム

この人とブロともになる

RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。