> Java > java지도 시간 > 우아한 Java 예외를 디자인하는 방법은 무엇입니까?

우아한 Java 예외를 디자인하는 방법은 무엇입니까?

王林
풀어 주다: 2023-04-26 11:25:07
앞으로
1222명이 탐색했습니다.

언제 예외를 던져야 합니까

먼저 질문을 이해해야 합니다. 언제 예외를 발생시켜야 합니까? 예외는 개발자가 사용하기 편리하도록 설계한 것이지, 무분별하게 사용하지 않도록 설계한 것입니다. 저자도 예외를 언제 던져야 하는지 많은 친구들에게 물어봤지만, 실제로 정확한 답변을 줄 수 있는 사람은 많지 않습니다. 실제로 이 문제는 매우 간단합니다. 특정 "문제"를 해결할 수 없다고 생각되면 예외를 발생시킬 수 있습니다. 예를 들어, 서비스를 작성 중이고 특정 코드를 작성할 때 문제가 발생할 수 있음을 발견한 경우 예외를 발생시키십시오. 지금이 예외를 발생시킬 수 있는 가장 좋은 시기입니다.

어떤 종류의 예외를 던져야 할까요

언제 예외를 던져야 하는지 이해한 후에, 실제로 예외를 던질 때 어떤 종류의 예외를 선택해야 하는지 생각해 봅시다. 확인된 예외입니까 아니면 확인되지 않은 예외(RuntimeException)입니까? 이 문제를 설명하기 위해 예를 들어 확인된 예외부터 시작하겠습니다. 예를 들어 이러한 비즈니스 로직이 있는 경우 특정 파일에서 특정 데이터를 읽어야 하는 경우 다른 문제로 인해 이 읽기 작업이 불가능할 수 있습니다. 파일 삭제 등으로 인해 읽기 오류가 발생하면 redis 또는 mysql 데이터베이스에서 데이터를 가져와야 합니다. getKey(Integer)는 입력 프로그램입니다.

공개 문자열 getKey(정수 키){

문자열 값;

시도해 보세요 {

InputStream inputStream = getFiles("/file/nofile");

//다음으로 스트림에서 키 값을 읽습니다

값 = ...;

} 잡기(예외 e) {

//예외가 발생하면 mysql 또는 redis

에서 가져옵니다. 값 = ...;

}

}

공개 InputStream getFiles(문자열 경로)에서 예외가 발생합니다. {

파일 파일 = 새 파일(경로);

InputStream inputStream = null;

시도해 보세요 {

inputStream = new BufferedInputStream(new FileInputStream(파일));

} 잡기(FileNotFoundException e) {

새로운 예외 발생("I/O 읽기 오류",e.getCause());

}

inputStream을 반환합니다;

}

위의 코드를 읽은 후 마음속에 몇 가지 생각이 들 수 있습니다. 확인된 예외가 의무 논리를 제어할 수 있다는 것이 밝혀졌습니다. 예, 그렇습니다. 실제로는 확인된 예외를 통해 비즈니스 논리를 제어할 수 있지만 사용하지 않는 것을 기억하세요. 프로그램 자체가 프로세스이기 때문에 예외를 발생시키는 것은 프로그램 흐름을 제어하기 위한 시작이나 종료로 사용될 수 없습니다. 이런 방식으로 사용하면 예외를 사용하게 됩니다. 확장된 역할은 코드 복잡성 증가, 결합 증가 및 코드 가독성 감소로 이어집니다. 그렇다면 그러한 예외를 사용하면 안 되는 걸까요? 사실, 아니요. 정말로 그러한 필요가 있을 때 이런 방식으로 사용할 수 있습니다. 하지만 프로세스를 제어하기 위한 도구나 수단으로 간주하지 마십시오. 그렇다면 그러한 예외는 언제 발생해야 할까요? 호출자가 호출 중에 오류를 범하는 경우 해당 요구 사항이 충족되는 경우에만 호출자가 오류를 처리하도록 허용해야 한다는 점을 고려해야 합니다.

다음으로 확인되지 않은 예외(RuntimeException)를 살펴보겠습니다. 실제로 java.lang.NullPointerException/java.lang.IllegalArgumentException 등과 같은 RuntimeException과 같은 예외가 많이 발생합니다. 그러면 이러한 예외는 언제 발생합니까? 특정 메소드를 작성할 때 실수로 오류가 발생할 수 있습니다. 이 문제는 런타임 중에 발생할 수 있으며 이론적으로 이러한 문제가 없으면 호출자는 이 예외를 잡을 필요가 없습니다. 이때 RuntimeException이 발생합니다. 예를 들어 경로가 전달되면 해당 경로에 해당하는 File 객체가 반환되어야 합니다.

공개 무효 테스트() {

myTest.getFiles("");

}

공개 파일 getFiles(문자열 경로) {

if(null == 경로 || "".equals(경로)){

throw new NullPointerException("경로는 비워둘 수 없습니다!");

}

파일 파일 = 새 파일(경로);

파일 반환;

}

위의 예에서는 호출자가 getFiles(String)를 호출할 때 경로가 비어 있으면 널 포인터 예외(RuntimeException의 하위 클래스)가 발생하고 호출자가 명시적으로 시도를 수행할 필요가 없음을 보여줍니다. catch... 강제 처리를 위한 작업입니다. RuntimeException을 방지하려면 호출자가 먼저 확인해야 합니다. 어떤 예외를 사용해야 합니까

위의 설명과 예제를 통해 RuntimeException과 확인된 예외의 차이점은 호출자가 이 예외를 강제로 처리해야 하는지 여부입니다. 그렇지 않으면 확인된 예외를 사용합니다. 확인되지 않은 예외(RuntimeException)를 선택합니다. 일반적으로 특별한 요구 사항이 없으면 RuntimeException을 사용하는 것이 좋습니다.

시나리오 소개 및 기술 선정

건축물 설명

우리가 알고 있듯이 전통적인 프로젝트는 MVC 프레임워크를 기반으로 개발되었습니다. 이 기사에서는 주로 편안한 스타일 인터페이스의 디자인을 사용하여 예외 처리의 우아함을 경험했습니다.

우리는 Restful API 계층(웹의 컨트롤러 계층과 유사)과 서비스 계층에 중점을 두고 서비스에서 예외가 발생하는 방법을 연구한 다음 API 계층이 예외를 캡처하고 변환하는 방법을 연구합니다.

사용되는 기술은 spring-boot, jpa(hibernate), mysql이며, 이러한 기술에 익숙하지 않은 경우 독자가 직접 관련 자료를 읽어야 합니다.

비즈니스 시나리오 설명

전자 상거래의 배송 주소 관리를 예로 들어 상대적으로 간단한 비즈니스 시나리오를 선택하십시오. 사용자가 모바일 단말기에서 상품을 구매할 때 프로젝트에서는 배송 주소를 관리해야 합니다. 액세스: 배송 주소 추가, 배송 주소 삭제, 배송 주소 변경, 기본 배송 주소 설정, 배송 주소 목록 쿼리, 단일 배송 주소 쿼리 및 기타 인터페이스.

제약조건을 구축하세요

좋습니다. 이것은 설정된 매우 기본적인 비즈니스 시나리오입니다. 물론 어떤 종류의 API 작업이든 여기에는 몇 가지 규칙이 포함됩니다.

배송 주소 추가:

재료:

사용자 아이디

주소 엔터티 정보 수신

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송지 필수 입력란은 비워둘 수 없습니다

사용자에게 아직 배송 주소가 없는 경우 이 배송 주소는 생성 시 기본 배송 주소로 설정됩니다. —

배송 주소 삭제:

재료:

사용자 아이디

배송주소 id

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송 주소는 비워둘 수 없으며 이 배송 주소는 존재합니다

이 배송 주소가 사용자의 배송 주소인지 확인하세요

해당 배송 주소가 기본 배송 주소인지 확인하세요. 기본 배송 주소라면 삭제할 수 없습니다

배송 주소 변경:

재료:

사용자 ID

배송주소 id

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송 주소는 비워둘 수 없으며 배송 주소가 존재합니다

이 배송 주소가 사용자의 배송 주소인지 확인하세요

기본 주소 설정:

재료:

사용자 ID

배송주소 id

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송 주소는 비워둘 수 없으며 배송 주소가 존재합니다

이 배송 주소가 사용자의 배송 주소인지 확인하세요

배송 주소 목록 쿼리:

재료:

사용자 ID

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

단일 배송 주소 문의:

재료:

사용자 ID

배송주소 id

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송 주소는 비워둘 수 없으며 배송 주소가 존재합니다

이 배송 주소가 사용자의 배송 주소인지 확인하세요

제약 판단과 기술 선택

위에 나열된 제약 조건 및 기능 목록에 대해 분석을 위해 몇 가지 일반적인 예외 처리 시나리오(배송 주소 추가, 배송 주소 삭제, 배송 주소 목록 가져오기)를 선택했습니다.

그렇다면 우리는 어떤 필수 지식을 보유해야 할까요? 배송 주소 기능을 살펴보겠습니다:

배송 주소를 추가할 때 사용자 ID와 배송 주소 엔터티 정보를 확인해야 합니다. 그러면 비어 있지 않은 판단 도구를 어떻게 선택합니까? 전통적인 판단은 다음과 같습니다.

/**

* 주소 추가

* @param uid

* @param 주소

* @return

*/

공개 주소 addAddress(정수 uid,주소 주소){

if(null != uid){

//프로세스..

}

null을 반환합니다;

}

위의 예에서는 uid가 비어 있다고 판단하면 괜찮지만, 주소 엔터티에 필요한 일부 속성이 비어 있는지 판단하면 필드가 많아지면 재앙이 됩니다.

그러면 매개변수 입력에 대해 어떻게 판단해야 할까요? 두 가지 지식 포인트를 소개하겠습니다.

Guava의 Preconditions 클래스는 많은 매개변수 입력 방법에 대한 판단을 구현합니다

JSR 303 유효성 검사 사양(현재 구현은 비교적 완벽하며 최대 절전 모드로 구현된 최대 절전 모드 유효성 검사기)

이 두 가지 추천 기술을 활용하면 매개변수 입력에 대한 판단이 훨씬 단순해질 것이다. 불필요한 작업량을 많이 줄일 수 있는 성숙한 기술과 jar 툴킷을 모든 사람이 사용하는 것이 좋습니다. 우리는 비즈니스 로직에만 집중하면 됩니다. 이러한 입력 판단으로 인해 더 많은 시간을 지연시키는 것이 아닙니다.

Java 예외를 우아하게 디자인하는 방법

도메인 소개

프로젝트 시나리오에 따르면 두 가지 도메인 모델이 필요합니다. 하나는 사용자 엔터티이고 다른 하나는 주소 엔터티입니다.

주소 도메인은 다음과 같습니다:

@엔티티

@데이터

공개 수업 주소 {

@ID

@GeneratedValue

개인 정수 ID;

개인 문자열 지방;//지방

개인 문자열 도시;//city

개인 스트링 카운티;//district

private Boolean isDefault;//기본 주소인가요

@ManyToOne(cascade={CascadeType.ALL})

@JoinColumn(name="uid")

개인 사용자 user;

}

사용자 도메인은 다음과 같습니다.

@엔티티

@데이터

공개 클래스 사용자 {

@ID

@GeneratedValue

개인 정수 ID;

개인 문자열 이름;//name

@OneToMany(cascade= CascadeType.ALL,mappedBy="user",fetch = FetchType.LAZY)

비공개 세트 주소;

}

위의 내용은 모델 관계이고 사용자와 배송 주소 간의 관계는 1-n 관계입니다. 위의 @Data는 Setter 및 Getter 메서드를 자동으로 생성하는 lombok이라는 도구를 사용하는데, 관심 있는 독자가 스스로 학습할 수 있습니다.

다오 소개

데이터 연결 계층의 경우 spring-data-jpa 프레임워크를 사용합니다. 이를 위해서는 프레임워크에서 제공하는 인터페이스만 상속하고 규칙에 따라 메서드 이름을 지정하면 원하는 데이터베이스 작업을 완료할 수 있습니다.

사용자 데이터베이스 작업은 다음과 같습니다.

@Repository

공용 인터페이스 IUserDao는 JpaRepository를 확장합니다. }

배송주소 운영은 다음과 같습니다

@Repository

공용 인터페이스 IAddressDao는 JpaRepository를 확장합니다. }

독자들이 볼 수 있듯이 우리 DAO는 JpaRepository만 상속하면 되며 이는 이미 기본 CURD 및 기타 작업을 완료하는 데 도움이 되었습니다. spring-data 프로젝트에 대해 더 자세히 알고 싶다면 spring 공식 문서를 참조하세요. 이상현상에 대한 연구.

서비스 비정상적인 디자인

자, 드디어 초점에 도달했습니다. 배송 주소 추가, 배송 주소 삭제, 배송 주소 목록 가져오기 등 몇 가지 서비스 작업을 완료해야 합니다.

먼저 내 서비스 인터페이스 정의를 살펴보세요.

공개 인터페이스 IAddressService {

/**

* 배송 주소 만들기

* @param uid

* @param 주소

* @return

*/

주소 createAddress(정수 uid,주소 주소);

/**

* 배송지 주소 삭제

* @param uid

* @param 원조

*/

void deleteAddress(정수 uid,정수 보조);

/**

* 사용자의 모든 배송 주소를 쿼리하세요

* @param uid

* @return

*/

목록<주소> listAddresses(정수 uid);

}

구현에 집중해 봅시다:

배송 주소 추가

먼저 이전에 컴파일한 제약 조건을 살펴보겠습니다.

재료:

사용자 ID

주소 엔터티 정보 수신

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송지 필수 입력란은 비워둘 수 없습니다

사용자가 아직 배송 주소가 없으면 생성 시 기본 배송 주소로 설정됩니다

먼저 다음 코드 구현을 살펴보세요.

@오버라이드

공개 주소 createAddress(정수 uid, 주소 주소) {

//============ 제약조건은 다음과 같습니다 ==============

//1. 사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

Preconditions.checkNotNull(uid);

사용자 사용자 = userDao.findOne(uid);

if(null == 사용자){

throw new RuntimeException("현재 사용자를 찾을 수 없습니다!");

}

//2. 배송 주소 필수 항목은 비워둘 수 없습니다

BeanValidators.validateWithException(유효성 검사기, 주소);

//3. 사용자가 아직 배송 주소가 없는 경우 배송 주소가 생성될 때 기본 배송 주소로 설정됩니다

if(ObjectUtils.isEmpty(user.getAddresses())){

address.setIsDefault(true);

}

//============ 다음은 일반적으로 실행되는 비즈니스 로직입니다 ==============

address.setUser(사용자);

주소 결과 = addressDao.save(address);

결과 반환;

}

그 중 위에서 설명한 세 가지 제약 조건이 충족되면 정상적인 비즈니스 로직이 수행될 수 있으며, 그렇지 않으면 예외가 발생합니다(일반적으로 여기서는 런타임 예외(RuntimeException)를 발생시키는 것이 좋습니다).

위에서 사용한 다음 기술을 소개합니다:

Preconfitions.checkNotNull(T t)은 Guava에서 com.google.common.base.Preconditions를 사용하여 판단됩니다. 서비스에서 많은 검증이 사용되므로 Preconfigions를 정적 가져오기로 변경하는 것이 좋습니다.

1정적 com.google.common.base.Preconditions.checkNotNull 가져오기;

물론 Guava의 github 지침에서도 이 방법을 사용하도록 권장하고 있습니다.

BeanValidators.validateWithException(유효성 검사기, 주소);

이는 최대 절전 모드로 구현된 jsr 303 사양을 사용하여 수행됩니다. 유효성 검사기와 확인해야 하는 엔터티를 전달해야 합니다. 따라서 유효성 검사기를 얻는 방법은 다음과 같습니다.

@구성

공개 클래스 BeanConfigs {

@빈

공개 javax.validation.Validator getValidator(){

새로운 LocalValidatorFactoryBean()을 반환합니다.

}

}

Validator 개체를 얻은 다음 이를 서비스에 삽입하여 사용할 수 있습니다.

@Autowired

개인 유효성 검사기 유효성 검사기 ;

그렇다면 BeanValidators 클래스는 어떻게 구현됩니까? 실제로 구현은 매우 간단합니다. jsr 303의 주석을 판단하면 괜찮을 것입니다.

그렇다면 jsr 303 주석은 어디에 기록됩니까? 물론 주소 엔터티 클래스에 기록됩니다.

@엔티티

@세터

@게터

공개 수업 주소 {

@ID

@GeneratedValue

개인 정수 ID;

@NotNull

개인 문자열 지방;//지방

@NotNull

개인 문자열 도시;//city

@NotNull

개인 스트링 카운티;//district

private Boolean isDefault = false;//기본 주소인가요

@ManyToOne(cascade={CascadeType.ALL})

@JoinColumn(name="uid")

개인 사용자 user;

}

판단에 필요한 제약사항을 작성하면 비즈니스 운영과 데이터베이스 운영이 가능합니다.

이 부분에 대한 검증이 필요한 이유 중 하나는 이러한 검증을 통해 더러운 데이터가 삽입되는 것을 방지할 수 있다는 것입니다. 공식적인 온라인 경험이 있는 독자라면 그러한 점을 이해할 수 있습니다. 어떤 코드 오류도 용납하고 수정할 수 있지만 더티 데이터 문제가 발생하면 엄청난 재앙이 될 수 있습니다. 프로그램 문제는 수정될 수 있지만 손상된 데이터는 복구가 불가능할 수 있습니다. 따라서 비즈니스 논리 작업을 수행하기 전에 서비스의 제약 조건을 결정해야 하는 이유입니다.

여기서의 판단은 비즈니스 관점에서 이루어지는 비즈니스 로직 판단입니다. 또한 많은 시나리오에서 다양한 비즈니스 조건 제약이 있을 수 있으며 요구 사항에 따라 수행하면 됩니다.

제약사항을 요약하면 다음과 같습니다.

기본 판단 제약(널값 등 기본 판단)

엔터티 속성 제약 조건(jsr 303과 같은 기본 판단 충족)

비즈니스 조건 제약(요구사항에 따라 제안되는 다양한 비즈니스 제약)

이 세 가지 사항이 충족되면 다음 단계로 진행할 수 있습니다

자, 이것은 기본적으로 기본적인 판단을 내리는 방법을 소개합니다. 예외 설계 문제로 돌아가서 위의 코드는 적절한 위치에서 예외를 합리적으로 판단하는 방법을 명확하게 설명했습니다.

RuntimeException을 발생시키는 것만으로도 예외를 정상적으로 발생시키는 것으로 간주됩니까? 물론 그렇지 않습니다. 서비스에서 발생하는 예외에 대해서는 예외를 발생시키는 방법이 대략 두 가지가 있다고 생각합니다.

상태 코드 RumtimeException

과 함께 예외가 발생합니다. 지정된 유형의 RuntimeException이 발생합니다

두 가지 예외 메서드와 비교하면 첫 번째 예외는 모든 예외가 RuntimeException 예외를 발생시키지만 상태 코드가 있어야 한다는 것을 의미합니다. 호출자는 상태 코드를 기반으로 서비스가 어떤 종류의 예외를 발생시켰는지 쿼리할 수 있습니다.

두 번째 유형의 예외는 서비스에서 발생하는 모든 예외에 대해 지정된 예외 오류를 사용자 정의한 다음 예외를 발생시키는 것을 의미합니다.

일반적으로 시스템에 특별한 요구 사항이 없다면 개발 및 설계 단계에서 두 번째 방법을 사용하는 것이 좋습니다. 하지만 예를 들어 기본 판단 예외는 guava에서 제공하는 클래스 라이브러리를 사용하여 완벽하게 작동할 수 있습니다. JSR 303 예외는 캡슐화된 자체 예외 판단 클래스를 사용하여 작동할 수도 있습니다. 왜냐하면 이 두 예외는 기본적인 판단이고 특별한 예외를 지정할 필요가 없기 때문입니다. 그러나 세 번째 의무조건 제약 판단에서 발생하는 예외에 대해서는 지정된 유형의 예외를 발생시켜야 합니다.

을 위해 1throw new RuntimeException("현재 사용자를 찾을 수 없습니다!");

이 의무 예외를 판단하려면 특정 예외 클래스를 정의하세요.

공개 클래스 NotFindUserException은 RuntimeException을 확장합니다. {

공개 NotFindUserException() {

super("이 사용자를 찾을 수 없습니다.");

}

공개 NotFindUserException(문자열 메시지) {

슈퍼(메시지);

}

}

그런 다음 이것을 다음과 같이 변경합니다:

1throw new NotFindUserException("현재 사용자를 찾을 수 없습니다!");

아니면

1새 NotFindUserException()을 발생시킵니다.

좋아, 위의 서비스 레이어 수정을 통해 코드 변경 사항은 다음과 같습니다.

@오버라이드

공개 주소 createAddress(정수 uid, 주소 주소) {

//============ 제약조건은 다음과 같습니다 ==============

//1. 사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

checkNotNull(uid);

사용자 사용자 = userDao.findOne(uid);

if(null == 사용자){

throw new NotFindUserException("현재 사용자를 찾을 수 없습니다!");

}

//2. 배송 주소 필수 항목은 비워둘 수 없습니다

BeanValidators.validateWithException(유효성 검사기, 주소);

//3. 사용자가 아직 배송 주소가 없는 경우 배송 주소가 생성될 때 기본 배송 주소로 설정됩니다

if(ObjectUtils.isEmpty(user.getAddresses())){

address.setIsDefault(true);

}

//============ 다음은 일반적으로 실행되는 비즈니스 로직입니다 ==============

address.setUser(사용자);

주소 결과 = addressDao.save(address);

결과 반환;

}

이런 서비스가 더 안정적이고 이해하기 쉬운 것 같습니다.

배송 주소 삭제:

재료:

사용자 ID

배송주소 id

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

배송 주소는 비워둘 수 없으며 배송 주소가 존재합니다

이 배송 주소가 사용자의 배송 주소인지 확인하세요

해당 배송 주소가 기본 배송 주소인지 확인하세요. 기본 배송 주소라면 삭제할 수 없습니다

위의 배송주소 추가와 유사하므로 자세한 설명은 생략하겠습니다. 삭제 서비스 디자인은 다음과 같습니다. @Override

public void deleteAddress(정수 uid, 정수 보조) {

//============ 제약조건은 다음과 같습니다 ==============

//1. 사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

checkNotNull(uid);

사용자 사용자 = userDao.findOne(uid);

if(null == 사용자){

새로운 NotFindUserException()을 던져보세요;

}

//2. 배송 주소는 비워둘 수 없으며 배송 주소는 존재합니다

checkNotNull(aid);

주소 주소 = addressDao.findOne(aid);

if(null == 주소){

새로운 NotFindAddressException()을 던져보세요;

}

//3. 이 배송 주소가 사용자의 배송 주소인지 확인합니다

if(!address.getUser().equals(사용자)){

새로운 NotMatchUserAddressException()을 던져보세요;

}

//4. 이 배송 주소가 기본 배송 주소인지 확인합니다. 기본 배송 주소인 경우 삭제할 수 없습니다

if(address.getIsDefault()){

새로운 DefaultAddressNotDeleteException()을 던져보세요;

}

//============ 다음은 일반적으로 실행되는 비즈니스 로직입니다 ==============

addressDao.delete(주소);

}

NotFindUserException, NotFindAddressException, NotMatchUserAddressException, DefaultAddressNotDeleteException의 네 가지 관련 예외 클래스가 설계되었습니다.

배송 주소 목록 받기:

재료:

사용자 ID

제약:

사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

코드는 다음과 같습니다: @Override

공개 목록

listAddresses(정수 uid) {

//============ 제약조건은 다음과 같습니다 ==============

//1. 사용자 ID는 비워둘 수 없으며 이 사용자는 존재합니다

checkNotNull(uid);

사용자 사용자 = userDao.findOne(uid);

if(null == 사용자){

새로운 NotFindUserException()을 던져보세요;

}

//============ 다음은 일반적으로 실행되는 비즈니스 로직입니다 ==============

사용자 결과 = userDao.findOne(uid);

return result.getAddresses();

}

API 비정상적인 디자인

던지는 방법은 대략 두 가지가 있습니다:

상태 코드 RumtimeException

과 함께 예외가 발생합니다. 지정된 유형의 RuntimeException이 발생합니다

이는 서비스 계층의 도입을 통해 서비스 계층에서 예외가 발생할 때 두 번째 발생 방법을 선택했다는 점입니다. API 계층에서 예외를 발생시킬 때 두 가지 방법을 사용해야 합니다. to throw: 예외를 발생시키기 전에 API 예외 유형과 관련 상태 코드를 지정해야 합니다. 이 예외 설계의 핵심은 API를 호출하는 사용자가 예외를 발생시키는 것 외에도 예외를 더 명확하게 이해할 수 있도록 하는 것입니다. 또한 사용자의 쿼리를 용이하게 하기 위해 상태 코드에 해당하는 예외의 세부 정보와 예외로 인해 발생할 수 있는 문제를 사용자에게 표시하는 해당 테이블을 만들어야 합니다. (예: github에서 제공하는 api 문서, WeChat에서 제공하는 api 문서 등) 또 다른 이점이 있습니다. 사용자가 프롬프트 메시지를 사용자 정의해야 하는 경우 반환된 상태 코드에 따라 프롬프트를 수정할 수 있습니다.

API 유효성 검사 제약 조건

우선, API 설계를 위해서는 dto 개체가 있어야 합니다. 이 개체는 호출자와의 통신 및 데이터 전송을 담당하며, dto->domain이 서비스에 전달되어 작동되어야 합니다. 둘째, Point는 앞서 언급한 기본 판단(무효 판단)과 jsr 303 검증이 필요한 서비스 외에 api 레이어에서도 관련 검증을 수행해야 하며, 검증에 실패하면 호출자에게 직접 반환됩니다. 호출이 실패했고 실패해서는 안 된다고 알립니다. 불법적인 데이터로 서비스에 액세스하면 독자가 약간 혼란스러울 수 있습니다. 서비스가 검증되지 않은 경우에도 API 계층을 검증해야 하는 이유는 무엇입니까? 여기에는 프로그래밍의 머피의 법칙이라는 개념이 설계되어 있습니다. API 계층의 데이터 확인을 무시하면 불법 데이터가 서비스 계층으로 유입될 수 있으며, 이후 더티 데이터가 데이터베이스에 저장될 수 있습니다.

따라서 엄격한 프로그래밍의 핵심은 수신된 데이터가 합법적이라고 믿지 않는 것입니다.

API 비정상적인 디자인

위에서 설명한 것처럼 API 계층 예외를 설계할 때 오류 코드와 오류 메시지를 제공해야 합니다. 그러면 다음과 같이 일반 API 슈퍼 클래스 예외를 제공하도록 설계할 수 있습니다.

공개 클래스 ApiException은 RuntimeException을 확장합니다. {

보호된 긴 오류 코드 ;

보호된 개체 데이터 ;

공개 ApiException(긴 오류 코드, 문자열 메시지, 객체 데이터, Throwable e){

슈퍼(메시지,e);

this.errorCode = 오류 코드 ;

this.data = 데이터 ;

}

공개 ApiException(긴 오류 코드, 문자열 메시지, 개체 데이터){

this(errorCode,메시지,데이터,null);

}

공개 ApiException(긴 오류 코드, 문자열 메시지){

this(errorCode,message,null,null);

}

공개 ApiException(문자열 메시지, Throwable e){

this(null, 메시지, null,e);

}

공개 ApiException(){

}

공개 ApiException(Throwable e){

슈퍼(e);

}

공개 긴 getErrorCode() {

errorCode를 반환합니다.

}

공개 무효 setErrorCode(긴 errorCode) {

this.errorCode = errorCode;

}

공개 객체 getData() {

데이터 반환;

}

공개 무효 setData(객체 데이터) {

this.data = 데이터;

}

}

api层异常:ApiDefaultAddressNotDeleteException,ApiNotFindAddressException,ApiNotFindUserException,ApiNotMatchUserAddressException。

以默认地址不能删除为例:

공개 클래스 ApiDefaultAddressNotDeleteException은 ApiException을 확장합니다. {

공개 ApiDefaultAddressNotDeleteException(문자열 메시지) {

super(AddressErrorCode.DefaultAddressNotDeleteErrorCode, 메시지, null);

}

}

AddressErrorCode.DefaultAddressNotDeleteErrorCode는 다음과 같습니다.

공개 추상 클래스 AddressErrorCode {

public static final Long DefaultAddressNotDeleteErrorCode = 10001L;//默认地址不能删除

공개 정적 최종 Long NotFindAddressErrorCode = 10002L;//找不到此收货地址

공개 정적 최종 Long NotFindUserErrorCode = 10003L;//找不到此用户

public static final Long NotMatchUserAddressErrorCode = 10004L;//사용户与收货地址不匹配

}

알겠습니다. API의 异常就已经设计完了, 여기에 더 많은 정보가 있습니다.到配置文件中进行管理。

api处理异常

api层会调사용 서비스层, 然后来处理 서비스 중출형 所有异常, 首先, 需要保证一点, 一定要让api层不常轻,基本上做成一个转发的功能就好(接口参数,传递给service参数,返回给调사용자数据,这三个基本功能),然后就要재传递给service参数的那个方法调上进行异常处理。

此处仅以添加地址为例:

@Autowired

개인 IAddressService 주소서비스;

/**

*배송지 추가

* @param 주소DTO

* @return

*/

@RequestMapping(메서드 = RequestMethod.POST)

공개 AddressDTO add(@Valid @RequestBody AddressDTO addressDTO){

주소 주소 = 새 주소();

BeanUtils.copyProperties(addressDTO,주소);

주소 결과;

시도해 보세요 {

결과 = addressService.createAddress(addressDTO.getUid(), 주소);

}catch(NotFindUserException e){

throw new ApiNotFindUserException("找不到该用户");

}catch(예외 e){//未知错误

새로운 ApiException(e)을 던져보세요;

}

AddressDTO 결과DTO = 새 AddressDTO();

BeanUtils.copyProperties(result,resultDTO);

resultDTO.setUid(result.getUser().getId());

결과 반환DTO;

}

这里的处理方案是调用 service时,判断异常的类型,然后将任何service异常道转化成api异常,然后抛异常,这是常用 一种异常转化方式. 似删除收货地址와获取收货地址也类似这样处理,여기此,不在赘述。

api异常转化

已经讲解了如何抛出异常和何如将service异常转化为api异常,那么转化成api异常直接抛take是否就完成了异常处理呢?答案是否定的,当抛出api异常后,我们需要把api异常返回的数据(json 또는 xml)让用户看懂,那么需要把api异常转化成dto对象(ErrorDTO),看如下代码:

@ControllerAdvice(주석 = RestController.class)

클래스 ApiExceptionHandlerAdvice {

/**

* 핸들러가 던진 예외를 처리합니다.

*/

@ExceptionHandler(값 = Exception.class)

@ResponseBody

공개 ResponseEntity 예외(예외 예외,HttpServletResponse 응답) {

ErrorDTO errorDTO = 새로운 ErrorDTO();

if(예외 인스턴스of ApiException){//api异常

ApiException apiException = (ApiException)예외;

errorDTO.setErrorCode(apiException.getErrorCode());

}else{//未知异常

errorDTO.setErrorCode(0L);

}

errorDTO.setTip(Exception.getMessage());

ResponseEntity responseEntity = 새로운 ResponseEntity<>(errorDTO,HttpStatus.valueOf(response.getStatus()));

응답을 반환Entity;

}

@세터

@게터

클래스 ErrorDTO{

비공개 긴 오류 코드;

비공개 문자열 팁;

}

}

알겠습니다. 这样就完成了api异常转化成용 户可以读懂의 DTO对象了, 代码中用到了@ControllerAdvice, 这是spring MVC는 다음과 같은 일을 하고 있습니다.

API 인터페이스 호출 시 예외가 발생하면 사용자도 일반 데이터 형식을 받을 수 있다. 예를 들어 사용자가 없는데(uid가 2인 경우) 이 사용자에 대해 배송 주소가 추가된 경우 postman(Google 플러그인을 사용함) http 요청을 시뮬레이션하기 위해) 데이터:

{

"오류 코드": 10003,

"tip": "사용자를 찾을 수 없습니다"

}

위 내용은 우아한 Java 예외를 디자인하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿