程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 在Spring基礎上實現自己的異常處理框架

在Spring基礎上實現自己的異常處理框架

編輯:關於JAVA

該異常處理框架滿足的要求:

完整的異常組織結構

異常的統一處理

可配置,受管式,方便使用

完整的異常組織結構:

用戶可以方便的定義自己的異常,但所有UncheckedException需要繼承BaseAppRuntimeException,所有的checked Exception可以繼承BaseAppException,或者需要拋出且不需要check時用WrapperredAppException封裝後拋出

合理地使用checked異常

Exception有唯一的error code,這樣用戶報告異常後,可以根據異常號找到相應Exception,把exception直接顯示給用戶也沒有太大的意義,如何紀錄exception那就是下文講到的ExceptionHandler的職責了。

如果是第三方包括jdk中的異常,需要封裝成BaseAppException或者BaseAppRuntimeException後拋出

統一的異常處理

異常統一在框架中進行處理,不需要在上層應用的代碼中去處理拋出的異常。為了盡量捕捉到所有的異常,將異常處理放在了ActionBroker中,這樣凡是action以後拋出的異常都可以捕捉到,因為webservice只是簡單的調用action類的方法,一般不會出現異常。當我們捕捉到異常後,需要進行異常處理,定義了ExceptionHandler接口,用接口抽象出異常處理類的具體實現。

USFContextFactory: 創建ExceptionContext的工廠

package com.ldd600.exception.context;
2
3public class CoreContextFactory {
4  private static CoreContextFactory instance;
5
6  private volatile ExceptionContext exceptionContext;
7
8  private Object exceptionContextLock = new Object();
9
10  private CoreContextFactory() {
11
12  }
13
14  public static synchronized CoreContextFactory getInstance() {
15    if (null == instance) {
16      instance = new CoreContextFactory();
17    }
18    return instance;
19  }
20
21  public ExceptionContext getExceptionContext() {
22    ExceptionContext tempExpContext = exceptionContext;
23    if (tempExpContext == null) {
24      synchronized (exceptionContextLock) {
25        tempExpContext = exceptionContext;
26        if (tempExpContext == null)
27          exceptionContext = tempExpContext = new ExceptionContext();
28      }
29    }
30    return tempExpContext;
31  }
32}
33

ExceptionContext: 存放全局的exception信息

1package com.ldd600.exception.context;
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Collections;
6import java.util.HashMap;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10
11import org.springframework.util.Assert;
12
13import com.ldd600.exception.base.BaseAppRuntimeException;
14import com.ldd600.exception.base.ConfigException;
15import com.ldd600.exception.base.handler.ExceptionHandler;
16import com.ldd600.exception.config.ExceptionDefinition;
17
18public class ExceptionContext {
19  private Map<Class<?>, ExceptionDefinition> exceptionMap;
20
21  private Map<String, ExceptionHandler> handlers = new HashMap<String, ExceptionHandler>();
22
23  ExceptionContext() {
24    exceptionMap = new HashMap<Class<?>, ExceptionDefinition>();
25  }
26
27  public boolean containsException(Class<?> expClazz) {
28    return (exceptionMap.containsKey(expClazz));
29  }
30  
31  public void addExceptionHander(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazz) {
32    try {
33      ExceptionDefinition definition = getRealExceptionDefinition(expClazz);
34      if (null == definition) {
35        throw new IllegalArgumentException(expClazz.getName() + "not in the context, please configure or add it to the context first!!");
36      }
37      ExceptionHandler handler = handlers.get(handlerClazz.getName());
38      if (null == handler) {
39        handler = handlerClazz.newInstance();
40        handlers.put(handlerClazz.getName(), handler);
41      }
42      
43      definition.getHandlerNames().add(handlerClazz.getName());
44    } catch (Exception ex) {
45      throw new ConfigException("Add exception handler to context failure!", ex);
46    }
47  }
48  
49  public void addExceptionHandler(Class<?> expClazz, String errorCode, Class<? extends ExceptionHandler> handlerClazz) {
50    Assert.hasLength(errorCode, expClazz + " errorCode must not be null or empty string!");
51    ExceptionDefinition definition = getRealExceptionDefinition(expClazz);
52    if(null == definition) {
53      definition = new ExceptionDefinition(errorCode);
54      exceptionMap.put(expClazz, definition);
55    }
56    addExceptionHander(expClazz, handlerClazz);
57  }
58  
59  
60  
61  public void addExceptionHandlers(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazzes) {
62    for(Class<? extends ExceptionHandler> handlerClazz : handlerClazzes) {
63      addExceptionHander(expClazz, handlerClazz);
64    }
65  }
66
67  public void removeExceptionHandler(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazz) {
68    Assert.isTrue(containsException(expClazz));
69    String handlerName = handlerClazz.getName();
70    getExceptionDefinition(expClazz).getHandlerNames().remove(handlerName);
71    Collection<ExceptionDefinition> definitons = exceptionMap.values();
72    boolean isClearHandler = true;
73    for (ExceptionDefinition expDefinition : definitons) {
74      if (expDefinition.getHandlerNames().contains(handlerName)) {
75        isClearHandler = false;
76        break;
77      }
78    }
79
80    if (isClearHandler) {
81      handlers.remove(handlers.get(handlerName));
82    }
83  }
84
85  public void setExceptionDefinition(Class<?> expClazz, ExceptionDefinition definition) {
86    exceptionMap.put(expClazz, definition);
87  }
88
89  public ExceptionDefinition getExceptionDefinition(Class<?> expClazz) {
90    if (containsException(expClazz)) {
91      return exceptionMap.get(expClazz); 
92    } else if (BaseAppRuntimeException.class.isAssignableFrom(expClazz.getSuperclass())) {
93      return getExceptionDefinition(expClazz.getSuperclass());
94    } else {
95      return null;
96    }
97  }
98  
99  public ExceptionDefinition getRealExceptionDefinition(Class<?> expClazz) {
100    return exceptionMap.get(expClazz);
101  }
102
103  public List<ExceptionHandler> getExceptionHandlers(Class<?> expClazz){
104    ExceptionDefinition definition = getExceptionDefinition(expClazz);
105    if (null != definition) {
106      Set<String> handlerNames = definition.getHandlerNames();
107      List<ExceptionHandler> handlerList = new ArrayList<ExceptionHandler>(handlerNames.size());
108      for (String handlerName : handlerNames) {
109        ExceptionHandler handler = handlers.get(handlerName);
110        handlerList.add(handler);
111      }
112      List<ExceptionHandler> resultHandlerList = new ArrayList<ExceptionHandler>(handlerList);
113      return resultHandlerList;
114    } else {
115      return Collections.<ExceptionHandler> emptyList();
116    }
117  }
118  
119  public String getErrorCode(Class<?> expClazz){
120    ExceptionDefinition definition = getExceptionDefinition(expClazz);
121    if (null != definition) {
122      return definition.getErrorCode();
123    } else {
124      return "";
125    }
126  }
127  
128  
129}
130

ExceptionDefinition: Exception信息單元

1package com.ldd600.exception.config;
2
3import java.util.LinkedHashSet;
4import java.util.Set;
5
6public class ExceptionDefinition {
7  private String errorCode;
8
9  private Set<String> handlerNames = new LinkedHashSet<String> ();
10
11  ExceptionDefinition() {
12    
13  }
14  
15  public ExceptionDefinition(String errorCode) {
16    this.errorCode = errorCode;
17  }
18  
19  public String getErrorCode() {
20    return errorCode;
21  }
22
23  public void setErrorCode(String errorCode) {
24    this.errorCode = errorCode;
25  }
26
27  public Set<String> getHandlerNames() {
28    return handlerNames;
29  }
30}
31

ExceptionDefiniton定義了和某個exception相關的具體信息,根據exception的class name可以從exceptionContext中的exceptionMap得到指定的exception的相關信息,這些信息是在系統初始化時讀取到exceptionContext中的。並且避免了exception handler的重復初始化。

可配置,受管式,方便使用

采取兩種配置方式,exception的相關信息比如它的errorCode, exceptionHandlers可以配置在外部的xml文件中,也可以用annotation標注。對於exception的處理是有繼承性質的,如果某個exception沒有在exceptionContext中注冊,就使用它的父類的配置信息。如果無任何父類在exceptionContext中注冊,就使用默認機制進行處理。

XML 方案:

因為spring2.0支持自定義schema功能,我們可以方便地采用自己的schema只要實現NamespaceHandler和BeanDefinitionPaser,後面一個比較重要,可以將自定義xml文件中的相關類注冊到spring的上下文中,成為spring bean。

Xml schema:

<xsd:complexType name="exceptionType">
<xsd:sequence>
<xsd:element name="level" default="error" minOccurs="0">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="error" />
<xsd:enumeration value="warning" />
<xsd:enumeration value="info" />
<xsd:enumeration value="confirmation" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="handler" maxOccurs="unbounded">
<xsd:simpleType>
<xsd:restriction base="xsd:string" />
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="errorCode">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:whiteSpace value="preserve" />
<xsd:pattern value="LDD600-+\d{1,5}.*" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string" use="required" />
</xsd:complexType>

Annotation方案:

JDK1.5以上就有了annotation,可以簡化我們的配置,使得配置信息和代碼聯系在一起,增加了代碼的可讀性。如何在spring中注冊自定義的annotation和用annotation標注的class,可以參考文章2和文章:  。對於每個注冊了的class用ExceptionalAnnotationBeanPostProcessor來parse具體的annotation信息(對於annotation的parse方法還會在以後繼續改進)。

1package com.ldd600.exception.annotation;
2
3import java.lang.annotation.Documented;
4import java.lang.annotation.ElementType;
5import java.lang.annotation.Retention;
6import java.lang.annotation.RetentionPolicy;
7import java.lang.annotation.Target;
8
9import com.ldd600.exception.base.handler.ExceptionHandler;
10
11@Target({ElementType.TYPE})
12@Retention(RetentionPolicy.RUNTIME)
13@Documented
14public @interface Exceptional {
15  String errorCode();
16  Class<? extends ExceptionHandler>[] handlers();
17}
18

1package com.ldd600.exception.processor;
2
3import org.springframework.beans.BeansException;
4import org.springframework.beans.factory.config.BeanPostProcessor;
5
6import com.ldd600.exception.annotation.Exceptional;
7import com.ldd600.exception.base.BaseAppException;
8import com.ldd600.exception.base.BaseAppRuntimeException;
9import com.ldd600.exception.config.ExceptionDefinition;
10import com.ldd600.exception.context.ExceptionContext;
11import com.ldd600.exception.context.CoreContextFactory;
12
13public class ExceptionalAnnotationBeanPostProcessor implements BeanPostProcessor {
14
15  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
16    if(bean instanceof BaseAppRuntimeException || bean instanceof BaseAppException) {
17      Exceptional exceptional = bean.getClass().getAnnotation(Exceptional.class);
18      if(null != exceptional) {
19        ExceptionContext ctx = CoreContextFactory.getInstance().getExceptionContext();
20        if(!ctx.containsException(bean.getClass())) {
21          ExceptionDefinition expDefinition = new ExceptionDefinition(exceptional.errorCode());
22          ctx.setExceptionDefinition(bean.getClass(), expDefinition);
23        }
24        ctx.addExceptionHandlers(bean.getClass(), exceptional.handlers());
25        return null;
26      }
27    }
28    return bean;
29  }
30
31  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
32      return bean;
33  }
34
35}
36

結果測試:

1package com.ldd600.exception.test;
2
3import org.jmock.Expectations;
4import org.jmock.Mockery;
5import org.springframework.beans.factory.BeanFactory;
6
7import com.ldd600.exception.action.BusinessAction;
8import com.ldd600.exception.base.BaseAppException;
9import com.ldd600.exception.base.BaseAppRuntimeException;
10import com.ldd600.exception.base.ConfigException;
11import com.ldd600.exception.base.handler.ConsoleHandler;
12import com.ldd600.exception.context.CoreContextFactory;
13import com.ldd600.exception.dto.DefaultRequest;
14import com.ldd600.exception.dto.DefaultResponse;
15import com.ldd600.exception.dto.Request;
16import com.ldd600.exception.dto.Response;
17import com.ldd600.exception.webservice.ActionBrokerImpl;
18
19public class ExceptionTest extends DependencyInjectionExceptionTestCase {
20  Mockery context = new Mockery();
21  ActionBrokerImpl broker = new ActionBrokerImpl();
22  final Request request = new DefaultRequest();
23  final Response response = new DefaultResponse();
24
25  @Override
26  protected String[] getConfigLocations() {
27    return new String[] { "applicationContext.xml" };
28  }
29
30  public void testExceptionThrow() {
31    final BusinessAction<Response, Request> action = context
32        .mock(BusinessAction.class);
33    final BeanFactory beanFactory = context.mock(BeanFactory.class);
34    assertThrowing(new Closure() {
35      public void run() throws Throwable {
36        context.checking(new Expectations() {
37          {
38            allowing(beanFactory).getBean("action");
39            will(returnValue(action));
40            one(action).execute(request, response);
41            will(throwException(new BaseAppException()));
42          }
43        });
44        broker.setExceptionHandler(new ConsoleHandler());
45        broker.setBeanFactory(beanFactory);
46        broker.execute("action", request, response);
47      }
48
49    }, BaseAppException.class);
50  }
51
52  public void testExceptionalAutoLoad() throws BaseAppException {
53    final BeanFactory beanFactory = context.mock(BeanFactory.class);
54    final BusinessAction<Response, Request> action = context
55        .mock(BusinessAction.class);
56    context.checking(new Expectations() {
57      {
58        allowing(beanFactory).getBean("action");
59        will(returnValue(action));
60        one(action).execute(request, response);
61        will(throwException(new ConfigException()));
62      }
63    });
64    broker.setBeanFactory(beanFactory);
65    broker.execute("action", request, response);
66    assertEquals(CoreContextFactory.getInstance().getExceptionContext()
67        .getErrorCode(ConfigException.class), "LDD600-00002");
68    context.assertIsSatisfied();
69  }
70
71  public void testRuntimeException() {
72    final BusinessAction<Response, Request> action = context
73        .mock(BusinessAction.class);
74    final BeanFactory beanFactory = context.mock(BeanFactory.class);
75    assertThrowing(new Closure() {
76      public void run() throws Throwable {
77        context.checking(new Expectations() {
78          {
79            allowing(beanFactory).getBean("action");
80            will(returnValue(action));
81            one(action).execute(request, response);
82            will(throwException(new BaseAppRuntimeException()));
83          }
84        });
85        broker.setExceptionHandler(new ConsoleHandler());
86        broker.setBeanFactory(beanFactory);
87        broker.execute("action", request, response);
88      }
89
90    }, BaseAppRuntimeException.class);
91    // test config
92    assertEquals(CoreContextFactory.getInstance().getExceptionContext()
93        .getErrorCode(BaseAppRuntimeException.class), "LDD600-00001");
94    // test handler
95    assertFalse(response.isSuccess());
96    assertEquals(response.getErrorCode(), CoreContextFactory.getInstance()
97        .getExceptionContext().getErrorCode(
98            BaseAppRuntimeException.class));
99    context.assertIsSatisfied();
100  }
101
102  public void testCheckedException() {
103    final BusinessAction<Response, Request> action = context
104        .mock(BusinessAction.class);
105    final BeanFactory beanFactory = context.mock(BeanFactory.class);
106    assertThrowing(new Closure() {
107      public void run() throws Throwable {
108        context.checking(new Expectations() {
109          {
110            allowing(beanFactory).getBean("action");
111            will(returnValue(action));
112            one(action).execute(request, response);
113            will(throwException(new ExceptionFaker()));
114          }
115        });
116        broker.setBeanFactory(beanFactory);
117        broker.execute("action", request, response);
118      }
119
120    }, ExceptionFaker.class);
121    // test config
122    assertEquals(CoreContextFactory.getInstance().getExceptionContext()
123        .getErrorCode(ExceptionFaker.class), "LDD600-00003");
124    // test handler
125    assertFalse(response.isSuccess());
126    assertEquals(response.getErrorCode(), CoreContextFactory.getInstance()
127        .getExceptionContext().getErrorCode(
128            ExceptionFaker.class));
129    context.assertIsSatisfied();
130  }
131}
132

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved