背景
这篇文章介绍的异常处理思路不错,本文试图给出一种具体实现,当然可能和作者的思路有所不同。
框架地址:https://github.com/happyframework/HappyFramework。
框架介绍
关于异常的一些想法:
- 异常不能跨越“边界类”。
- 在边界类之下,异步不能被“吞掉”。
- 系统在不同场景或分层中,需要的不同的处理“策略”。
- 每种策略都是一个可扩展的“管道”。
- 可以和 AOP 进行集成。
- 异常可以用来给业务用户提供“提醒”。
- 异常可以给运维用户提供“日志”。
我希望异常处理框架以某种机制支持上面的各种想法。
针对边界类的处理场景
模拟的边界类
1 package com.happyframework.exception.handling; 2 3 import org.springframework.stereotype.Controller; 4 5 import com.happyframework.exception.handling.springframework.HandleException; 6 7 @Controller 8 public class TestController { 9 10 @HandleException("UI") 11 public void test() { 12 throw new RuntimeException("业务失败"); 13 } 14 15 }
因为边界类需要特殊的异常处理策略,这里指定了的策略名字为:“UI”。
“UI”策略对应的管道配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:mvc="http://www.springframework.org/schema/mvc" 7 xmlns:aop="http://www.springframework.org/schema/aop" 8 xmlns:util="http://www.springframework.org/schema/util" 9 xsi:schemaLocation="http://www.springframework.org/schema/beans 10 http://www.springframework.org/schema/beans/spring-beans.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context.xsd 13 http://www.springframework.org/schema/mvc 14 http://www.springframework.org/schema/mvc/spring-mvc.xsd 15 http://www.springframework.org/schema/aop 16 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 17 http://www.springframework.org/schema/util 18 http://www.springframework.org/schema/util/spring-util-3.0.xsd"> 19 20 <context:component-scan base-package="com.happyframework.exception.handling.springframework" /> 21 <context:component-scan base-package="com.happyframework.exception.handling" /> 22 23 <aop:aspectj-autoproxy/> 24 25 <bean class="com.happyframework.exception.handling.springframework.ExceptionHandlerDefination"> 26 <property name="policy" value="UI"/> 27 <property name="handler"> 28 <bean class="com.happyframework.exception.handling.ConsoleLoggingHandler"/> 29 </property> 30 </bean> 31 <bean class="com.happyframework.exception.handling.springframework.ExceptionHandlerDefination"> 32 <property name="policy" value="UI"/> 33 <property name="handler"> 34 <bean class="com.happyframework.exception.handling.ConsoleNotificationHandler"/> 35 </property> 36 </bean> 37 </beans> 38
为管道定义了两个处理器:日志处理器和提醒处理器,因为是测试的处理器,实现就比较简单,真实项目中可以决定:哪些异常需要日志?哪些信息提醒给用户?
运行输出
1 记录日志:java.lang.RuntimeException: 业务失败 2 提示消息:java.lang.RuntimeException: 业务失败
“UI”策略导致异常被吞掉了,这也是我们期望的行为,后面会介绍如何在处理器中吞掉异常。
核心 API 介绍
主要类型
代码可以去 https://github.com/happyframework/HappyFramework 下载。
重点说一下 ExceptionHandlerChain
1 package com.happyframework.exception.handling; 2 3 public interface ExceptionHandlerChain { 4 5 Throwable getException(); 6 7 void setNewException(final Throwable newException); 8 9 boolean isExceptionHandled(); 10 11 void setExceptionHandled(final boolean exceptionHandled); 12 13 void proceed(); 14 15 }
ExceptionHandlerChain 是作为参数传递给 ExceptionHandler:
1 package com.happyframework.exception.handling; 2 3 public interface ExceptionHandler { 4 5 void handle(final ExceptionHandlerChain chain); 6 7 }
ExceptionHandlerChain 的成员的主要意图是:
- Throwable getException():返回正在处理的异常。
- void setNewException(final Throwable newException):替换或包装异常的时候调用此方法。
- boolean isExceptionHandled():异常是否被处理过。
- void setExceptionHandled(final boolean exceptionHandled):吞掉异常的时候调用此方法。
- void proceed():执行下一个处理器。
一个简单的异常处理器
1 package com.happyframework.exception.handling; 2 3 final class ConsoleNotificationHandler implements ExceptionHandler { 4 5 @Override 6 public final void handle(final ExceptionHandlerChain chain) { 7 System.out.println("提示消息:" + chain.getException()); 8 9 chain.setExceptionHandled(true); 10 chain.proceed(); 11 } 12 13 }
备注
第一次用 Java 写东西,感觉挺不错的。