异常处理经验小结之二:利用Spring AOP将check exception 转化为unchecked exception

本文探讨如何通过Spring AOP技术将检查异常转化为运行时异常,减少重复工作量和遗漏风险,提供一种高效异常处理方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于Exception的处理,究竟是采用check exception还是unchecked exception,众说纷纭,业绩很多大牛都有不同的意见,本文的关注点不在这个话题上,而是着重于,当我们程序中,需要把各种check exception转化为自定义的和业务逻辑相关的Runtime exceptionn的时候,如何利用Spring AOP来简化这一转化过程。

在实际工作中,如果我们也想像Spring Jdbctemplate一样,把check exception转化为Runtime exception的话,笨一点的办法就是在每次抛出check exception的地方都将其捕获,然后重新包装成Runtime exception,不过这样的话,不但重复工作量大,而且还容易产生遗漏,可谓事倍功半。那还要其它的什么办法能做到这一点呢?仔细分析下我们的我们面临的问题:即要在每个抛出check exception的方法里把异常包装成runtime exception重新抛出。这里有两个关键词很重要:每个。。。方法。。。,我们要在每个XX方法执行的时候都给其添加一项新的功能,这不正是AOP的用武之地。

因为Spring 提供了强大的AOP功能,我们就不需要自己去重复造轮子实现类似的功能,我翻了下Spring AOP部分的API,很快就找到ThrowsAdviceInterceptor这个类,这个类正好提供了我所需要的功能:

 

	public ThrowsAdviceInterceptor(Object throwsAdvice) {
		Assert.notNull(throwsAdvice, "Advice must not be null");
		this.throwsAdvice = throwsAdvice;

		Method[] methods = throwsAdvice.getClass().getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			if (method.getName().equals(AFTER_THROWING) &&
					//m.getReturnType() == null &&
					(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
					Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
				) {
				// Have an exception handler
				this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
				if (logger.isDebugEnabled()) {
					logger.debug("Found exception handler method: " + method);
				}
			}
		}
		
		if (this.exceptionHandlerMap.isEmpty()) {
			throw new IllegalArgumentException(
					"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
		}
	}
	
	public int getHandlerMethodCount() {
		return this.exceptionHandlerMap.size();
	}

	/**
	 * Determine the exception handle method. Can return null if not found.
	 * @param exception the exception thrown
	 * @return a handler for the given exception type
	 */
	private Method getExceptionHandler(Throwable exception) {
		Class exceptionClass = exception.getClass();
		if (logger.isTraceEnabled()) {
			logger.trace("Trying to find handler for exception of type [" + exceptionClass.getName() + "]");
		}
		Method handler = (Method) this.exceptionHandlerMap.get(exceptionClass);
		while (handler == null && !exceptionClass.equals(Throwable.class)) {
			exceptionClass = exceptionClass.getSuperclass();
			handler = (Method) this.exceptionHandlerMap.get(exceptionClass);
		}
		if (handler != null && logger.isDebugEnabled()) {
			logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler);
		}
		return handler;
	}

	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			return mi.proceed();
		}
		catch (Throwable ex) {
			Method handlerMethod = getExceptionHandler(ex);
			if (handlerMethod != null) {
				invokeHandlerMethod(mi, ex, handlerMethod);
			}
			throw ex;
		}
	}
	
	private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
		Object[] handlerArgs;
		if (method.getParameterTypes().length == 1) {
			handlerArgs = new Object[] { ex };
		}
		else {
			handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
		}
		try {
			method.invoke(this.throwsAdvice, handlerArgs);
		}
		catch (InvocationTargetException targetEx) {
			throw targetEx.getTargetException();
		}
	}

   这个类的源代码并不复杂,简而言之就是根据注入的throwsAdvice,来生产针对各种不同的exception的AOP处理方法。其中有一点需要注意的是,在这个throwsAdvice当中,各种exception的处理方法都要以afterThrowing开头。见下面的例子:

 

public class CDMExceptionHandler implements ThrowsAdvice {
	
	private static final int stackTraceLevel = 4;
	
	public void afterThrowingFileNotFound(Method m, Object[] args, Object target, 
			FileNotFoundException ex) throws Exception {
		//处理异常并包装成业务逻辑对应的Runtime exception,重新抛出
	}
	
	public void afterThrowing(Method m, Object[] args, Object target,
			HttpHostConnectException ex) throws Exception {
		//处理异常并包装成业务逻辑对应的Runtime exception,重新抛出
	}
}
 

 

如果把ExceptionHandler 的实例当做throwsAdvice注入进ThrowsAdviceInterceptor的话,那么ThrowsAdviceInterceptor里的exceptionHandlerMap就有两个entery,分别为:

 

Key=FileNotFoundException, value(method)=afterThrowingFileNotFound

key=HttpHostConnectionException,value(method)=afterThrowingHttpHost。

  在Invoke,ThrowsAdviceInterceptor根据捕获的异常从exceptionHandlerMethod里面get到相应的处理方法,进行处理。

 

通过对ThrowsAdviceInterceptor原理的简单分析,我们可以发现,其正好满足我们的需要,我们唯一要做的事情就是根据具体的应用,编写好自己的exceptionHandler即throwsAdvice,并将其注入进ThrowsAdviceInterceptor,然后把这个ThrowsAdviceInterceptor配置到我们的应用程序当中即可。至于AOP的具体配置,google下Spring AOP,将会有无数的文章教你如何配置,下面给出个简单的配置示例:

 

	<bean id ="throwInterceptor" class="org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor">
 		<constructor-arg ref="throwAdvice" />       
	</bean>
	
	<bean id ="throwAdvice" class="exceptionHandler.utils.ExceptionHandler">
	</bean>
		
	<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames">
			<list>
				<value>contentPutServiceImpl</value>									
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>throwInterceptor</value>
			</list>
		</property>
	</bean>

 

 BeanNameAutoProxyCreator来配置需要在应用程序的那些类中使用ThrowsAdviceInterceptor。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值