[[exceptions]]
= 异常处理 / Exceptions

关于java的异常处理可以参考这篇文章 http://www.importnew.com/20139.html[《Java异常处理的10个最佳实践》]，下面对本框架中的异常使用进行说明。

== 基础业务异常
框架中定义了基础的业务异常类 BusinessException，该异常类通过`@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)`定义了请求返回的状态码为500，并对国际化做了支持。
[source,java]
----
package net.parim.spark.common.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

import net.parim.spark.common.spring.SpringUtil;
import net.parim.spark.i18n.LocaleMessageSourceService;
/**
 * 基础业务异常类，通过增加成员变量 messageKey 和 args 并重写父类的 getMessage() 方法对国际化进行支持
 * @author jiawei
 *
 */
@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
public class BusinessException extends RuntimeException {

	private static final long serialVersionUID = 1L;
	/**
	 * 国际化参数名
	 */
	protected String messageKey;
	/**
	 * 占位符形式对应的参数值
	 */
	protected Object[] args;
	protected LocaleMessageSourceService messageSourceService = SpringUtil.getBean(LocaleMessageSourceService.class);
	public BusinessException() {
		super();
	}

	public BusinessException(String message, Throwable cause) {
		super(message, cause);
	}
	public BusinessException(String messageKey, Object[] args) {
		super();
		this.messageKey = messageKey;
		this.args = args;
	}

	public BusinessException(String message) {
		super(message);
	}

	public BusinessException(Throwable cause) {
		super(cause);
	}

	protected BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
	@Override
	public String getLocalizedMessage() {
        return getMessage();
    }
	@Override
    public String getMessage() {
	    String defaultMessage = messageSourceService.getMessage(super.getMessage(),super.getMessage());
        return messageSourceService.getMessage(messageKey, args, defaultMessage);
    }
	
}

----
[NOTE]
由于spring在处理`@ResponseStatus`注解时，`reason`值的优先级大于异常类的getMessage()方法，所以不要在ResponseStatus中设置reason的值，否则国际化会失效。

== 异常类的使用

=== BusinessException
使用该构造函数时`message`的值可以是国际化的key，也可以使普通的字符串。调用异常的`getMessage()`会优先国际化文件key对应的value，如果找不到则返回message本身。

在国际化文件中定义如下信息，更多国际化配置请参考 <<i18n,国际化>>
```
system.name=星火
system.welcome=欢迎登录{0}系统
```
==== 使用国际化Key构造异常（支持国际化）
[source,java]
----
@ResponseBody
@RequestMapping("i18n/ex")
public String ex(HttpServletRequest request, HttpServletResponse response, String lang) {
	// 使用国际化Key构造
    throw new BusinessException("system.name");
}
----
通过ajax请求会返回如下信息：
```
{
    "timestamp": 1499403137633,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "net.parim.spark.common.exceptions.BusinessException",
    "message": "星火",
    "path": "/i18n/ex"
}
```
==== 使用国际化Key与占位符参数构造异常（支持国际化）
[source,java]
----
@ResponseBody
@RequestMapping("i18n/ex")
public String ex(HttpServletRequest request, HttpServletResponse response, String lang) {
	// 使用国际化Key与占位符参数构造
    throw new BusinessException("system.welcome", new String[]{messageSource.getMessage("system.name")});
}
----
通过ajax请求会返回如下信息：
```
{
    "timestamp": 1499403337783,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "net.parim.spark.common.exceptions.BusinessException",
    "message": "欢迎登录星火系统",
    "path": "/i18n/ex"
}
```
==== 使用普通字符串构造异常（不支持国际化）
[source,java]
----
@ResponseBody
@RequestMapping("i18n/ex")
public String ex(HttpServletRequest request, HttpServletResponse response, String lang) {
	// 通过普通字符串构造
    throw new BusinessException("简单异常测试");
}
----
通过ajax请求会返回如下信息：
```
{
    "timestamp": 1499399332991,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "net.parim.spark.common.exceptions.BusinessException",
    "message": "简单异常测试",
    "path": "/i18n/ex"
}
```
=== 自定义业务异常
在基础的业务异常不能满足业务需求时，可以通过继承BusinessException创建新的异常,实例如下,下面的异常通过`@ResponseStatus(value=HttpStatus.BAD_REQUEST)`重新定义了当请求遇到该异常时返回的状态码为400：
[source,java]
----
package net.parim.spark.common.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value=HttpStatus.BAD_REQUEST)
public class SimpleException extends BusinessException {
	private static final long serialVersionUID = -1351524603475045914L;

	public SimpleException() {
		super();
	}

	public SimpleException(String message, Throwable cause) {
		super(message, cause);
	}
	public SimpleException(String messageKey, Object[] args) {
		super(messageKey, args);
	}

	public SimpleException(String message) {
		super(message);
	}

	public SimpleException(Throwable cause) {
		super(cause);
	}

	protected SimpleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
	
}

----

ajax请求时返回:
```
{
    "timestamp": 1499405807406,
    "status": 400,
    "error": "Bad Request",
    "exception": "net.parim.spark.common.exceptions.SimpleException",
    "message": "欢迎登录{0}系统",
    "path": "/i18n/ex"
}
```
[NOTE]
当使用其他异常时，需要通过LocaleMessageSourceService来处理国际化信息
