为什么自定义异常呢?主要有以下几个理由值得你去做:
1,统一异常,团队开发一定要有规则,不能自己想怎么抛异常就怎么抛,不仅仅是格式,可以对每种业务新建一个异常。
2,结合业务中断程序运行,有时候java代码本身不会有错误,但是不符合业务逻辑。例如用户想要发表评论,系统就会提示他要先登录,在这之前是没有java语法错误的,但是从业务逻辑上来说(或保证之后不会出错)这就需要抛错。
3,可以隐藏底层异常,只打印业务日志,不用输出堆栈信息。例如在查看商品详情这个操作中,可能包含查询商品基本信息,获取用户与商品关系,加入分享商品链接,载入商品评论,计算最近地铁站。。。多个步骤。如果在计算地铁站距离这一步出错了,那么可以直接打印汉字说明错误在这一步。便于排查。
自定义异常有几种方式:
1.新建异常类(继承自Exception或RuntimeException)
自定义父类异常
/**
* @author uiao
* @Title: 自定义父类异常
* @date 2018/8/615:36
*/
public class BaseException extends Exception {
public BaseException() {}
public BaseException(String message) {
super(message);
}
}
按照业务来自定义各种异常
/**
* @author uiao
* @Title: 自定义Bean转换异常
* @date 2018/8/615:42
*/
public class BeanConverterException extends BaseException {
public BeanConverterException() {
}
public BeanConverterException(String message) {
super(message);
}
}
/**
* @author uiao
* @Title: 自定义账单异常
* @date 2018/8/615:45
*/
public class BillException extends BaseException {
public BillException() {
}
public BillException(String message) {
super(message);
}
public String customException() {
return "账单异常";
}
}
/**
* @author uiao
* @Title: 自定义支付异常
* @date 2018/8/615:40
*/
public class PayException extends BaseException {
public PayException() {
super("支付异常");
}
public PayException(String message) {
super(message);
}
}
异常的使用
/**
* @author uiao
* @Title: 自定义异常使用
* @date 2018/8/615:58
*/
@Service
public class ExceptionDemoServiceImpl {
public void payTest() throws PayException {
if (true) {
throw new PayException("支付金额和订单金额不相符,请核对金额");
}
}
public void billTest() throws PayException, BillException {
if (true) {
throw new BillException("账单异常");
}
if (true) {
throw new PayException();
}
}
}
测试类
/**
* @author uiao
* @Title: 自定义异常测试类
* @date 2018/8/615:47
*/
@Controller
@RequestMapping(value = "exception", method = RequestMethod.GET)
public class ExceptionDemoController {
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(ExceptionDemoController.class);
@Autowired
private ExceptionDemoServiceImpl impl;
@GetMapping("beanConverter")
public void converter(HttpServletRequest request) {
try {
throw new BeanConverterException("bean 转换异常");
} catch (BeanConverterException e) {
logger.error(e.getMessage());
}
}
@GetMapping("pay")
public void pay(HttpServletRequest request) {
try {
impl.payTest();
} catch (PayException e) {
logger.error(e.getMessage());
}
}
@GetMapping("bill")
public void bill(HttpServletRequest request) {
try {
impl.billTest();
} catch (BaseException e) {
if (e instanceof BillException) {
logger.error(((BillException) e).customException());
} else if (e instanceof PayException) {
logger.error(e.getMessage());
}
}
}
}
2,重写HandlerExceptionResolver接口的resolveException方法做一个全局的异常处理(基于spring mvc)
这种方法不同于第一种,上一个可以针对每一种业务定义异常,这种方法作用范围更大,在Controller层通过throws把异常抛出来,交给spring mvc来处理,重写的方法里判断异常类型(当然这些异常类型也可以是我们自定义的)进而做出处理。
全局处理异常类
public class ExceptionHandler implements HandlerExceptionResolver,Ordered {
private static Logger logger = Logger.getLogger(ExceptionHandler.class);
private static Integer order = Integer.MIN_VALUE;
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Object handler, Exception e) {
logger.error("异常请求URL:" + httpServletRequest.getRequestURL());
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
logger.warn("处理器的方法为:" + method.getName());
}
logger.error("异常栈 ==>> ");
logger.error(ExceptionUtils.parseException(e));
if (e instanceof NoHandlerFoundException){
responseJson(httpServletResponse,"请求的页面不存在");
return new ModelAndView((String) null);
} else if(e instanceof ServletException){
responseJson(httpServletResponse,e.getMessage());
return new ModelAndView((String) null);
}
else if (e instanceof PermissionDefineException){
responseJson(httpServletResponse, "您没有权限进行该操作");
return new ModelAndView((String) null);
}
else if (e instanceof BaseException) {
responseJson(httpServletResponse, "数据异常~ 请稍后再试");
return new ModelAndView((String) null);
}else {
responseJson(httpServletResponse, "服务器异常~ 请稍后再试");
return new ModelAndView((String) null);
}
}
private void responseJson(HttpServletResponse response,String content){
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
ApiResponseBean respBean = ApiResponseBuilder.buildResponse(-1,content);
PrintWriter out =null;
try {
out = response.getWriter();
out.write(JsonUtils.toJSONString(respBean));
out.flush();
out.close();
} catch (IOException e) {
logger.error(ExceptionUtils.parseException(e));
}finally {
if(out!=null){
out.close();
}
}
};
@Override
public int getOrder() {
return order;
}
}
加入xml文件
<!--定义全局异常处理-->
<bean class="com.uiao.web.exception.ExceptionHandler"/>
另外:spring boot也支持注解的异常处理,后续补上。
3,在web.xml里配置(基于spring mvc)
其实这个不能算是异常处理,因为不涉及异常的一些捕获抛出,所有流程都是正常执行的,只是对最后结果做一个再处理。
<error-page>
<error-code>404</error-code>
<location>/notFound</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/sysError</location>
</error-page>
异常结果处理类
@Controller
public class ExceptionController {
@RequestMapping("notFound")
@ResponseBody
public Map<String,Object> processNotFountException(){
return Tools.getApiResponse(1,"页面未找到,请检查路径是否正确");
}
@RequestMapping("sysError")
@ResponseBody
public Map<String,Object> processException(){
return Tools.getApiResponse(1,"数据异常,请稍后再试");
}
}
这样可以给前端返回一些具体的提示。