程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Jersey框架的同一異常處置機制剖析

Jersey框架的同一異常處置機制剖析

編輯:關於JAVA

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

全文(完)

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