ホームページ  >  記事  >  Java  >  エレガントな Java 例外を設計するにはどうすればよいでしょうか?

エレガントな Java 例外を設計するにはどうすればよいでしょうか?

王林
王林転載
2023-04-26 11:25:071142ブラウズ

例外をスローする必要があるのはどのような場合です。

まず、いつ例外をスローする必要があるのか​​という質問を理解する必要があります。例外は開発者にとって使いやすいように設計されていますが、無差別に使用されることはありません。著者も例外をスローするタイミングについて多くの友人に尋ねましたが、正確に答えられる人は実際には多くありません。実際、この問題は非常に単純で、特定の「問題」を解決できないと思われる場合は、例外をスローできます。たとえば、サービスを作成していて、特定のコードを作成しているときに問題が発生する可能性があることがわかった場合は、例外をスローしてください。例外をスローするのに最適なタイミングです。

どのような種類の例外をスローする必要があるか

例外をスローする必要がある場合を理解したら、次の質問について考えてみましょう。実際に例外をスローする場合、どのような例外を選択すればよいでしょうか。それはチェックされた例外ですか、それともチェックされていない例外 (RuntimeException) ですか?この問題を例として、チェック例外から説明します。たとえば、このようなビジネス ロジックがある場合、特定のファイルから特定のデータを読み取る必要があります。この読み取り操作は、他の問題によって実行できない可能性があります。ファイルの削除など 取得時に読み込みエラーが発生した場合は、redisまたはmysqlデータベースからデータを取得する必要があります。以下のコードを参照してください。getKey(Integer)はエントリプログラムです。

public String getKey(整数キー){

文字列値;

試す {###### InputStream inputStream = getFiles("/file/nofile");

//次に、ストリーム

からキーの値を読み取ります。 値 = ...;

} catch (例外 e) {

//例外がスローされた場合、例外は mysql または redis から取得されます

値 = ...;

}

}

public InputStream getFiles(String path) が例外をスローします {

ファイル file = 新しいファイル (パス);

入力ストリーム inputStream = null;

試す {###### inputStream = new BufferedInputStream(new FileInputStream(file));

} catch (FileNotFoundException e) {

throw new Exception("I/O 読み取りエラー",e.getCause());

}

inputStream を返す;

}

上記のコードを読んだ後、頭の中にいくつかの考えが浮かぶかもしれません。チェック例外で義務ロジックを制御できることが分かりました。はい、その通りです。実際にはチェック例外を使用してビジネス ロジックを制御できますが、使用しないように注意してください。このようにします。合理的でなければなりません。プログラム自体がプロセスであるため、例外をスローします。例外の機能は、先に進めないときの単なる言い訳です。プログラムの流れを制御する入り口や出口として使用することはできません。役割が拡張されると、コードの複雑さ、結合の増加、およびコードの可読性の低下につながります。では、そのような例外を使用してはいけないのでしょうか?実際、いいえ、本当にそのような必要がある場合には、このように使用できますが、プロセスを制御するためのツールや手段として実際に見なさないように注意してください。では、そのような例外はどのような場合にスローされるのでしょうか?呼び出し側が呼び出しでエラーを起こした場合、呼び出し側にエラーの処理を許可する必要があることを考慮する必要があり、そのような要件が満たされる場合にのみ、チェック例外の使用を検討します。

次に、チェックされていない例外 (RuntimeException) を見てみましょう。実際には、java.lang.NullPointerException/java.lang.IllegalArgumentException など、RuntimeException のような例外がたくさんあります。では、この種の例外はどのようなときにスローされるのでしょうか? ?あるメソッドを作成すると、偶発的にエラーが発生することがあります。この問題は実行時に発生する可能性があると考えられますが、理論的には、そのような問題がなければプログラムは正常に実行されます。呼び出し元はこの例外をキャッチする必要はありません。このとき、RuntimeException がスローされます。たとえば、パスが渡された場合、そのパスに対応する File オブジェクトを返す必要があります:

public void test() {

myTest.getFiles("");

}

public File getFiles(文字列パス) {

if(null == パス || "".equals(path)){

throw new NullPointerException("パスを空にすることはできません!");

}

ファイル file = 新しいファイル (パス);

ファイルを返す;

}

上記の例は、呼び出し元が getFiles(String) を呼び出したときにパスが空の場合、null ポインター例外 (RuntimeException のサブクラス) がスローされ、呼び出し元が明示的に try... を実行する必要がないことを示しています。強制処理のための catch... オペレーション。これには、RuntimeException を回避するために、呼び出し元がそのようなメソッドを呼び出すときに最初に検証する必要があります。 どの例外を使用する必要がありますか

# 上記の説明と例を通じて、結論を導き出すことができます。RuntimeException とチェック例外の違いは、呼び出し元がこの例外の処理を強制されるかどうかです。呼び出し元がこの例外の処理を強制される場合は、チェック例外を使用します。それ以外の場合は、チェック例外を使用します。未チェックの例外 (RuntimeException) を選択します。一般に、特別な要件がない場合は、RuntimeException を使用することをお勧めします。

シナリオの導入とテクノロジーの選択

アーキテクチャの説明

ご存知のとおり、従来のプロジェクトは MVC フレームワークに基づいて開発されていますが、この記事では主に例外処理の優雅さを体験するために、Restful スタイルのインターフェイスのデザインを使用します。

ここでは、Restful API 層 (Web のコントローラー層に似ています) とサービス層に焦点を当て、サービスで例外がどのようにスローされるか、そして API 層が例外をどのようにキャプチャして変換するかを調査します。

使用されているテクノロジは、spring-boot、jpa (hibernate)、mysql です。これらのテクノロジに詳しくない場合は、読者自身が関連資料を読む必要があります。

ビジネスシナリオの説明

電子商取引における配送先管理を例として、比較的単純なビジネス シナリオを選択します。ユーザーがモバイル端末で商品を購入する場合、配送先住所を管理する必要があります。プロジェクトでは、モバイル端末向けにいくつかの API インターフェイスが提供されます。アクセス:配送先住所の追加、配送先住所の削除、配送先住所の変更、デフォルトの配送先住所設定、配送先住所リストのクエリ、単一の配送先住所のクエリ、その他のインターフェース。

ビルドの制約

OK、これはセットアップされた非常に基本的なビジネス シナリオです。もちろん、API 操作の種類に関係なく、いくつかのルールが含まれています:

配送先住所を追加:

入力パラメータ:

ユーザーID###### 配送先住所エンティティ情報

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所の必須フィールドを空にすることはできません

ユーザーがまだ配送先住所を持っていない場合、その配送先住所は作成時にデフォルトの配送先住所として設定されます —

配送先住所を削除:

入力パラメータ:

ユーザーID###### 配送先住所 ID

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所を空にすることはできません。配送先住所は存在します。

この配送先住所がユーザーの配送先住所かどうかを確認します

この配送先住所がデフォルトの配送先住所であるかどうかを確認します。デフォルトの配送先住所である場合、削除できません

配送先を変更する:###### 入力パラメータ:

ユーザーID###### 配送先住所 ID

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所を空にすることはできません。配送先住所は存在します。

この配送先住所がユーザーの配送先住所かどうかを確認します

デフォルトのアドレス設定:

入力パラメータ:

ユーザーID###### 配送先住所 ID

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所を空にすることはできません。配送先住所は存在します。

この配送先住所がユーザーの配送先住所かどうかを確認します

配送先住所リストのクエリ:

入力パラメータ:

ユーザーID###### 制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

単一の配送先住所クエリ:

入力パラメータ:

ユーザーID###### 配送先住所 ID

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所を空にすることはできません。配送先住所は存在します。

この配送先住所がユーザーの配送先住所かどうかを確認します

制約判断と技術選択

上記の制約と関数リストについて、配送先住所の追加、配送先住所の削除、配送先住所のリストの取得など、いくつかの典型的な例外処理シナリオを分析用に選択しました。

では、どのような必要な知識を蓄えておくべきでしょうか? 配送先住所関数を見てみましょう:

配送先を追加する場合、ユーザーIDと配送先実体情報の検証が必要ですが、空でないことを判定するツールはどのように選べばよいのでしょうか。従来の判断は次のとおりです:

/**

* アドレスを追加

# * @param uid

* @パラメータアドレス

* @戻る######*/

パブリック アドレス addAddress(整数 uid,アドレス アドレス){

if(null != uid){

//プロセス..###### }

null を返す;

}

上記の例では、uid が空であることだけを判断すれば問題ありませんが、次に address エンティティ内の必要な属性が空であるかどうかを判断すると、フィールドが多数ある場合に致命的になります。

では、パラメーターの入力に関してこれらの判断をどのように行うべきでしょうか? 2 つの知識ポイントを紹介します:

Guava の Preconditions クラスは、多くのパラメータ入力メソッドの判定を実装します

JSR 303 検証仕様 (現在の実装は、hibernate によって実装された比較的完全な hibernate-validator です)

この2つのレコメンド技術を活用すれば、パラメータ入力の判断がより簡単になります。不必要なワークロードを大幅に削減できる、これらの成熟したテクノロジと jar ツールキットを全員が使用することをお勧めします。ビジネス ロジックに集中する必要があるだけです。これらの入力判断によってさらに時間が遅れるのではなく。

Java 例外をエレガントに設計する方法

ドメインの紹介

プロジェクト シナリオによれば、2 つのドメイン モデルが必要です。1 つはユーザー エンティティ、もう 1 つはアドレス エンティティです。

アドレスドメインは次のとおりです:

@実在物###### @データ###### パブリック クラス アドレス {

@Id

@GeneratedValue

プライベート整数 ID;

private String 州;//province

private String city;//city

private String County;//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)

private Set

アドレス;

}

上記はモデルの関係であり、ユーザーと配送先住所の関係は 1 対 n の関係です。上記の @Data は lombok という Setter メソッドと Getter メソッドを自動生成するツールを使用していて非常に便利なので、興味のある方はご自身で勉強してみてください。

ダオの紹介

データ接続層には、 spring-data-jpa フレームワークを使用します。このフレームワークでは、フレームワークによって提供されるインターフェイスを継承し、規則に従ってメソッドに名前を付けるだけで、必要なデータベース操作を完了できます。

ユーザーデータベースの操作は次のとおりです:

@リポジトリ

パブリック インターフェイス IUserDao は JpaRepository を拡張します {

}

配送先住所の操作は次のとおりです:

@リポジトリ

パブリック インターフェイス IAddressDao は JpaRepository を拡張します {

}

読者がお分かりのとおり、DAO は JpaRepository を継承するだけで済み、基本的な CURD やその他の操作を完了するのにすでに役立っています。spring-data プロジェクトについて詳しく知りたい場合は、spring の公式ドキュメントを参照してください。異常についての研究。

サービス例外設計

OK、ようやく焦点に到達しました。配送先住所の追加、配送先住所の削除、配送先住所リストの取得など、サービスのいくつかの操作を完了する必要があります。

まずサービス インターフェイス定義を見てください:

パブリック インターフェイス IAddressService {

/**

* 配送先住所を作成します

* @param uid

* @パラメータアドレス

* @戻る######*/

アドレス createAddress(Integer uid,Address address);

/**

* 配送先住所を削除

* @param uid

* @param 援助

*/

void deleteAddress(整数 uid,整数補助);

/**

* ユーザーのすべての配送先アドレスをクエリします

* @param uid

* @戻る######*/

リスト<アドレス> listAddresses(Integer uid);

}

実装に焦点を当てましょう:

配送先住所を追加

まず、以前にコンパイルした制約を見てみましょう:

入力パラメータ:

ユーザーID###### 配送先住所エンティティ情報

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

配送先住所の必須フィールドを空にすることはできません

ユーザーがまだ配送先住所を持っていない場合、配送先住所は作成時にデフォルトの配送先住所として設定されます

まず、次のコード実装を見てみましょう:

@オーバーライド###### public Address createAddress(Integer uid, Address address) {

//============ 以下は制約です ==============

//1. ユーザー ID を空にすることはできません。このユーザーは存在します。

Preconditions.checkNotNull(uid);

ユーザー user = userDao.findOne(uid);

if(null == ユーザー){

throw new RuntimeException("現在のユーザーが見つかりません!");

}

//2. 配送先住所の必須フィールドを空にすることはできません

BeanValidators.validateWithException(バリデーター, アドレス);

//3. ユーザーがまだ配送先住所を持っていない場合、配送先住所が作成されると、デフォルトの配送先住所

に設定されます。 if(ObjectUtils.isEmpty(user.getAddresses())){

address.setIsDefault(true);

}

//============ 通常実行されるビジネスロジックは以下の通り ==============

address.setUser(ユーザー);

アドレスの結果 = addressDao.save(アドレス);

結果を返す;

}

このうち、上記の 3 つの制約は完了しており、3 つの制約が満たされている場合は通常のビジネス ロジックを実行できますが、満たされていない場合は例外がスローされます (通常は、実行時例外 - ここでは RuntimeException をスローすることをお勧めします)。

上記で使用した次のテクノロジーを紹介します:

Preconfitions.checkNotNull(T t) は Guava の com.google.common.base.Preconditions を使用して判定されます。サービスでは多くの検証が使用されるため、Preconfitions を静的なインポート メソッドに変更することをお勧めします:

1インポート静的 com.google.common.base.Preconditions.checkNotNull;

もちろん、Guava の github にある手順でも、この方法で使用することを推奨しています。

BeanValidators.validateWithException(バリデーター, アドレス);

これは、hibernate によって実装された jsr 303 仕様を使用して行われます。検証する必要があるバリデーターとエンティティを渡す必要があります。したがって、バリデーターの取得方法は次のようになります:

@構成###### パブリック クラス BeanConfigs {

@豆###### public javax.validation.Validator getValidator(){

新しい LocalValidatorFactoryBean() を返します;

}

}

Validator オブジェクトを取得すると、それをサービスに挿入して使用できます。 @Autowired

プライベート Validator バリデーター ;

では、BeanValidators クラスはどのように実装されるのでしょうか?実は実装は非常に簡単で、jsr303のアノテーションを判断するだけでOKです。

では、jsr 303 アノテーションはどこに書かれているのでしょうか?もちろん、これはアドレス エンティティ クラスに記述されます:

@実在物###### @Setter

@Getter

パブリック クラス アドレス {

@Id

@GeneratedValue

プライベート整数 ID;

@NotNull

private String 州;//province

@NotNull

private String city;//city

@NotNull

private String County;//District

private Boolean isDefault = false;//デフォルトのアドレスかどうか

@ManyToOne(cascade={CascadeType.ALL})

@JoinColumn(name="uid")

プライベートユーザー user;

}

判断に必要な制約を記述し、それが合理的であれば業務を実行し、データベースを運用することができます。

この部分の検証が必要である主な理由の 1 つは、検証によってダーティ データの挿入を回避できることです。読者に正式なオンライン経験があれば、そのようなことは理解できるでしょう。コード エラーは許容され、修正されますが、ダーティ データの問題が発生すると、壊滅的な惨事になる可能性があります。プログラムの問題は修正できますが、汚れたデータの外観は回復できない場合があります。このため、ビジネス ロジック操作を実行する前にサービス内の制約を決定する必要があります。

ここでの判断は、ビジネスの観点から行われるビジネス ロジックの判断であり、また、多くのシナリオではさまざまなビジネス条件の制約が存在する可能性があり、要件に応じて判断するだけで済みます。

制約の概要は次のとおりです。

基本的な判定制約(null値などの基本的な判定)

エンティティ属性の制約 (jsr 303 などの基本的な判断を満たす)

ビジネス条件の制約 (要件によって提案されるさまざまなビジネス制約)

これら 3 つのポイントが満たされている場合は、次のステップに進むことができます

さて、これは基本的な判断方法の紹介です。例外設計の問題に戻りましょう。上記のコードは、適切な場所で例外を合理的に判断する方法を明確に記述しています。では、例外を合理的にスローするにはどうすればよいでしょうか?

RuntimeException をスローするだけでは、正常に例外をスローしたとみなされますか?サービス中にスローされる例外には、大きく分けて 2 つのスロー方法があると思います。 ステータス コード RumtimeException

の例外をスローします 指定されたタイプの RuntimeException をスローします

2 つの例外メソッドと比較すると、最初の例外は、すべての例外が RuntimeException 例外をスローすることを意味しますが、ステータス コードが必要です。呼び出し元は、ステータス コードに基づいてサービスがスローしたものの種類を問い合わせることができます。

2 番目のタイプの例外は、サービスでスローされる例外に対して指定された例外エラーをカスタマイズして、例外をスローすることを指します。

一般に、システムに他に特別な要件がない場合は、開発および設計中に 2 番目の方法を使用することをお勧めします。しかし、例えば基本的な判定例外はguavaが提供するクラスライブラリを利用して完全に操作することができます。 JSR 303 例外は、独自のカプセル化された例外判定クラスを使用して操作することもできます。これらの 2 つの例外は基本的な判定であり、特別な例外を指定する必要がないためです。ただし、3 番目の義務条件制約判定でスローされる例外については、指定された種類の例外をスローする必要があります。

のために ###### 1throw new RuntimeException("現在のユーザーが見つかりません!");

この必須の例外を判断するための特定の例外クラスを定義します:

パブリック クラス NotFindUserException extends RuntimeException {

public NotFindUserException() {

super("このユーザーは見つかりません");

}

public NotFindUserException(文字列メッセージ) {

スーパー(メッセージ);

}

}

次に、これを次のように変更します:

1throw new NotFindUserException("現在のユーザーが見つかりません!");

または###### 1throw new NotFindUserException();

サービス層に対する上記の変更により、コードは次のように変更されます:

@オーバーライド###### public Address createAddress(Integer uid, Address address) {

//============ 以下は制約です ==============

//1. ユーザー ID を空にすることはできません。このユーザーは存在します。

checkNotNull(uid);

ユーザー user = userDao.findOne(uid);

if(null == ユーザー){

throw new NotFindUserException("現在のユーザーが見つかりません!");

}

//2. 配送先住所の必須フィールドを空にすることはできません

BeanValidators.validateWithException(バリデーター, アドレス);

//3. ユーザーがまだ配送先住所を持っていない場合、配送先住所が作成されると、デフォルトの配送先住所

に設定されます。 if(ObjectUtils.isEmpty(user.getAddresses())){

address.setIsDefault(true);

}

//============ 通常実行されるビジネスロジックは以下の通り ==============

address.setUser(ユーザー);

アドレスの結果 = addressDao.save(アドレス);

結果を返す;

}

このようなサービスはより安定しており、理解しやすいと思われます。

配送先住所を削除:

入力パラメータ:

ユーザーID###### 配送先住所 ID

制約:

ユーザー ID を空にすることはできません。このユーザーは存在します

配送先住所を空にすることはできません。配送先住所は存在します。

この配送先住所がユーザーの配送先住所かどうかを確認します

この配送先住所がデフォルトの配送先住所であるかどうかを確認します。デフォルトの配送先住所である場合、削除できません

上記の配送先アドレスの追加と同様なので詳細は省略しますが、削除のサービス設計は以下の通りです: @Override

public void deleteAddress(整数 uid, 整数補助) {

//============ 以下は制約です ==============

//1. ユーザー ID を空にすることはできません。このユーザーは存在します。

checkNotNull(uid);

ユーザー user = userDao.findOne(uid);

if(null == ユーザー){

新しい NotFindUserException();

をスローします }

//2. 配送先住所を空にすることはできません。配送先住所は存在します

checkNotNull(援助);

アドレス address = addressDao.findOne(aid);

if(null == アドレス){

新しい NotFindAddressException();

をスローします }

//3. この配送先住所がユーザーの配送先住所かどうかを判断します

if(!address.getUser().equals(user)){

新しい NotMatchUserAddressException();

をスローします }

//4. 配送先住所がデフォルトの配送先アドレスであるかどうかを確認し、デフォルトの配送先アドレスである場合は削除できません

if(address.getIsDefault()){

新しい DefaultAddressNotDeleteException();

をスローします }

//============ 通常実行されるビジネスロジックは以下の通り ==============

addressDao.delete(アドレス);

}

NotFindUserException、NotFindAddressException、NotMatchUserAddressException、DefaultAddressNotDeleteException という 4 つの関連例外クラスが設計されており、さまざまなビジネス要件に応じてさまざまな例外がスローされます。

配送先住所リストを取得します:

入力パラメータ:

ユーザーID###### 制約:

ユーザー ID を空にすることはできません。このユーザーは存在します。

コードは次のとおりです:@Override

public List

listAddresses(Integer uid) {

//============ 以下は制約です ==============

//1. ユーザー ID を空にすることはできません。このユーザーは存在します。

checkNotNull(uid);

ユーザー user = userDao.findOne(uid);

if(null == ユーザー){

新しい NotFindUserException();

をスローします }

//============ 通常実行されるビジネスロジックは以下の通り ==============

ユーザー結果 = userDao.findOne(uid);

return result.getAddresses();

}

API例外設計

投げる方法は大きく分けて 2 つあります。 ステータス コード RumtimeException

の例外をスローします 指定されたタイプの RuntimeException をスローします

これは、サービス層の例外を設計するときに言及しましたが、サービス層の導入により、サービス層が例外をスローするときに 2 番目のスロー方法を選択しました。違いは、API 層で例外をスローするときに、次の 2 つの方法を使用する必要があることです。スローする: API 例外のタイプを指定し、関連するステータス コードを指定して、例外をスローします。この例外設計の核心は、API を呼び出すユーザーが例外をより明確に理解できるようにすることです。例外をスローするだけでなく、また、ユーザーの問い合わせを容易にするために、ステータス コードに対応する例外の詳細情報と、例外によって発生する可能性のある問題をユーザーに表示するための対応するテーブルを作成する必要もあります。 (github によって提供される API ドキュメント、WeChat によって提供される API ドキュメントなど) には、別の利点があります。ユーザーがプロンプト メッセージをカスタマイズする必要がある場合、返されたステータス コードに基づいてプロンプトを変更できるということです。

API 検証の制約

まず、API の設計では、dto オブジェクトが存在する必要があります。このオブジェクトは呼び出し元との通信とデータ転送を担当し、dto->domain が操作のためにサービスに渡されます。これは支払われる必要があります。第二に、ポイントは、基本的な判定 (null 判定) と jsr 303 検証を必要とする前述のサービスに加えて、API 層も関連する検証を実行する必要があります。検証が失敗した場合は、呼び出し元に直接返されて通知されます。呼び出しが失敗したため、呼び出しが失敗してはいけないということ 違法なデータでサービスにアクセスすると、読者は少し混乱するかもしれません サービスが検証されていないのに、なぜ API 層を検証する必要があるのでしょうか?ここでは、プログラミングにおけるマーフィーの法則という概念を設計しています。API 層でのデータ検証を怠ると、不正なデータがサービス層に持ち込まれ、ダーティなデータがデータベースに保存される可能性があります。

したがって、厳密なプログラミングの核心は、受信したデータが合法であると決して信じないことです。

API例外設計

API レイヤーの例外を設計するときは、上で述べたように、エラー コードとエラー メッセージを指定する必要があります。その後、次のように設計して、一般的な API スーパー クラスの例外を提供できます。他のさまざまな API 例外は、このスーパー クラスから継承されます:

public class ApiException extends RuntimeException {

protected 長いエラーコード ;

保護されたオブジェクト データ ;

public ApiException(Long errorCode,String message,Object data,Throwable e){

スーパー(メッセージ,e);

this.errorCode = エラーコード ;

this.data = データ ;

}

public ApiException(長いエラーコード、文字列メッセージ、オブジェクトデータ){

this(errorCode,message,data,null);

}

public ApiException(長いエラーコード、文字列メッセージ){

this(errorCode,message,null,null);

}

public ApiException(String message,Throwable e){

this(null,メッセージ,null,e);

}

public ApiException(){

}

public ApiException(Throwable e){

スーパー(e);

}

public Long getErrorCode() {

エラーコードを返します;

}

public void setErrorCode(Long errorCode) {

this.errorCode = errorCode;

}

public Object getData() {

データを返す;

}

public void setData(オブジェクトデータ) {

this.data = データ;

}

}

その後、api 層の例外:ApiDefaultAddressNotDeleteException、ApiNotFindAddressException、ApiNotFindUserException、ApiNotMatchUserAddressException をそれぞれ決定します。

以下の例を削除することはできません:

public class ApiDefaultAddressNotDeleteException extends ApiException {

public ApiDefaultAddressNotDeleteException(文字列メッセージ) {

super(AddressErrorCode.DefaultAddressNotDeleteErrorCode, メッセージ, null);

}

}

AddressErrorCode.DefaultAddressNotDeleteErrorCode は、ユーザーに提供する必要があるコードです。コードの種類は次のとおりです:

パブリック抽象クラス AddressErrorCode {

public static Final Long DefaultAddressNotDeleteErrorCode = 10001L;//默认地址は削除できません

public static Final Long NotFindAddressErrorCode = 10002L;//ここには到達できませんでした public static Final Long NotFindUserErrorCode = 10003L;//ここには到達できません用户

public static Final Long NotMatchUserAddressErrorCode = 10004L;//用户与收货地址不一致

}

わかりました。api 層の設定はすでに完了しています。これまで以上に、AddressErrorCode のコードの種類には、発生する可能性のあるコードが保存されています。より適切な方法は、配置ファイル内に配置して管理することです。

API処理常備

api層会議はservice層を使用し、その後サービスで発生したすべての例外を処理します。まず、api層を非常によく理解する必要があり、基本的に一转発行の機能が優れています(インターフェイスパラメータ、転送サービスパラメータ、

使用する数値データ (この 3 つの基本機能) が返され、サービス パラメータを転送する方法で通常の処理が実行されます。 ここに例を追加します:

@Autowired

プライベート IAddressService addressService;

/**

*配送先住所を追加

* @param addressDTO

* @戻る######*/

@RequestMapping(メソッド = RequestMethod.POST)

public AddressDTO add(@Valid @RequestBody AddressDTO addressDTO){

アドレス address = 新しいアドレス();

BeanUtils.copyProperties(addressDTO,address);

アドレス結果;

試す {###### result = addressService.createAddress(addressDTO.getUid(), address);

}catch (NotFindUserException e){

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

}catch (例外 e){//不明错误

throw new ApiException(e);

}

AddressDTO resultDTO = 新しい AddressDTO();

BeanUtils.copyProperties(result,resultDTO);

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

結果を返しますDTO;

}

ここでの処理方法は、サービスを調整するときに、常用の種類を判断し、その後、任意のサービス常用を API 常用に変換し、次に API 常用を出力するという、一般的な常用変換方式の 1 つです。地盤も同様に処理されますが、ここでは説明しません。 api常转化

サービスを定期的に API に変換する方法と、サービスを定期的に API に変換する方法については既に解決済みであり、サービスを定期的に API に変換する方法は、通常の処理が完了するかどうかを確認していますか? 答えは否定的です。返されたデータ (json または xml) を確認するには、API を dto オブジェクト (ErrorDTO) に定期的に変換する必要があります。次の例を参照してください:

@ControllerAdvice(annotations = RestController.class)

クラス ApiExceptionHandlerAdvice {

/**

* ハンドラーによってスローされた例外を処理します。

*/

@ExceptionHandler(値 = Exception.class)

@ResponseBody

public ResponseEntity例外(例外例外、HttpServletResponse 応答) {

ErrorDTO errorDTO = 新しい ErrorDTO();

if(例外インスタンスオブ ApiException){//api 常

ApiException apiException = (ApiException)例外;

errorDTO.setErrorCode(apiException.getErrorCode());

}else{//不明异常

errorDTO.setErrorCode(0L);

}

errorDTO.setTip(例外.getMessage());

ResponseEntity responseEntity = 新しい ResponseEntity<>(errorDTO,HttpStatus.valueOf(response.getStatus()));

応答エンティティを返す;

}

@Setter

@Getter

クラス ErrorDTO{

プライベート ロング エラーコード;

プライベート文字列のヒント;

}

}

OK、これにより、ユーザーが参照できるように API が定期的に変換された DTO オブジェクトが完了します。これは、Spring MVC が提供する特殊なセクション処理である @ControllerAdvice に使用されます。

API インターフェース呼び出し時に例外が発生した場合、ユーザーは通常のデータ形式で受信することもできます。たとえば、ユーザーが存在しない (uid が 2) が、このユーザーに対して配送先アドレスが追加されている場合、postman (Google プラグインを使用します) http リクエストをシミュレートするため) データ:

{

「エラーコード」: 10003,

"tip": "ユーザーが見つかりません"

}

以上がエレガントな Java 例外を設計するにはどうすればよいでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。