Jersey框架的同一異常處置機制剖析。本站提示廣大學習愛好者:(Jersey框架的同一異常處置機制剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是Jersey框架的同一異常處置機制剖析正文
1、配景
寫這邊文章源於有同伙問過java中的checked exception和unchecked exception有啥差別,其時我對其的答復是:我編程時僅用RuntimeException。其實,我說句話我是有條件的,確實的應當這麼說:在成熟的開辟框架下編寫營業代碼,我只應用或存眷RuntimeException。由於,因為框架常常將異常的處置同一封裝,如許以便法式員更好的存眷營業代碼,而營業的一些毛病平日是在體系運轉時代產生的,是以營業的異常平日被設計為RuntimeException的子類。
我的答復明顯不克不及讓同伙滿足!由於,不論是任何一個初學java的都曉得,在我們進修IO類和JDBC編程的時刻,我們用了年夜量的try...catch...,這類重復反復的try...catch會讓我們對java的異常記憶深入!初學者常常不清晰java的異常為何會設計成這個模樣,他們平日會對異常只停止簡略的處置——在catch塊外面簡略的把異常打印出來,用的最多的就是這個語句:
e.printStackTrace()。
我們還與一些記憶,好比數組越界這類的異常:
java.lang.ArrayIndexOutOfBoundsException: 6
這也會使我們浮光掠影,由於在我們調試法式的時刻,它常常湧現!我們會發明這類異常其實不須要在代碼裡用try...catch...去捕捉它。
下面兩個例子,其實就是同伙問到的checked exception和unchecked exception,須要try...catch...的異常是checked exception,不須要的則是unchecked exception。假如要說他們的差別,我說他們一個要try...catch...,一個不須要,如許的答復行嗎?我以為如許的答復是慘白的。有同窗會進一步說,try...catch很明顯,是強迫請求拋出異常的辦法挪用者顯式的處置異常,那e.printStackTrace()算不算處置了異常,我以為那只算是一種簡略懶散的處置方法吧!那甚麼樣的處置方法算是高超的,java說話設計者實際上是希冀產生異常後,挪用者可以或許在catch裡將異常恢復回來,使得法式可以或許持續履行下去。然則,“聰慧的法式員都是懶散的”呵呵,年夜多半情形下我們選擇異常湧現後只停止記載日記和UI用戶提醒,前面我會聯合jersey框架,說說個中的同一異常處置。讀到這裡,有人會說,那checked exception和unchecked exception異常的差別就是,一個須要處置,一個不須要處置。這個答復准確嗎?我以為是毛病的!我的不雅點是:不管是checked exception照樣unchecked exception,我們都要停止處置!
上一段,我們仿佛照樣沒有處理checked exception和unchecked exception的差別,我以為若何給出謎底其實不主要,主要的是我們怎樣行止理這些異常,和我們若何在開辟時應用異常。
我的不雅點是(Web體系開辟):
1、在框架層面封裝checked exception,將其轉化為unchecked exception,防止開辟進程中編寫繁雜的try...catch代碼。
2、營業層面的開辟,依據法式代碼職責界說分歧的RuntimeException(它就是unchecked exception,普通界說為RuntimeException的子類)
3、經由過程前兩個不雅點,體系中自界說的異常將只存在unchecked exception,體系只在於客戶端交流數據的下層,設置同一異常處置機制,並將一些異常轉化為用戶所能懂得的信息轉達給用戶。
4、其他如營業層,數據耐久層,等底層只擔任將異常拋出便可,但要留意不要喪失失落異常客棧(這一點是初學者輕易犯的一個毛病)。
配景說的夠長了!讓我們進入正題吧,看看Jersey框架的同一異常處置器是如何應用的!
2、jersey框架的同一異常處置機制
有以下商定:
1、示例采取jersey1.x版本
2、spring版本為2.5
3、為了簡略起見,示例項目不采取Maven機制
示例的營業場景解釋:
1、我們經由過程讀取一個properties設置裝備擺設文件,設置裝備擺設文件的內容為:
key1=hello key2=iteye.com
2、提議一個http://localhost:8888/a/resources/test?n=11的GET要求,請求n為數字,且必需小於10,假如n毛病,將發生一個unchecked exception毛病。
3、本示例中數據拜訪層將讀取一個文件,讀取文件毛病將會發生checked exception毛病。
示例項目構造設計
代碼片斷解釋
1、數據存儲文件:test.properties
key1=hello key2=iteye.com
這就是我們要讀取的文件,為了簡略起見,它是一個properties文件。
2、數據拜訪類:TestDao.java
package com.iteye.redhacker.jersey.dao; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Properties; import org.springframework.stereotype.Component; import com.iteye.redhacker.jersey.exception.DaoException; import com.iteye.redhacker.jersey.exception.ExceptionCode; @Component public class TestDao { public String sayHello() { ClassLoader classLoader = TestDao.class.getClassLoader(); String iniFile = "com/iteye/redhacker/jersey/dao/test.properties"; URL url = classLoader.getResource(iniFile); InputStream is; try { is = url.openStream(); } catch (IOException e) { throw new DaoException(e, ExceptionCode.READ_FILE_FAILED); } Properties proper = null; try { if (proper == null) { proper = new Properties(); } proper.load(url.openStream()); } catch (IOException e) { throw new DaoException(e, ExceptionCode.READ_CONFIG_FAILED); } finally { if (is != null) { try { is.close(); is = null; } catch (IOException e) { throw new DaoException(e, ExceptionCode.COLSE_FILE_FAILED); } } } return proper.getProperty("key1") + "," + proper.getProperty("key2"); } }
在該類中,將checked exception全體轉化為unchecked exception(我們自界說的exception),挪用sayHello()辦法時,不再須要try...catch...
3、營業完成類:TestService.java
package com.iteye.redhacker.jersey.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.iteye.redhacker.jersey.dao.TestDao; import com.iteye.redhacker.jersey.exception.ExceptionCode; import com.iteye.redhacker.jersey.exception.ServiceException; @Component public class TestService { @Autowired private TestDao testDao; public String sayHello(int n) { // 營業上劃定n不克不及年夜於10 if (n > 10) { throw new ServiceException(ExceptionCode.MUST_BE_LESS_THAN_10); } return testDao.sayHello(); } /** * @param testDao the testDao to set */ public void setTestDao(TestDao testDao) { this.testDao = testDao; } }
在該類中,我們拋出了一個本身的營業異常,它是一個unchecked exception。
留意:我們應用@Autowired注入了TestDao類,@Autowired是Spring供給的一個注解;我們必需供給一個要注解屬性的Set辦法,不然注解將掉敗。
4、要求接入類:TestResources.java
package com.iteye.redhacker.jersey.delegate; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import com.iteye.redhacker.jersey.service.TestService; import com.sun.jersey.api.spring.Autowire; @Path("/test") @Autowire public class TestResources { private TestService testService; @GET @Produces(MediaType.TEXT_PLAIN) public String sayHello(@QueryParam("n") int n) { return testService.sayHello(n); } /** * @param testService the testService to set */ public void setTestService(TestService testService) { this.testService = testService; } }
這裡是jersey界說的一個資本,我們可以如許拜訪這個資本:提議GET要求,拜訪URI為/resources/test,可以傳遞一個查詢參數n,例如:/resources/test?n=1
留意:我們應用了@Autowire其實不是Spring的一個注解,它是jersey-srping集成包的一個注解;我們必需供給一個要注解屬性的Set辦法,不然注解將掉敗。
5、同一異常處置器類:ExceptionMapperSupport.java
package com.iteye.redhacker.jersey.jaxrs; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; import org.apache.log4j.Logger; import org.springframework.web.context.WebApplicationContext; import com.iteye.redhacker.jersey.exception.BaseException; import com.iteye.redhacker.jersey.exception.ExceptionCode; import com.sun.jersey.api.NotFoundException; /** * 同一異常處置器 */ @Provider public class ExceptionMapperSupport implements ExceptionMapper<Exception> { private static final Logger LOGGER = Logger .getLogger(ExceptionMapperSupport.class); private static final String CONTEXT_ATTRIBUTE = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; @Context private HttpServletRequest request; @Context private ServletContext servletContext; /** * 異常處置 * * @param exception * @return 異常處置後的Response對象 */ public Response toResponse(Exception exception) { String message = ExceptionCode.INTERNAL_SERVER_ERROR; Status statusCode = Status.INTERNAL_SERVER_ERROR; WebApplicationContext context = (WebApplicationContext) servletContext .getAttribute(CONTEXT_ATTRIBUTE); // 處置unchecked exception if (exception instanceof BaseException) { BaseException baseException = (BaseException) exception; String code = baseException.getCode(); Object[] args = baseException.getValues(); message = context.getMessage(code, args, exception.getMessage(), request.getLocale()); } else if (exception instanceof NotFoundException) { message = ExceptionCode.REQUEST_NOT_FOUND; statusCode = Status.NOT_FOUND; } // checked exception和unchecked exception均被記載在日記裡 LOGGER.error(message, exception); return Response.ok(message, MediaType.TEXT_PLAIN).status(statusCode) .build(); } }
在這個類外面我們處置了我們界說的unchecked exception異常,還處置了體系未知的exception(包含未知的unchecked exception和checked exception)。我們的處置方法是:a、記載異常日記;b、向客戶端拋一個尺度的http尺度毛病狀況碼和毛病新聞,由客戶端對毛病信息停止自行處置,值得解釋的是,這類處置方法是REST所倡導的,它適當的應用了HTTP尺度狀況碼;
在這個類中我們還應用了spring的國際化設置裝備擺設組件,用於對體系拋出的毛病key停止國際化轉換,這有益於我們的項目國際化進級。
6、自界說異常基類:BaseException.java
package com.iteye.redhacker.jersey.exception; /** * 異常基類,各個模塊的運轉期異常均繼續與該類 */ public class BaseException extends RuntimeException { /** * the serialVersionUID */ private static final long serialVersionUID = 1381325479896057076L; /** * message key */ private String code; /** * message params */ private Object[] values; /** * @return the code */ public String getCode() { return code; } /** * @param code the code to set */ public void setCode(String code) { this.code = code; } /** * @return the values */ public Object[] getValues() { return values; } /** * @param values the values to set */ public void setValues(Object[] values) { this.values = values; } public BaseException(String message, Throwable cause, String code, Object[] values) { super(message, cause); this.code = code; this.values = values; } }
這個類界說了項目異常類的根本模板,其他異常繼續與它。值得留意的是,它奇妙的應用了國際化設置裝備擺設的一些特點,乃至可以拋出上面如許界說的一個毛病新聞,經由過程傳遞參數的方法,復用毛病信息:
第{0}個{1}參數毛病
7、其他異常根本差不多,只是類型分歧,我們看一下DaoException.java
package com.iteye.redhacker.jersey.exception; public class DaoException extends BaseException { /** * Constructors * * @param code * 毛病代碼 */ public DaoException(String code) { super(code, null, code, null); } /** * Constructors * * @param cause * 異常接口 * @param code * 毛病代碼 */ public DaoException(Throwable cause, String code) { super(code, cause, code, null); } /** * Constructors * * @param code * 毛病代碼 * @param values * 一組異常信息待定參數 */ public DaoException(String code, Object[] values) { super(code, null, code, values); } /** * Constructors * * @param cause * 異常接口 * @param code * 毛病代碼 * @param values * 一組異常信息待定參數 */ public DaoException(Throwable cause, String code, Object[] values) { super(code, null, code, values); } private static final long serialVersionUID = -3711290613973933714L; }
它繼續了BaseException,當拋出這個異常時,我們就從異常名字上直接初步斷定出,毛病出自Dao層。
8、errMsg.properties用於界說異常信息,來看一下:
read.file.failed=讀取文件掉敗 read.config.failed=讀取設置裝備擺設項掉敗 must.be.less.than.10=參數必需小於10 colse.file.failed=封閉文件掉敗 request.not.found=沒有找到響應的辦事 internal.server.error=辦事器外部毛病
3、安排及測試
你可以在本文附件裡下載到源碼。導入eclipse後,檢查源碼。
安排很簡略,只需將你的tomcat/config/server.xml裡參加:
<Host> ... <Context path="/a" reloadable="true" docBase="D:/workspace/test/JerseyExceptionMapperTest/web" /> </Host>
啟動tomcat便可以了!
做兩個測試:
1、
2、
第1個測試,還可以在log中看到以下異常毛病:
[2013-08-15 00:25:55] [ERROR] 參數必需小於10 com.iteye.redhacker.jersey.exception.ServiceException: must.be.less.than.10 at com.iteye.redhacker.jersey.service.TestService.sayHello(TestService.java:20) at com.iteye.redhacker.jersey.delegate.TestResources.sayHello(TestResources.java:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185) at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75) at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288) at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108) at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483) at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363) at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
關於其他的一些測試,年夜家可以去測驗考試一下,好比有意把test.properties刪除,當找不到要讀取的文件時,checked exception是若何轉化為我們本身界說個unchecked exception,並記載下了日記,前往給客戶端尺度的http毛病狀況碼和毛病信息。
4、總結
1、經由過程jersey框架我們不好看出,在web項目開辟來說,關於checked exception和unchecked exception的處置我們盡量在框架層面就停止了同一處置,以便我們加倍存眷與營業的完成。
2、假如長短web項目,我想,法式架構設計者也應該盡可能同一的處置異常;假如不做同一處置,當碰到checked exception,我們應該對其停止適當的異常處置,而不是否是簡略的做一個e.printStackTrace()的處置;假如我們不克不及恢復異常,那我們至多要將異常的毛病信息完全的記載到日記文件中去,以便後續的法式湧現毛病時停止毛病排查。
全文(完)