【架构点滴】项目代码的异常处理流程优化

      有人说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的方式统一处理异常

转载于:https://my.oschina.net/simonton/blog/1631961

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值