执子之手

与子偕老


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索
close

迁移到SpringBoot 02 - 异常处理

时间: 2018-05-15   |   分类: 开发     |   阅读: 1616 字 ~4分钟   |   访问: 0

首先说一下背景信息。云认证平台是一个传统的Spring项目,从上到下可以分为:API层(对应Spring Controller)、Service层、DAO层(MyBatis)几层。为了方便处理,从开发规范上我们要求了Service层的异常在Service层自行处理。

为了实现这一点,在接口定义上,从API层调用Service层的请求会有统一的返回基类:CommonResult。其定义如下:

 1public class CommonResult implements Serializable {
 2    private static final long serialVersionUID = 1L;
 3
 4    /**
 5     * success 请求成功与否的标志。
 6     */
 7    protected boolean success = Boolean.FALSE;
 8    /**
 9     * resultCode 返回码。
10     */
11    protected String resultCode = "";
12    /**
13     * message 详细信息。
14     */
15    protected String message = "";
16    ...
17}

所有需要从API层调用到的Service层的返回值都是该类或者该类的子类。如果是Service层之间互相调用,则不受此限制。

本章的内容主要与AOP有关。

1. 传统Spring中的处理

基本的背景介绍完毕。为了在Service层处理异常,实现了一个ServiceExceptionHandler,专门拦截异常,并根据异常类型,设置返回值中的相应字段;然后通过AOP功能将功能织入整个流程。

1.1 ServiceExceptionHandler

 1public class ServiceExceptionHandler {
 2	private static Logger logger = LoggerFactory.getLogger(ServiceExceptionHandler.class);
 3
 4	/**
 5	 * 异常处理。使用aop:around进行拦截,当方法执行过程中出错的时候可以根据异常类型生成返回值。
 6	 *
 7	 * @param joinPoint a {@link org.aspectj.lang.ProceedingJoinPoint} object.
 8	 * @throws java.lang.ClassNotFoundException if any.
 9	 * @throws java.lang.IllegalAccessException if any.
10	 * @throws java.lang.InstantiationException if any.
11	 * @return a {@link java.lang.Object} object.
12	 */
13	public Object processAndCatchException(ProceedingJoinPoint joinPoint) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
14		Object result = null;
15		Signature signature =  joinPoint.getSignature(); 
16		@SuppressWarnings("rawtypes")
17		Class returnType = ((MethodSignature) signature).getReturnType(); 
18		try {
19			result = joinPoint.proceed();
20		} catch (DataAccessException e) {
21			logger.error("Database exception occured: ", e);
22			result = prepareResult(returnType, ResultCode.DATABASE_ERROR);
23		}
24		catch (Exception e) {
25			logger.error("Unknow exception occured:", e);
26			result = prepareResult(returnType, ResultCode.UNKNOW_ERROR);
27		} catch (Throwable e) {
28			logger.error("Unknown throwable occured:", e);
29			result = prepareResult(returnType, ResultCode.UNKNOW_ERROR);
30		}
31		return result;
32	}
33	
34	/**
35	 * 准备返回值
36	 * @param returnType
37	 * @param errorCode
38	 * @return
39	 */
40	private Object prepareResult(@SuppressWarnings("rawtypes") Class returnType, ResultCode errorCode) {
41		Object result = null;
42		try {
43			result = Class.forName(returnType.getName()).newInstance();
44			if (!(result instanceof CommonResult)) {
45				logger.error("Return type is not subclass of CommonResult, please contact developer!!!");
46				return null;
47			}
48			CommonResult ret = (CommonResult)result;
49			ret.setResultCode(errorCode.getCode());
50			ret.setMessage(errorCode.getDesc());
51			ret.setSuccess(false);
52		} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
53			logger.error("prepareResult failed. ", e);
54		}
55		return result;
56	}
57}

1.2 AOP配置

然后通过Spring的AOP配置,将异常处理织入处理过程中:

 1	<!-- Service Exception Handler -->
 2  	<bean id="serviceExceptionHandlerAspect" class="com.eveus.cloudauth.service.exception.ServiceExceptionHandler" />
 3    <aop:config>
 4        <aop:aspect ref="serviceExceptionHandlerAspect" order="1">
 5            <aop:pointcut id="capServicePointcut" expression="execution(com.eveus.cloudauth.service.bean..* com.eveus.cloudauth.service.impl.*ServiceImpl..*(..))" />
 6            <aop:around pointcut-ref="capServicePointcut" method="processAndCatchException"/>
 7            <aop:pointcut id="uidServicePointCut" expression="execution(com.eveus.cloudauth.service.bean..* com.eveus.cloudauth.service.impl.uid.*ServiceImpl..*(..))" />
 8            <aop:around pointcut-ref="uidServicePointCut" method="processAndCatchException"/>
 9        </aop:aspect>
10    </aop:config>

2. SpringBoot中的处理

在SpringBoot中推荐的是基于Java的配置,不再推荐采用XML配置。因此需要稍微改写一下配置方法:

2.1 Bean配置

1@Configuration
2public class ServiceConfig {
3    // 生成ServiceExceptionHandler实例
4    @Bean(name="serviceExceptionHandler")
5    public ServiceExceptionHandler serviceExceptionHandler() throws  Exception {
6        return new ServiceExceptionHandler();
7    }
8}

Bean配置比较简单,在一个@Configuration注解的类中生成返回一个类对象即可。

2.2 AOP配置

AOP相关的配置大部分可以通过注解完成,例如:aop:aspect使用@Aspect替代;aop:pointcut使用@Pointcut代替;aop:around使用@Around代替。这些注解可以直接写入ServiceExceptionHandler类。

经过修改,改写完的ServiceExceptionHandler如下:

 1@Aspect
 2@Order(1)	// 优先级数字越小优先级越高。优先级越高越先执行。执行堆栈如下:高-低-serviceProcess-低-高。
 3public class ServiceExceptionHandler {
 4	private static Logger logger = LoggerFactory.getLogger(ServiceExceptionHandler.class);
 5
 6	// 拦截点定义。只拦截返回值为 ServiceResult 的方法。
 7	@Pointcut("execution(com.eveus.cloudauth.service.bean..* com.eveus.cloudauth.service.impl.*ServiceImpl..*(..))")
 8	public void capServiceProcess() {};
 9
10	@Pointcut("execution(com.eveus.cloudauth.service.bean..* com.eveus.cloudauth.service.impl.uid.*ServiceImpl..*(..))")
11	public void uidServiceProcess() {};
12
13
14	/**
15	 * 异常处理。使用aop:around进行拦截,当方法执行过程中出错的时候可以根据异常类型生成返回值。
16	 *
17	 * @param joinPoint a {@link org.aspectj.lang.ProceedingJoinPoint} object.
18	 * @throws java.lang.ClassNotFoundException if any.
19	 * @throws java.lang.IllegalAccessException if any.
20	 * @throws java.lang.InstantiationException if any.
21	 * @return a {@link java.lang.Object} object.
22	 */
23	@Around("capServiceProcess() || uidServiceProcess()")
24	public Object processAndCatchException(ProceedingJoinPoint joinPoint) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
25		Object result = null;
26		Signature signature =  joinPoint.getSignature(); 
27		@SuppressWarnings("rawtypes")
28		Class returnType = ((MethodSignature) signature).getReturnType(); 
29		try {
30			result = joinPoint.proceed();
31		} catch (DataAccessException e) {
32			logger.error("Database exception occured: ", e);
33			result = prepareResult(returnType, ResultCode.DATABASE_ERROR);
34		}
35		catch (Exception e) {
36			logger.error("Unknow exception occured:", e);
37			result = prepareResult(returnType, ResultCode.UNKNOW_ERROR);
38		} catch (Throwable e) {
39			logger.error("Unknown throwable occured:", e);
40			result = prepareResult(returnType, ResultCode.UNKNOW_ERROR);
41		}
42		return result;
43	}
44	
45	/**
46	 * 准备返回值
47	 * @param returnType
48	 * @param errorCode
49	 * @return
50	 */
51	private Object prepareResult(@SuppressWarnings("rawtypes") Class returnType, ResultCode errorCode) {
52		Object result = null;
53		try {
54			result = Class.forName(returnType.getName()).newInstance();
55			if (!(result instanceof CommonResult)) {
56				logger.error("Return type is not subclass of CommonResult, please contact developer!!!");
57				return null;
58			}
59			CommonResult ret = (CommonResult)result;
60			ret.setResultCode(errorCode.getCode());
61			ret.setMessage(errorCode.getDesc());
62			ret.setSuccess(false);
63		} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
64			logger.error("prepareResult failed. ", e);
65		}
66		return result;
67	}
68}

注意一下其中几个注解的使用方法。更多详细的内容可以参考:Spring Framework Core: AOP。

附录、参考资料

  • Spring Framework Core: AOP
#Spring# #Springboot# #AOP#
迁移到SpringBoot 03 - Servlet和Filter
迁移到SpringBoot 01 - 插件加载
  • 文章目录
  • 站点概览
Orchidflower

Orchidflower

Do one thing at a time, and do well.

77 日志
6 分类
84 标签
GitHub 知乎 OSC 豆瓣
  • 1. 传统Spring中的处理
    • 1.1 ServiceExceptionHandler
    • 1.2 AOP配置
  • 2. SpringBoot中的处理
    • 2.1 Bean配置
    • 2.2 AOP配置
  • 附录、参考资料
© 2009 - 2024 执子之手
Powered by - Hugo v0.113.0
Theme by - NexT
ICP - 鲁ICP备17006463号-1
0%