问题
我们在捕获到异常并对异常进行处理时可能会遇到如下一些问题:
- 不确定应如何处理这些异常
- 需要记录异常日志时没有记录,或者异常在不同的地方重复记录,使得排错调试不方便
- 处理日志时,需要在每一个try-catch块包含一些处理代码,有时候异常处理的代码比正常执行代码还多,污染正常执行代码。
- 同样类型异常有不同的处理方式,使最终的处理变得复杂。
- 接口抛出异常,破坏封装,打破了接口与实现之间的清晰界限。
- 异常处理代码散落在,修改起来时非常麻烦。无法对某些异常进行统一处理和修改。
有必要使用一个统一的异常处理机制 来进行某些异常处理的统一决策。比如对异常进行统一的日志记录,对散落在各处的某类异常进行统一处理等。
目标
-
保障数据前后一致性,包括与外系统数据的一致性
-
将业务代码与补偿代码解耦
-
补偿框架易用性
解决办法
使用Spring AOP对serivce层抛出的异常进行拦截,记录所有未处理的异常日志,并将所有未处理异常转换成统一自定义的系统异常,以便前端能够处理。下面的代码举例说明了如何使用Spring AOP将异常处理从正常的逻辑中分离出来,使得异常处理逻辑和正常的业务逻辑做到完全解耦。不同项目对于异常处理的要求不同,在具体项目中需要对异常处理代码根据项目需要进行修改,加以完善。
模块分解
异常捕获
-
AOP配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!--设定自动代理--> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <!--负责记录有异常发生时的信息--> <bean id="exceptionHandler" class="com.za.exceptionhandler.demo.exception.aop.ExceptionHandler"></bean> <bean id="exceptionHandlerAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref bean="exceptionHandler"/> </property> <property name="patterns"> <value>.*.*.impl.*</value> </property> </bean> </beans>
-
异常处理类
package com.za.exceptionhandler.demo.exception.aop; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.aop.ThrowsAdvice; import com.za.exceptionhandler.demo.util.SpringContextUtil; import com.za.exceptionhandler.router.support.Compensation; import com.za.exceptionhandler.router.support.CompensationService; public class ExceptionHandler implements ThrowsAdvice { private Logger logger = Logger.getLogger(this.getClass().getName()); public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable { logger.log(Level.INFO, args[0] + " 执行 " + method.getName() + "时有异常抛出..." + ex); method=target.getClass().getMethod(method.getName(), method.getParameterTypes()); if (method.isAnnotationPresent(Compensation.class)) { Compensation compensation = method.getAnnotation(Compensation.class); CompensationService compensationService = (CompensationService) SpringContextUtil.getBean(compensation.beanName()); compensationService.process(compensation.beanName(), method, args, target, ex); } } }
补偿路由
-
补