나는 다음 코드를 여러 번 본 적이있다.
try
{
... // some code
}
catch (Exception ex)
{
... // Do something
throw new CustomException(ex);
// or
// throw;
// or
// throw ex;
}
예외를 다시 던지는 목적을 설명해 주시겠습니까? 예외 처리에서 패턴 / 모범 사례를 따르고 있습니까? (나는 "Caller Inform"패턴이라고 불리는 곳을 읽었습니다.)
같은 예외를 되 돌리는 것은 예외를 기록하고 싶지만 처리 할 수 없다면 유용합니다.
잡힌 예외를 래핑하는 새로운 예외를 던지면 추상화에 유리합니다. 예를 들어 라이브러리에 라이브러리 클라이언트가 알지 못하는 예외를 throw하는 제 3 자 라이브러리가 사용됩니다. 이 경우 라이브러리의 고유 한 예외 유형으로 래핑하고이를 대신 throw합니다.
실제로는 차이가 있습니다.
throw new CustomException(ex);
과
throw;
두 번째는 스택 정보를 보존합니다.
그러나 때로는 예외를 응용 프로그램 도메인에 "친숙하게"만들려고하면 DatabaseException이 GUI에 도달하는 대신 원래의 예외가 포함 된 사용자 정의 예외가 발생합니다.
예를 들면 :
try
{
}
catch (SqlException ex)
{
switch (ex.Number) {
case 17:
case 4060:
case 18456:
throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex);
case 547:
throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex);
default:
throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex);
}
}
때로는 메소드의 구현 세부 사항을 숨기거나 개선하려는 경우가 있습니다. 문제의 추상화 수준으로 인해 호출자에게 더 의미가 있습니다. 방법의. 이렇게하려면 원래 예외를 가로 챌 수 있고 대체 할 수 있습니다. 문제를 설명하는 데 더 적합한 맞춤 예외입니다.
예를 들어 텍스트 파일에서 요청한 사용자의 세부 정보를로드하는 메소드를 예로 들어 보겠습니다. 이 방법은 사용자 ID와 접미사 ".data"로 이름 지정된 텍스트 파일이 있다고 가정합니다. 이 파일이 실제로 존재하지 않으면 각 사용자의 세부 정보가 텍스트 파일에 저장된다는 사실이 메서드의 구현 세부 사항이기 때문에 FileNotFoundException을 throw하는 것은별로 의미가 없습니다. 따라서이 메서드는 설명 메시지를 사용하여 사용자 지정 예외에서 원래 예외를 래핑 할 수 있습니다.
표시된 코드와 달리 가장 좋은 방법은 원래 예외를 새 예외의 InnerException 속성으로로드하여 유지해야한다는 것입니다. 이는 개발자가 필요한 경우 기본 문제를 계속 분석 할 수 있음을 의미합니다.
맞춤 예외를 만들 때 다음과 같은 유용한 체크리스트가 있습니다.
• 예외가 던진 이유를 나타내는 좋은 이름을 찾아 이름이 "Exception"이라는 단어로 끝나는 지 확인하십시오.
• 세 가지 표준 예외 생성자를 구현해야합니다.
• Serializable 특성으로 예외를 표시했는지 확인하십시오.
• deserialization 생성자를 구현해야합니다.
• 개발자가 예외를 더 잘 이해하고 처리하는 데 도움이되는 사용자 지정 예외 속성을 추가합니다.
• 사용자 지정 속성을 추가하는 경우 GetObjectData를 구현하고 재정 의하여 사용자 지정 속성을 serialize해야합니다.
• 사용자 지정 속성을 추가하는 경우 Message 속성을 재정 의하여 표준 예외 메시지에 속성을 추가 할 수 있습니다.
• 사용자 지정 예외의 InnerException 속성을 사용하여 원래 예외를 첨부해야합니다.
일반적으로 응용 프로그램 내에서 코드가 구조적으로 어디에 위치하는지에 따라 두 가지 이유 중 하나를 잡아서 다시 throw합니다.
응용 프로그램의 핵심에서는 예외를 더 의미있는 것으로 변환하기 위해 일반적으로 catch하고 다시 throw합니다. 예를 들어 데이터 액세스 계층을 작성하고 SQL Server에서 사용자 정의 오류 코드를 사용하는 경우 SqlException을 ObjectNotFoundException과 같은 것으로 변환 할 수 있습니다. 이는 (a) 호출자가 특정 유형의 예외를 쉽게 처리 할 수 있고 (b) 다른 계층에 지속성을 위해 SQL Server를 사용하고 있다는 사실과 같은 계층의 구현 세부 사항을 방지하기 때문에 유용합니다. 미래의 일들을보다 쉽게 변경할 수 있습니다.
응용 프로그램의 경계에서 예외를 번역하지 않고 catch하고 다시 throw하는 것이 일반적이므로 디버깅 및 라이브 문제 진단에 도움이됩니다. 이상적으로는 운영 팀이 쉽게 모니터링 할 수있는 오류 (예 : 이벤트 로그)뿐만 아니라 개발자를위한 제어 흐름에서 예외가 발생한 곳 (일반적으로 추적)에 대한 컨텍스트를 제공하는 어딘가에서 오류를 게시하는 것이 이상적입니다.
나는 다음과 같은 이유들을 생각할 수있다.
호출자가 고정 된 예외 집합에 대해서만 염려 할 수 있도록 API의 일부로 던진 예외 유형 세트를 고정 된 상태로 유지합니다. 자바에서는 체크 된 예외 메커니즘으로 인해 실제로 그렇게해야합니다.
예외에 컨텍스트 정보를 추가합니다. 예를 들어 DB에서 맨손으로 "레코드를 찾을 수 없음"을 전달하는 대신이를 포착하고 "... YYY 제품을 찾는 주문 번호 XXX을 처리하는 중"을 추가 할 수 있습니다.
일부 정리 작업 - 파일 닫기, 트랜잭션 롤백, 일부 핸들 해제.
일반적으로 "Do Something"은 예외를 더 잘 설명하고 (예를 들어 다른 예외로 감싸는 경우) 또는 특정 소스를 통해 정보를 추적합니다.
또 다른 가능성은 예외 유형이 예외를 포착해야하는지 알 수있는 정보가 충분하지 않은 경우입니다.이 경우 예외를 포착하면 자세한 정보가 제공됩니다.
이것은 메소드가 순전히 좋은 이유로 사용되었다는 것을 말하는 것은 아니며 개발자가 추후 정보가 필요할 수도 있다고 생각할 때 사용됩니다.이 경우 try {} catch {throw;} 스타일이 사용됩니다. 별로 도움이 안된다.
나는 그것이 당신이 예외로 무엇을하려고하는지에 달려 있다고 생각한다.
하나의 좋은 이유는 catch에서 먼저 오류를 기록한 다음이를 UI에 던져 친숙한 오류 메시지를 생성하는 옵션을 사용하여 원래의 오류가 포함 된 오류의 "고급 / 세부 정보"보기를 더 많이 볼 수 있습니다 .
또 다른 접근법은 "재시도"접근법이다. 예를 들어, 에러 카운트가 유지되고, 재 시도의 일정량이 에러가 스택으로 보내지는 유일한 시간이다. (이것은 타임 아웃 데이터베이스 호출을위한 데이터베이스 액세스를 위해 때때로 수행된다. 느린 네트워크를 통한 웹 서비스 액세스).
그러나 그것을 할 다른 이유가있을 것입니다.
참고로, 이것은 각 유형의 재 드로잉과 관련된 질문입니다.예외를 throw하기위한 성능 고려 사항
내 질문은 "왜"우리는 응용 프로그램 예외 처리 전략에서 예외와 그 사용법을 다시 던집니까?
EntLib ExceptionBlock을 사용할 때까지는 오류를 로그하기 전에 오류를 로그하는 데 사용하고있었습니다. 그 시점에서 내가 처리 할 수 있다고 생각할 때 더 심술 스럽지만, 당시에는 플로우 - 온 (flow-on) 버그를 다루기보다는 UAT (로그를 기록한 후)에서 급히 실패하게하는 것이 낫습니다.
응용 프로그램은 아마도 호출 스택보다 높은 예외를 다시 잡아서 다시 던져서 상위 처리기가 해당 호출을 가로 채서 처리 할 수있게합니다. 애플리케이션이 expection을 기록하거나보고하는 최상위 예외 처리기를 갖는 것은 매우 일반적입니다.
또 다른 대안은 코더가 게으르다가 처리하려고하는 일련의 예외를 잡는 대신 모든 것을 포착 한 다음 실제로 처리 할 수없는 것만 다시 던져 버리는 것입니다.
Rafal이 말했듯이 검사 된 예외를 확인되지 않은 예외로 변환하거나 API에 더 적합한 확인 된 예외로 변환하기 위해 수행되는 경우가 있습니다. 여기에 예제가 있습니다.
http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html
다음과 같이 예외를 살펴 본다면메소드 결과를 얻는 다른 방법, 예외를 다시 던지는 것은 다른 객체에 결과를 래핑하는 것과 같습니다.
그리고 이것은 비범 한 세계에서 일반적인 작업입니다. 일반적으로 이것은 두 개의 어플리케이션 레이어의 경계에서 발생합니다 - 레이어의 함수B
레이어에서 함수를 호출합니다.C
, 그것은 변형된다.C
~에 대한 결과B
내부 양식.
A -- calls --> B -- calls --> C
그렇지 않으면 레이어에서A
레이어를 호출하는B
처리 할 수있는 전체 JDK 예외 집합이있을 것입니다. :-)
허용 된 대답이 지적한대로 레이어A
알지도 못하다.C
예외예요.
예
A 층, servlet : 이미지와 메타 정보를 가져옵니다.
B 층, JPEG 라이브러리 : 사용 가능한 DCIM 태그를 수집하여 JPEG 파일을 구문 분석합니다.
레이어 C, 간단한 DB : 랜덤 액세스 파일에서 문자열 레코드를 읽는 클래스. 일부 바이트가 손상되어 " 'bibliographicCitation'레코드의 UTF-8 문자열을 읽을 수 없습니다. '라는 예외가 발생합니다.
그래서A
'서지 인용'의 의미를 이해하지 못할 것이다. 그래서B
이 예외를 번역해야합니다.A
으로TagsReadingException
원본을 래핑합니다.
예외를 다시 던지는 주된 이유는 호출 스택을 손대지 않게하는 것입니다. 따라서 발생하는 상황을 자세히 파악하고 시퀀스를 호출 할 수 있습니다.