这是对前一段时间异常处理的一个总结
java异常分为两类 运行时异常和受检异常。两种异常的应用场景如下:
运行时异常: 调用代码不能继续执行,需要立即终止。通常是由于错误的操作,比如数据库connectionURL写错了等等。
受检异常: 调用代码需要进一步处理和恢复,需要显式捕捉,并且在代码产生异常后清理资源(finally语句块)。
Original method
public void doSomething() throws SomeException {
///这是一个普通方法,声明了受检异常
}
为了调用doSomething,可以用try catch包裹doSomething()
public void doSomethingElse(){
try {
doSomething();
} catch (SomeException e) {
e.printStackTrace();
}
}
为了调用doSomething,也可以声明受检异常
public void doSomethingElse() throws SomeException {
doSomething();
}
但是这两种方式不是每种情况都适用的,假如是类似FileOutputStream这样的需要释放资源的操作一般来说应该使用try catch包裹。
另外:Java 提供了在try语句块中自动释放资源的功能,类似C#的using()语句块,但是不如using()优雅。这样可以避免手动释放资源(还是比之前好一点的
try (OutputStream outputStream = new FileOutputStream(filePath)) {
//
} catch (IOException e) {
//
}
另外的另外: Lombok提供了@Cleanup注解,更加方便了资源释放,下面是栗子:
public static boolean isImage(@NotNull final File tempFile)
throws Exception {
@Cleanup ImageInputStream imageInputStream = ImageIO.createImageInputStream(tempFile);
return imageInputStream != null;
}
然后就是对Repository/DAO/Service设计的一点思考:
操作数据库的操作一般都要有事务控制,失败了要回滚,刚开始是手写事务,发现代码很难看!事务管理加上try catch弄得代码很复杂,而实实在在的业务代码就埋没在这一坨代码中了。
后来知道Spring有事务注解@Transactional(此处不展开
这样的话代码中就不能有try catch,因为一旦捕获了异常(没有再次抛出),Spring就会认为没有抛异常,程序正确运行从而提交了事务,导致问题。
所以:我是这么做的,使用Spring AOP的方式记录异常日志记录到数据库中并用@ControllerAdvice处理错误页面响应:
AOP的方式记录异常日志
@Aspect
@Component
@Slf4j
public class LogAspect {
@AfterThrowing(throwing = "ex", pointcut = "execution(* com.github.izhangzhihao.SpringMVCSeedProject.*.*.*(..)))")
public void LogToDB(JoinPoint joinPoint, Throwable ex) {
//出错行
int lineNumber = ex.getStackTrace()[0].getLineNumber();
//方法签名
Signature signature = joinPoint.getSignature();
//参数
Object[] args = joinPoint.getArgs();
//拼接参数
StringBuilder argString = new StringBuilder();
if (args.length > 0)
for (Object arg : args)
argString.append(arg);
log.error("方法" + signature, "参数" + argString, "错误行:" + lineNumber, "时间" + new Date(), "异常内容" + ex.toString());
}
}
@ControllerAdvice处理错误页面响应
@ControllerAdvice
public class HandlerExceptionController {
/**
* 无权限访问跳转
*/
@ExceptionHandler({UnauthorizedException.class})
@ResponseStatus(HttpStatus.FORBIDDEN)
public ModelAndView handlerUnauthenticatedException() {
return new ModelAndView("../../403");
}
/**
* 全局Controller异常处理
* @param ex 异常
* @return 跳转出错页面
*/
@ExceptionHandler({Exception.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView handlerExceptionMethod(Exception ex) {
//将错误信息记录到数据库
LogToDB(ex);
ModelAndView modelAndView = new ModelAndView("../../500");
modelAndView.addObject("MSG", ex.toString());
modelAndView.addObject("Line", ex.getStackTrace()[0].getLineNumber());
modelAndView.addObject("Method", ex.getStackTrace()[0].getMethodName());
Writer writer = new StringWriter();
//客户端输出一下,打开F12可以看到
ex.printStackTrace(new PrintWriter(writer));
modelAndView.addObject("detailed", writer.toString());
return modelAndView;
}
}
这样依赖几乎不需要手动处理异常,代码变得干净,可以专一的处理业务逻辑。
Original link:https://izhangzhihao.github.io//2016/08/22/异常处理实践/