Làm thế nào tôi có thể vượt qua một bộ sưu tập các ngoại lệ như một nguyên nhân gốc rễ?


52

Một số phương pháp, myMethod gọi một số thực thi song song và chờ kết thúc của chúng.

Những thực thi song song có thể kết thúc với ngoại lệ. Vì vậy, myMethodcó được một danh sách ngoại lệ.

Tôi muốn vượt qua danh sách ngoại lệ là nguyên nhân gốc, nhưng nguyên nhân gốc có thể chỉ là một ngoại lệ duy nhất. Chắc chắn tôi có thể tạo ngoại lệ của riêng mình để đạt được những gì tôi muốn, nhưng tôi muốn biết liệu Java, Spring hay Spring Batch có thứ gì đó giống như thế này không.


3
.NET có AggregateExceptionmột danh sách các trường hợp ngoại lệ. Ý tưởng đó cũng nên được áp dụng cho Java.
usr

Câu trả lời:


49

Tôi không chắc chắn tôi đã làm điều đó (mặc dù được cung cấp JavaDoc tôi không thể cho bạn biết lý do tại sao tôi ngần ngại), nhưng có một danh sách các trường hợp ngoại lệ bị loại bỏ Throwablemà bạn có thể thêm vào thông qua addSuppressed. JavaDoc dường như không nói rằng điều này chỉ dành cho JVM để sử dụng trong tài nguyên dùng thử:

Áp dụng ngoại lệ được chỉ định cho các ngoại lệ đã bị loại bỏ để cung cấp ngoại lệ này. Phương thức này an toàn cho luồng và thường được gọi (tự động và ngầm) bởi câu lệnh try-with-resource.

Hành vi triệt tiêu được kích hoạt trừ khi bị vô hiệu hóa thông qua một hàm tạo. Khi triệt tiêu bị vô hiệu hóa, phương thức này không làm gì khác hơn là xác nhận đối số của nó.

Lưu ý rằng khi một ngoại lệ gây ra ngoại lệ khác, ngoại lệ đầu tiên thường được bắt và sau đó ngoại lệ thứ hai được đưa ra để đáp lại. Nói cách khác, có một mối liên hệ nhân quả giữa hai ngoại lệ. Ngược lại, có những tình huống hai trường hợp ngoại lệ độc lập có thể được ném vào các khối mã anh chị em, đặc biệt là trong khối thử của câu lệnh try-with-resource và khối cuối cùng do trình biên dịch tạo ra sẽ đóng tài nguyên. Trong những tình huống này, chỉ một trong những ngoại lệ được ném có thể được truyền bá. Trong câu lệnh try-with-resource, khi có hai ngoại lệ như vậy, ngoại lệ bắt nguồn từ khối thử được lan truyền và ngoại lệ từ khối cuối cùng được thêm vào danh sách các ngoại lệ bị loại trừ bởi ngoại lệ từ khối thử. Như một ngoại lệ làm thư giãn ngăn xếp,

Một ngoại lệ có thể đã loại bỏ các ngoại lệ trong khi cũng được gây ra bởi một ngoại lệ khác. Có hay không một ngoại lệ có nguyên nhân được biết đến về mặt ngữ nghĩa tại thời điểm tạo ra nó, không giống như liệu một ngoại lệ có ngăn chặn các ngoại lệ khác thường chỉ được xác định sau khi ném ngoại lệ hay không.

Lưu ý rằng mã viết của lập trình viên cũng có thể tận dụng việc gọi phương thức này trong các tình huống có nhiều ngoại lệ anh chị em và chỉ có thể truyền bá một.

Lưu ý rằng đoạn cuối, có vẻ phù hợp với trường hợp của bạn.


[...] có hay không một ngoại lệ sẽ loại bỏ các ngoại lệ khác [...] thường chỉ được xác định sau khi ném ngoại lệ. Tôi tưởng tượng điều này sẽ không xảy ra khi nhiều trường hợp ngoại lệ bị triệt tiêu được thu thập từ các lần chạy song song.
GOTO 0

24

Các ngoại lệ và nguyên nhân của chúng luôn chỉ là 1: 1: bạn có thể ném một ngoại lệ và mỗi ngoại lệ chỉ có thể có một nguyên nhân (một lần nữa có thể có một nguyên nhân ...).

Đó có thể được coi là một lỗi thiết kế, đặc biệt là khi xem xét hành vi đa luồng như bạn mô tả.

Đó là một trong những lý do tại sao Java 7 được thêm vào addSuppressed có thể ném được, về cơ bản có thể gắn một lượng ngoại lệ tùy ý vào một ngoại lệ khác (động lực chính khác là thử tài nguyên cần một cách để xử lý các ngoại lệ trong khối cuối cùng mà không âm thầm giảm họ).

Vì vậy, về cơ bản khi bạn có 1 ngoại lệ khiến quy trình của bạn thất bại, bạn thêm một ngoại lệ đó là nguyên nhân của ngoại lệ cấp cao hơn và nếu bạn có thêm ngoại lệ, thì bạn thêm chúng vào quy trình ban đầu addSuppressed. Ý tưởng là ngoại lệ đầu tiên "đè nén" những người khác trở thành thành viên của "chuỗi ngoại lệ thực sự".

Mã mẫu:

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}

4
Tôi sẽ không làm theo cách bạn đề xuất, trừ khi một trong những trường hợp ngoại lệ thực sự có thể được gọi là "chính". Nếu bạn chỉ tùy ý chọn một trong các ngoại lệ là ngoại lệ chính và các ngoại lệ khác bị loại bỏ, bạn đang mời người gọi bỏ qua các ngoại lệ bị chặn và chỉ báo cáo ngoại lệ chính - ngay cả khi ngoại lệ "chính" là một ngoại lệ TypoInUserInputException và một trong những cái bị loại bỏ là một DatabaseCorruptedException.
Ilmari Karonen

1
... Thay vào đó, tôi muốn đánh dấu tất cả các trường hợp ngoại lệ cơ bản là bị đàn áp bởi các SomethingWentWrongException, và cho rằng ngoại trừ một thông báo chỉ rõ rằng một hoặc nhiều trường hợp ngoại lệ ức chế nên làm theo, ví dụ như một cái gì đó như " X ra khỏi Y nhiệm vụ thất bại, xem danh sách của những thất bại dưới đây ".
Ilmari Karonen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.