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()的處置;假如我們不克不及恢復異常,那我們至多要將異常的毛病信息完全的記載到日記文件中去,以便後續的法式湧現毛病時停止毛病排查。
全文(完)