博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用 Exception 写出优雅的代码
阅读量:7052 次
发布时间:2019-06-28

本文共 5680 字,大约阅读时间需要 18 分钟。

  hot3.png

首先先来看一组代码:

Controller:

@RequestMapping	public ModelAndView doCreate(HttpServletRequest request, Map
 out, Events event, String gmtStartStr, String gmtEndStr, String invitedAccountId){ eventsService.initGmt(event, gmtStartStr, gmtEndStr); event.setUid(getSessionUser(request).getUid()); Integer id = eventsService.saveEvent(event); if(id!=null && id.intValue()>0){// eventsService.appendJoiner(id, invitedAccountId); return new ModelAndView("redirect:index.do"); } return new ModelAndView("/events/create"); }

Service:

@Override	public Integer saveEvent(Events event) {		if(event == null){			return null;		}				if(Strings.isNullOrEmpty(event.getName())){			return null;		}				event.setStatusPublic(event.getStatusPublic()==null?Events.STATUS_DEFAULT:event.getStatusPublic());				eventsMapper.save(event);				return event.getId();	}

这组代码应该算是比较典型的 controller 调用 service 实现业务功能的代码,以前的系统里一直也是这么干的,也有确实没有遇到什么特别严重的问题,但是在错误返回上总是会有那么点小纠结,有时候,错误是由于参数引起的,有时候是由于数据库操作引起的,在controller层其实不能特别准确的判断,特别是那些复杂的 service 方法,错误往往产生于众多逻辑中的一个,如果只是返回 null,controller 就只能知道方法是执行成功还是失败,但不知道失败的细节。另一个问题是 null 值判断被移到了 controller 里,这导致 controller 参与了业务,与 controller 应有的职责不符。

那么如何改进呢?

针对错误细节,我们可以把一个业务方法执行过程中,不可继续的环节认为是一种错误,只不过这种错误是开发人员自己可控的逻辑错误。错误在软件系统里也叫异常,所以,用异常来改进上述代码:

Controller:

@RequestMapping	public ModelAndView doCreate(HttpServletRequest request, Map
 out, Events event, String gmtStartStr, String gmtEndStr, String invitedAccountId){ eventsService.initGmt(event, gmtStartStr, gmtEndStr); event.setUid(getSessionUser(request).getUid()); try{     Integer id = eventsService.saveEvent(event);     return new ModelAndView("redirect:index.do"); }catch(Exception e){     out.put("error",e.getMessage());                    return new ModelAndView("/events/create"); } }

Service:

@Override	public Integer saveEvent(Events event) throws Exception{		if(event == null){			throw new Exception("event can not be null.");		}				if(Strings.isNullOrEmpty(event.getName())){			throw new Exception("event.name can not be null.");		}				event.setStatusPublic(event.getStatusPublic()==null?Events.STATUS_DEFAULT:event.getStatusPublic());				eventsMapper.save(event);		if(event.getId() ==null || event.getId()<=0){		    throw new Exception("event save failure.");		}				return event.getId();	}

以上改进的好处是在 controller 层很容易知道 service 出错的细节,同时也居然了处理 null 值判断问题,也可以在 controller 层做统一异常处理,比如跳转到错误页面,或返回错误的 JSON 等。

但是,实际的项目中,service 层业务并不总是这么简单,当 service 层中使用的工具或者类中本身就有异常抛出的时候,因为抛出的是 Exception,所以其他的异常信息被忽略了,在 controller 里仍然不能准确把握要处理的异常,这可能会导致前端页面无法正确提示不同的异常信息。

既然要区分不同的异常,那么把自己业务里的异常都用自定义异常类 ServiceException 来抛出不就可以解决问题了吗,代码改进如下:

Controller:

@RequestMapping	public ModelAndView doCreate(HttpServletRequest request, Map
 out, Events event, String gmtStartStr, String gmtEndStr, String invitedAccountId){ eventsService.initGmt(event, gmtStartStr, gmtEndStr); event.setUid(getSessionUser(request).getUid()); try{     Integer id = eventsService.saveEvent(event);     return new ModelAndView("redirect:index.do"); }catch(ServiceException e){     out.put("error",e.getMessage());                    return new ModelAndView("/events/create"); } }

Service:

@Override	public Integer saveEvent(Events event) throws ServiceException{		if(event == null){			throw new ServiceException("event can not be null.");		}				if(Strings.isNullOrEmpty(event.getName())){			throw new ServiceException("event.name can not be null.");		}				try{    		    event.setStatusPublic(event.getStatusPublic()==null?Events.STATUS_DEFAULT:event.getStatusPublic());		}catch(OtherException e){		    throw new ServiceException("set status failure.", e);		}				eventsMapper.save(event);		if(event.getId() ==null || event.getId()<=0){		    throw new Exception("event save failure.");		}				return event.getId();	}

嗯,到这里为止,代码看上去已经比较优雅了,让我们再来找找茬,首先是参数判定,虽然现在是用自己的异常类来做了处理,但 service 参数传递的错误应属于开发调用时就没正确调用导致,级别应属于Error级别,这种级别的错误应在开发时就被处理掉,正常情况下不应有这种情况发生,也不应该与业务上的逻辑错误归为一类,而 jdk 本身提供了参数较验的异常 IllegalArgumentException,这个异常继承自 RuntimeException,我们可以好好利用一下这个异常类。

另外一方面,现在 ServiceException 返回的错误信息对用户来说并不友好,应改进成用户可以理解的语言,比如改成国际化配置文件中的 key,这样可以直接在 controller 层得到有设定好的错误提示信息,也可以在 controller 层直接抛提供给前端,让前端转成国际化结果。

代码再次改进:

Controller:

@Resource	private MessageSource messageSource;		@RequestMapping	public ModelAndView doCreate(HttpServletRequest request, Map
 out, Events event, String gmtStartStr, String gmtEndStr, String invitedAccountId, Locale locale){ eventsService.initGmt(event, gmtStartStr, gmtEndStr); event.setUid(getSessionUser(request).getUid()); try{     Integer id = eventsService.saveEvent(event);     return new ModelAndView("redirect:index.do"); }catch(ServiceException e){     out.put("error",messageSource.getMessage(e.getMessage(), null, locale));                    return new ModelAndView("/events/create"); } }

Service:

@Override	public Integer saveEvent(Events event) throws ServiceException{		if(event == null){			throw new IllegalArgumentException("event can not be null.");		}				if(Strings.isNullOrEmpty(event.getName())){			throw new IllegalArgumentException("event.name can not be null.");		}				try{    		    event.setStatusPublic(event.getStatusPublic()==null?Events.STATUS_DEFAULT:event.getStatusPublic());		}catch(OtherException e){		    throw new ServiceException("EVENT_SET_STATUS_FAILURE", e);		}				eventsMapper.save(event);		if(event.getId() ==null || event.getId()<=0){		    throw new Exception("EVENT_SAVE_FAILURE");		}				return event.getId();	}

至此,代码优化暂时告一段落,代码的改进是无止境的,我们不应停留在原有的模式上,要不断寻求新的改进方法,而在改进过程中,更应注重 JDK,JAVA 本身所提供了各类工具和方法。

相关连接:

关于 null 判断的问题,importNews 上有一篇文章写得很好:

转载于:https://my.oschina.net/mays/blog/481139

你可能感兴趣的文章
Swift设置自动行高
查看>>
171. Excel Sheet Column Number
查看>>
简单深搜
查看>>
java-http请求
查看>>
WMI VS. ExplorerOM
查看>>
问题的定义和管理复杂度《Code Complete 2》
查看>>
Android init.rc解析【转】
查看>>
算法(Algorithms)第4版 练习 2.2.11(2)
查看>>
云解放了计算机这台机器,让计算的能力彻底从一个箱子里释放出来,回归了计算的本质。...
查看>>
Java ---- baidu评价抽取关键词-商品评论
查看>>
排序算法之选择排序
查看>>
三门问题
查看>>
ES6之主要知识点(十)Proxy
查看>>
UITableView
查看>>
list详解
查看>>
oracle学习篇六:子查询
查看>>
关于获取客户端Mac地址
查看>>
紫书 例题 10-9 UVa 1636 (概率计算)
查看>>
51nod 01背包
查看>>
outlook anywhere 配置
查看>>