有人说java中最恶心是异常处理,不管是以前很大的服务单元(Monolith)开发架构,还是最近很流行的微服务架构开发。为了代码的清晰度和可读性等,都会在项目内部做代码的内部分层,比如controller层, service层和dao层。其中每层都有要实现的业务及对应的方法,且每个方法都有对应的返回值。
在java代码中,实现业务的同时还要处理对应的异常,会导致代码不简洁,可读性非常差,接下来举个例子来作说明:
业务场景: demo的service负责业务实现并避免不了要抛出异常 DemoException。
demo的controller则是负责对外输出web项目的服务。
DemoService.java:
package com.simonton.demo.service.impl;
import org.springframework.stereotype.Service;
import com.simonton.demo.common.ResponseCode;
import com.simonton.demo.exception.DemoException;
import com.simonton.demo.service.IDemoService;
@Service
public class DemoService implements IDemoService {
@Override
public String doDemo(){
boolean mockCondition = true;
doBusiness();
if (mockCondition) {
throw new DemoException(ResponseCode.PARAMETER_ILLEGAL);
}
return "demo response...";
}
@Override
public String demo() {
boolean mockCondition = true;
doBusiness();
if (mockCondition) {
throw new DemoException(ResponseCode.PARAMETER_ILLEGAL);
}
return "demo response...";
}
@Override
public String doSth() {
boolean mockCondition = true;
doBusiness();
if (mockCondition) {
throw new DemoException(ResponseCode.PARAMETER_ILLEGAL);
}
return "demo response...";
}
public void doBusiness() {
System.out.println("do business...");
}
}
DemoController.java:
@RestController
public class DemoController {
@Autowired
private IDemoService demoService;
@RequestMapping("/demo/{requestParam}")
public String demo(String requestParam) {
return "demo done....";
}
@RequestMapping("/demo")
public String doSth() {
try {
return demoService.doSth();
} catch(DemoException ex) {
return JSON.toJSONString(new Response(ex.getCode().getCode(), ex.getCode().getMessage()));
}
}
@RequestMapping("/dodemo")
public String demo() {
try {
return demoService.demo();
} catch(DemoException ex) {
return JSON.toJSONString(new Response(ex.getCode().getCode(), ex.getCode().getMessage()));
}
}
}
从DemoCotroller的源码可以看出,为了对外提供web服务,controller的每个方法里都需要模版式处理异常问题,这极大的影响了代码简洁和可读性。
为了解决这类模版式的,冗余式的异常处理代码,我们来通过 spring AOP来优化它,方案分步:
1. 定义统一的异常:
DemoException.java:
package com.simonton.demo.exception;
import com.simonton.demo.common.ResponseCode;
public class DemoException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1950333732256835289L;
private ResponseCode code;
public DemoException() {}
public DemoException(ResponseCode code) {
this.code = code;
}
public DemoException(String message) {
super(message);
}
public ResponseCode getCode() {
return code;
}
public void setCode(ResponseCode code) {
this.code = code;
}
}
ResponseCode.java:
package com.simonton.demo.common;
public enum ResponseCode {
SUCCESS("0000","成功"),
FAILE("9999","失败"),
PARAMETER_ILLEGAL("0001","请求参数非法");
private String code;
private String message;
private ResponseCode(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2. 在业务方法中,抛异常时,抛出定义好的模版exception:
@Override
public String demo() {
boolean mockCondition = true;
doBusiness();
if (mockCondition) {
throw new DemoException(ResponseCode.PARAMETER_ILLEGAL);
}
return "demo response...";
}
3. 通过AOP来统一解决异常问题,以增强代码的简洁和可读性。
定义一个Around的annotation统一处理异常问题:
a). 定义一个 @annotation Web.java:
package com.simonton.demo.annotation;
public @interface Web {
}
b). @annotation Web的实现业务类 WebInterceptor.java:
package com.simonton.demo.interceptor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.simonton.demo.common.Response;
import com.simonton.demo.common.ResponseCode;
import com.simonton.demo.exception.DemoException;
@Aspect
@Component
public class WebInterceptor {
@Around(value="@annotation(com.simonton.demo.annotation.Web)")
public Object around(ProceedingJoinPoint joinPoint) {
ResponseCode code = null;
try {
return joinPoint.proceed();
} catch (DemoException e) {
code = e.getCode();
} catch (Throwable e) {
e.printStackTrace();
}
return JSON.toJSONString(new Response(code.getCode(), code.getMessage()));
}
}
c). 在controller需要用来处理异常的地方调用@Web
package com.simonton.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.simonton.demo.annotation.Web;
import com.simonton.demo.service.IDemoService;
@RestController
public class DemoController {
@Autowired
private IDemoService demoService;
@RequestMapping("/demo/{requestParam}")
public String demo(String requestParam) {
return "demo done....";
}
@Web
@RequestMapping("/web/demo")
public String demoWeb() {
return demoService.doDemo();
}
@Web
@RequestMapping("/demo")
public String doSth() {
return demoService.doSth();
}
@Web
@RequestMapping("/dodemo")
public String demo() {
return demoService.demo();
}
}
到此异常处理的优化就结束了。
最后总结一下:
1. 通过预先定义好异常模版(开发时可按需扩大异常模版),在业务实现类中,按模版抛出异常。
2. 实现一个around 的annotation(例子中的@Web),利用AOP统一处理处理异常。
3. 在项目的最外层(controller 层),在需要处理异常的方法上,配上该annotation。
@Web
@RequestMapping("/demo")
public String doSth() {
return demoService.doSth();
}
源码:
github:
https://github.com/simonton/exceptionHandler.git
码云gitee:
https://gitee.com/simonton/exceptionHandler.git
WebAnnotation 项目是本博客提到的源码
SpringMVC 项目是利用SpringMVC @ExceptionHandler的方式统一处理异常