参考文章来源:[leaforbook - 如何正确使用Java异常处理机制]
http://leaforbook.com/blog/jexception/original/practical.html
概述
Java异常处理机制的目的至少有三个:
一是归类处理不同的异常,
二是提供足够的信息方便调试,
三是让主流程代码保持整洁。
代码异常
指的是运行期异常,不需要显示声明,比如空指针、类型转换异常等等,这些异常可以通过代码的控制来规避掉业务异常
lcms中定义了BusinessException、UniqueKeyException等异常类,用来处理业务异常
异常的处理
异常均要有error日志
log.error(“异常信息”, e)不知道如何处理的情况下,异常向上抛出
throw e;业务需要情况下,异常向上转义并抛出
throw new BusinessException(“异常信息”, e);使用异常来进行验证信息的返回
- Excel导入验证、webservice接口验证等
Excel导入需呀验证一系列规则,比如各单元格内容是否必填、格式是否符合要求等
外部数据导入一般都是要求验证,争取则处理,不正确则返回错误信息。
在不定义返回多值(返回Map或ResultCollection对象)的情况下
如果不使用异常机制下,则需要返回一个个字符串,来作为判断的依据
而且后面获取数据的时候还要再次读取一次- Excel导入验证、webservice接口验证等
/**
* Excel人员导入数据必填验证
* @param sheet
* @return
* @throws Exception
*/
public static String personExcelDataRequired(HSSFSheet sheet)throws Exception{
for (int i = 1; i <= sheet.getLastRowNum(); i++) {//从第二行开始
int m=i+1;//行号
HSSFRow row = sheet.getRow(i);
if(!ExcelUtil.isBlankRow(row)){//不为空行
//每一行列数为6验证
int rowFirst=row.getFirstCellNum();//第一列index
int rowLast=row.getLastCellNum();//最后一列index
if(rowFirst==0 || rowFirst==1){//序号 可以不填
if(rowLast!=7){
return "第"+m+"行"+ImportConstants.DATA_COLUMN_ERROR;
}
}else {//第一列和第二列为空
return "第"+m+"行"+ImportConstants.DATA_ORIGN_COLUMN_ERROR;
}
//每一行非空、格式、字符长度验证
String message="";
for(int j=1;j<=row.getLastCellNum();j++){//从第二列"姓名"开始验证
HSSFCell cell=row.getCell(j);
switch(j){
//姓名
case 1:message=ExcelUtil.validateStringCell(cell,20);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_1+message;
}
break;
//性别
case 2:message=ExcelUtil.validateStringCell(cell,4);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_2+message;
}
break;
//身份证号码
case 3:message=ExcelUtil.validateStringCell(cell,20);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_3+message;
}
break;
//单位
case 4:message=ExcelUtil.validateStringCell(cell,50);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_4+message;
}
break;
//培训日期
case 5:message=ExcelUtil.validateStringCell(cell,50);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_5+message;
}
break;
//上岗证号
//case 6:message=ExcelUtil.validateStringOrNumricCell(cell,12);
case 6:message=ExcelUtil.validateStringCell(cell,10);
if(!message.equals("0")){
return "第"+m+"行,"+PersonConstants.EXCELHEAD_CELL_6+message;
}
break;
default:
break;
}
}
}
}
return "0";
}
/**
* 获得Excel personList
* @param sheet
* @return
* @throws Exception
*/
public static List<Person> getPersonExcelList(HSSFSheet sheet) throws Exception{
List<Person> list=new ArrayList<Person>();
//遍历每一行
for(int i=1;i<=sheet.getLastRowNum();i++){
HSSFRow row= sheet.getRow(i);
if(!ExcelUtil.isBlankRow(row)){
Person person=getPersonExcelRow(row);//获得一行的值
list.add(person);
}
}
return list;
}
/**
* 获取Excel的一行数值
* set Supplier
* @param row
* @return
* @throws Exception
*/
public static Person getPersonExcelRow(Row row) throws Exception{
Person p=new Person();
p.setName(row.getCell(1).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());
p.setSex(row.getCell(2).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());//性别为汉字
p.setIdcard(row.getCell(3).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());
p.setApplyUnit(row.getCell(4).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());
p.setRemark(row.getCell(5).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());//培训日期设为备注
p.setLicenseNumber(row.getCell(6).toString().trim().replaceAll(ConfigConstants.CN_SPACE,"").trim());//【年龄不足】【超龄】后面处理
//System.out.println("p:"+p);
return p;
}
这样写在数据量不是很大的情况下,实现功能是没有问题的,但就是代码不简洁,业务整体流程不顺畅。
又比如:
//=====================common check======================//
/**
* XXX 通用 接口验证
*
* @param type
* @param workSheet
* @param requiredXml
* @param notNullXml
* @return
*/
private String commonCheck(int type,Wms2ItosWorkSheet workSheet,
String[] requiredXml,String[] notNullXml){
System.out.println("workSheet.opDetail:"+workSheet.getOpDetail());
//=============验证workSheet 非空验证===============//
callResult=OpDetailUtil.validateWorkSheet(workSheet);
if(!VALID_SUCCESS.equals(callResult)){
return callResult;
}
//=============验证xml SAX解析===============//
callResult=OpDetailUtil.checkXmlByOpDetail(workSheet.getOpDetail());
if(!VALID_SUCCESS.equals(callResult)){
return callResult;
}
//=============解析xml===============//
rowList=OpDetailUtil.getRowListByOpDetail(workSheet.getOpDetail());
nodeList=OpDetailUtil.getNodeListByRowList(rowList);
//=============验证rowList,nodeList 所有节点,重复字段===========//
callResult=OpDetailUtil.checkRowList(nodeList,requiredXml);
if(!VALID_SUCCESS.equals(callResult)){
return callResult;
}
//=============解析rowList===============//
eleMap=OpDetailUtil.getElementMapByRowList(rowList);
//=============验证eleMap 必填字段================//
callResult=OpDetailUtil.checkElementMap(eleMap, notNullXml);
if(!VALID_SUCCESS.equals(callResult)){
return callResult;
}
//callResult为0,则执行 业务验证
switch(type){
//1.新单分派+CL
case WebserviceConstants.NEW_WORK_SHEET_CL:
callResult = ComplainsValidation.validateNewWorkSheetCL(workSheet, eleMap);
break;
//2.审核时间
case WebserviceConstants.NEW_WORK_SHEET_SH:
callResult = ComplainsValidation.validateNewWorkSheetSH(workSheet, eleMap);
break;
//3.回复时间
case WebserviceConstants.NEW_WORK_SHEET_HF:
callResult = ComplainsValidation.validateNewWorkSheetHF(workSheet, eleMap);
break;
//4.催单
case WebserviceConstants.NOTIFY_WORK_SHEET_REMIND:
callResult = ComplainsValidation.validateRemind(workSheet, eleMap);
break;
//5.备注
case WebserviceConstants.NOTIFY_WORK_SHEET_REMARK:
callResult = ComplainsValidation.validateRemark(workSheet, eleMap);
break;
//6.协同回复
case WebserviceConstants.REPLY_WORK_SHEET_XT:
callResult = ComplainsValidation.validateXTReply2(workSheet, eleMap);
//用第二套规则
break;
//7.回复(协同单、SS单)
case WebserviceConstants.REPLY_WORK_SHEET:
callResult = ComplainsValidation.validateReply(workSheet, eleMap);
break;
//8.归档
case WebserviceConstants.CHECKIN_WORK_SHEET:
callResult = ComplainsValidation.validateCheckin(workSheet, eleMap);
break;
default:callResult = "none";
break;
}
return callResult;
}
这是一个webservice对外发布接口的通用验证,接收的是14个字段和一段xml格式的remark。暂且不管代码结构写的好坏,其中
验证xml SAX解析
和 解析xml
基本上是相同的代码:
//================= 对xml的验证 =================//
/**
* 增加对BOM(<?xml version="1.0" encoding="utf-8"?>)的判断
*
* 检验SAX是否可以解析opDetail
*
* 1.首先,XML解析器根据文件的BOM来解析文件;
* 2.如果没找到BOM,由用XML里的encoding属性指定的编码;
* 3.如果xml里encoding没指定的话,就默认用utf-8来解析文档。
* 4.然后又可以推出,BOM和ENCODING都有的话,则以BOM指定的为准。
*
* @param opDetail
*/
public static String checkXmlByOpDetail(String opDetail) {
//解析xml格式
try {
Document jDoc = DocumentHelper.parseText(opDetail);
String encode = jDoc.getXMLEncoding();
if(StringUtil.isEmpty(encode)||"UTF-8".equalsIgnoreCase(encode)||"utf-8".equals(encode)){
//可以解析
}else{
return "opDetail的xml格式须为UTF-8";
}
//dom4j可以解析非utf-8格式的xml
// List<Element> l = jDoc.selectNodes("//opDetail/recordInfo/fieldInfo");
// System.out.println("==================l:"+l.size());
// for(Element e:l){
// System.out.println(e.elementText("fieldEnName"));
// }
} catch (DocumentException e1) {
e1.printStackTrace();
return e1.getNestedException().toString();
}
//验证sax解析
SAXReader saxReader = new SAXReader();
try {
//生成文档对应实体
Document saxDoc = saxReader.read(new ByteArrayInputStream(opDetail.getBytes("UTF-8")));
//获取指定路径下的元素列表,这里指获取所有的data下的row元素
saxDoc.selectNodes("//opDetail/recordInfo/fieldInfo");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return e.getMessage().toString();
} catch (DocumentException e) {
e.printStackTrace();
return e.getNestedException().toString();
}
//doc可以正常解析
return CHECK_SUCCESS;
}
//==================== 解析xml ======================//
/**
* 使用SAX解析opDetail
* @param opDetail
*/
@SuppressWarnings("unchecked")
public static List<Element> getRowListByOpDetail(String opDetail) {
List<Element> rowList=new ArrayList<Element>();
SAXReader saxReader = new SAXReader();
try {
//生成文档对应实体
Document doc = saxReader.read(new ByteArrayInputStream(opDetail.getBytes("UTF-8")));
//获取指定路径下的元素列表,这里指获取所有的data下的row元素
rowList=doc.selectNodes("//opDetail/recordInfo/fieldInfo");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
// return null;
} catch (DocumentException e) {
e.printStackTrace();
// return null;
}
//rowList可以为空,所以不在公共方法里验证
return rowList;
}
如果xml很长或者解析比较慢,一个实时性的webservice就会造成不必要的性能开销
在lcms中,对于接口过来的数据验证,全部通过异常的方式返回(定时任务运行接口程序)。
/**
*
* <p>
* Description: 任务执行
* </p>
*
* @param ctx ctx
*/
protected void work(JobExecutionContext ctx) {
this.thread = Thread.currentThread();
this.logger.info("=============== 定时任务处理开始 ==== " + this.dateFormat.format(new Date()));
try {
//默认成功
this.taskLog.setConsequence(Constants.TASK_RESULT_SUCCEED);
//抽象方法,具体任务的执行
doExcute(ctx);
} catch (Exception e) { //此处允许捕获Exception异常 byLiao
//其他异常
this.logger.error("", e);
this.taskLog.setConsequence(Constants.TASK_RESULT_FAILED);
//失败原因说明
this.taskLog.setConsequenceDesc(GetExceptionInfo.getStackTrace(e));
} finally {
this.logger.info("task status :" + this.taskLog.getConsequence());
}
this.logger.info("=============== 定时任务处理结束 ==== " + this.dateFormat.format(new Date()));
}
- 框架级别异常处理
SpringMVC异常,在baseController中,使用基于@ExceptionHandler异常处理
/**
*
* <p>
* Description: 基于@ExceptionHandler异常处理
* </p>
*
* @param request 请求
* @param response 响应
* @param ex ex
* @return errorMessage
*/
@ExceptionHandler
public String exp(HttpServletRequest request, HttpServletResponse response, Exception ex) {
logger.error(ex, ex);
request.setAttribute("ex", ex);
//如果是json格式的ajax请求
if (request.getHeader("accept").indexOf("application/json") > -1
|| (request.getHeader("X-Requested-With") != null
&& request.getHeader("X-Requested-With")
.indexOf("XMLHttpRequest") > -1)) {
return ajaxExceptionHandle(request, response, ex);
} else {
//普通请求
//根据生成环境和开发环境,分别指向不同的异常处理页面
//生产环境--不对外暴露异常信息:根据服务器host判断
String host;
host = getHost(request);
if (host.equalsIgnoreCase(LOCAL_HOST) || host.equalsIgnoreCase(LOCAL_HOST2)) {
//debug--开发环境可见
return "exception/debugError";
} else {
//生产环境--请联系管理员
return "exception/productError";
}
}
}
/**
* <p>
* Description: ajax异常处理
* </p>
*
* @param request req
* @param response rep
* @param ex ex
*
* @return Str
*/
public String ajaxExceptionHandle(HttpServletRequest request, HttpServletResponse response, Exception ex) {
//如果是json格式的ajax请求
// response.setStatus(STATE_INNER_SERVER_ERROR);
// response.setContentType("application/json;charset=utf-8");
// try {
// PrintWriter writer;
// writer = response.getWriter();
// writer.write(ex.getMessage());
// writer.flush();
// } catch (IOException ioe) {
// logger.error(ioe);
// }
// return null;
return "exception/ajaxError";
}
另外,在Struts2中,定义了全局异常和全局返回结果来处理异常
<global-results>
<result name="appBaseError">/pages/common/appBaseError.jsp</result>
<result name="runtimeError">/pages/common/runtimeError.jsp</result>
<result name="unkownError">/pages/common/runtimeError.jsp</result>
<result name="timeOut">/pages/common/timeOut.jsp</result>
<result name="input">/pages/common/input.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="appBaseError" exception="com.broadtext.framework.exception.BaseException"></exception-mapping>
<exception-mapping result="runtimeError" exception="com.broadtext.framework.exception.CrashException"></exception-mapping>
<exception-mapping result="unkownError" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>