{"id":45476543,"url":"https://github.com/kyriechao/failure","last_synced_at":"2026-04-28T02:03:17.080Z","repository":{"id":339935312,"uuid":"1161371292","full_name":"KyrieChao/Failure","owner":"KyrieChao","description":"一个Spring Boot 参数验证与业务异常处理框架。专为提升开发体验而设计，支持链式调用、注解驱动以及标准的 Bean Validation 集成。","archived":false,"fork":false,"pushed_at":"2026-04-27T03:18:49.000Z","size":1768,"stargazers_count":8,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-27T05:19:30.721Z","etag":null,"topics":["annotation","fail-fast","fail-strict","failure","java","maven-central","spring-boot-3","starter","validation"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KyrieChao.png","metadata":{"files":{"readme":"README.en.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-19T03:05:07.000Z","updated_at":"2026-04-27T03:18:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/KyrieChao/Failure","commit_stats":null,"previous_names":["kyriechao/failure"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/KyrieChao/Failure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyrieChao%2FFailure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyrieChao%2FFailure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyrieChao%2FFailure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyrieChao%2FFailure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KyrieChao","download_url":"https://codeload.github.com/KyrieChao/Failure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KyrieChao%2FFailure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32362783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T20:07:02.737Z","status":"online","status_checked_at":"2026-04-28T02:00:07.250Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation","fail-fast","fail-strict","failure","java","maven-central","spring-boot-3","starter","validation"],"created_at":"2026-02-22T15:26:34.252Z","updated_at":"2026-04-28T02:03:17.074Z","avatar_url":"https://github.com/KyrieChao.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Failure Spring Boot Starter\n\n[![Java CI with Maven](https://github.com/KyrieChao/Failure/actions/workflows/ci.yml/badge.svg)](https://github.com/KyrieChao/Failure/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/KyrieChao/Failure/branch/main/graph/badge.svg)](https://codecov.io/gh/KyrieChao/Failure)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)\n[![Java 17+](https://img.shields.io/badge/Java-17+-orange.svg)](https://www.oracle.com/java/technologies/downloads/)\n[![Spring Boot 3](https://img.shields.io/badge/Spring%20Boot-3.x-brightgreen.svg)](https://spring.io/projects/spring-boot)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.kyriechao/failure-spring-boot-starter.svg)](https://central.sonatype.com/artifact/io.github.kyriechao/failure-spring-boot-starter)\n[![Release](https://jitpack.io/v/KyrieChao/Failure.svg)](https://jitpack.io/#KyrieChao/Failure)\n[![Sponsor](https://img.shields.io/badge/Sponsor-Aifadian-946ce6?style=flat-square)](https://ifdian.net/a/chao242702)\n[![Last Commit](https://img.shields.io/github/last-commit/KyrieChao/Failure?logo=git\u0026color=yellow)](https://github.com/KyrieChao/Failure/commits/main)\n[![Stars](https://img.shields.io/github/stars/KyrieChao/Failure?style=social\u0026logo=github)](https://github.com/KyrieChao/Failure/stargazers)\n\n\n[中文版本](./README.md)\n\nFailure is a lightweight, high-performance validation and business-exception framework designed for Spring Boot 3.x.\nFollowing the \"Fail Fast, Fail Strict\" philosophy, it eliminates boilerplate code and provides a type-strict, fluent\nvalidation experience.\n\n🔗 Practical Example Project: [Failure-in-Action](https://github.com/KyrieChao/Failure-in-Action)\n\n🌐 Failure Framework Guide: [KyrieChao Blogs](https://kyriechao.github.io)\n\n📊 Full Performance Report \u0026 Test Code: [Failure-Benchmark](https://github.com/KyrieChao/Benchmark)\n\n---\n\n## 🚀 Core Features\n\n- **Fluent Validation Chain**: Supports `Fail-Fast` (immediate fail) and `Fail-Strict` (collect all errors) modes.\n- **Rich Assertions**: Built-in 50+ validation methods for Objects, Strings, Numbers, Collections, Date/Time, Enums, Optionals, etc.\n- **Default Localization**: Provides out-of-the-box localized error messages (e.g., Chinese support) without manual configuration.\n- **Annotation-Driven \u0026 Type Dispatch**: Provides `@Validate` annotation and `FastValidator` interface for AOP validation; supports `TypedValidator` pattern for automatic type dispatch and decoupling.\n- **Functional Results**: Provides `Result\u003cT\u003e` monad with `map`, `flatMap`, `recover` operations.\n- **Smart Debug Snapshot**: Optionally includes invalid values in exceptions (auto-masking \u0026 truncation) when `fail-fast.debug-snapshot=true` (default: false).\n- **Smart Exception Handling**: Automatically maps business error codes to HTTP status codes, with `shadow-trace` for quick debugging.\n- **Optional Starters**: Provides optional starters (Micrometer Observability / OpenAPI springdoc) without polluting core dependencies.\n\n---\n\n## ⚡ 30-Second Comparison\n\n**No More Boilerplate, Just Fluent Flow**\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003cth width=\"50%\"\u003eTraditional \"if-throw\" Hell\u003c/th\u003e\n\u003cth width=\"50%\"\u003eFailure \"Fluent\" Style\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```java\nif(user ==null){\n    throw new BusinessException(Code.USER_NULL);\n}\nif(StringUtils.isBlank(user.getName())){\n    throw new BusinessException(Code.NAME_EMPTY);\n}\nif(user.getAge() \u003c 18){\n    throw new BusinessException(Code.TOO_YOUNG);\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```java\nFailure.begin()\n    .notNull(user, Code.USER_NULL)\n    .notBlank(user.getName(),Code.NAME_EMPTY)\n    .min(user.getAge(), 18,Code.TOO_YOUNG)\n    .fail();\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n---\n## ⚡ Performance\n\nJMH microbenchmark results (vs Hibernate Validator):\n\n![Performance Chart](docs/images/failure_benchmark_visualization.png)\n\n*Test environment \u0026 reproducible code: [Benchmark repo](https://github.com/KyrieChao/Benchmark)*\n\n---\n\n## 📚 Documentation\n\n| Document                                  | Content                                                 |\n|:------------------------------------------|:--------------------------------------------------------|\n| [Quick Start](#%EF%B8%8F-quick-start)     | Installation, basic usage, and three modes introduction |\n| [API Reference](docs/API_REFERENCE.en.md)    | Complete API list, method details, and best practices   |\n| [Configuration](#%EF%B8%8F-configuration) | application.yml configuration details                   |\n| [I18n Guide](./docs/I18N_GUIDE.md)        | Internationalization configuration and key reference    |\n| [Response Code Management](./docs/RESPONSE_CODE_MANAGEMENT.md) | Response code mapping and management scheme |\n\n---\n\n## 🛠️ Quick Start\n\n### 1. Requirements\n\n- JDK 17+\n- Spring Boot 3.2.x+\n\n### 2. Dependency\n\nThis project is published on Maven Central. Add the dependency to your `pom.xml`:\n\n```xml\n\u003c!-- Maven Central (Recommended for Production) --\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.kyriechao\u003c/groupId\u003e\n    \u003cartifactId\u003efailure-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.0\u003c/version\u003e \u003c!-- Make sure it's up to date. --\u003e\n\u003c/dependency\u003e\n```\n\n### Optional Starters (Observability / OpenAPI)\n\nFailure uses a \"core starter + optional ecosystem starters\" structure. The core starter does not hard-depend on Micrometer/springdoc; optional modules are enabled when present on the classpath.\n\n#### 1) Observability (Micrometer)\n\nEnabled automatically when `MeterRegistry` is present. Metrics:\n- `failure.validation.time` (Timer, tag: `source=chain|jsr|method`)\n- `failure.validation.count` (Counter, tags: `source=chain|jsr|method`, `result=success|fail`)\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.kyriechao\u003c/groupId\u003e\n    \u003cartifactId\u003efailure-observability-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n#### 2) OpenAPI (springdoc)\n\nEnabled automatically when springdoc `OpenAPI` type is present:\n- Adds unified error response schemas (`ErrorItem` / `ErrorResponse`)\n- Adds `400` / `422` error responses for all operations if missing\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.kyriechao\u003c/groupId\u003e\n    \u003cartifactId\u003efailure-openapi-springdoc-starter\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n---\n\n## 💡 Three Validation Modes\n\n### Mode 1: Fail-Fast (Immediate Failure)\n\n**Scenario**: Defensive programming for parameters. Stops subsequent logic immediately upon finding an invalid\nparameter.\n\n```java\n// Throws exception immediately if notBlank fails, subsequent checks will not be executed\nFailure.begin()\n    .notBlank(username, UserCode.USERNAME_REQUIRED)\n    .email(email, UserCode.EMAIL_INVALID)\n    .fail();\n```\n\n**Terminal Methods**:\n\n| Method                    | Description                                                                           |\n|:--------------------------|:--------------------------------------------------------------------------------------|\n| `.fail()`                 | Standard terminal method, throws the first exception if errors exist                  |\n| `.failNow(code, message)` | **Force Immediate Failure**, throws specified exception regardless of previous checks |\n\n```java\n// Force fail example: Permission check\nFailure.begin()\n    .notNull(user, UserCode.USER_NOT_FOUND)\n    .failNow(UserCode.PERMISSION_DENIED, \"Access Denied\")  // Throws immediately\n    .state(user.getRole() ==Role.ADMIN,UserCode.PERMISSION_DENIED)  // Will not execute\n    .fail();\n```\n\n---\n\n### Mode 2: Fail-Strict (Collect All)\n\n**Scenario**: Form submission, batch import, etc., where all errors need to be returned at once.\n\n```java\n// All validations are executed, errors are collected and thrown together\nFailure.strict()\n    .notBlank(username, UserCode.USERNAME_REQUIRED, \"Username cannot be empty\")\n    .email(email, UserCode.EMAIL_INVALID, \"Invalid email format\")\n    .min(age, 18,UserCode.AGE_TOO_YOUNG, \"Must be at least 18 years old\")\n    .failAll();  // Must use failAll()\n```\n\n**Manual Error Retrieval (No Exception)**:\n\n```java\nvar chain = Failure.strict()\n        .notBlank(username, UserCode.USERNAME_REQUIRED)\n        .email(email, UserCode.EMAIL_INVALID);\n\nif(!chain.isValid()){\nvar causes = chain.getCauses();  // Get all errors\n    return Result.fail(\"Validation failed\",causes);\n}\n```\n\n---\n\n### Mode 3: Contextual (Context Integration)\n\n**Scenario**: Used with `@Validate` annotation to decouple validation logic from business code.\n\n```java\n// Controller\n@PostMapping(\"/register\")\n@Validate(value = UserRegisterValidator.class, fast = false)  // fast=false collects all errors\npublic Result\u003c?\u003e register(@RequestBody UserRegisterDTO dto) {\n    userService.register(dto);\n    return Result.success(\"Registration successful\");\n}\n\n// Validator\n@Component\npublic class UserRegisterValidator implements FastValidator\u003cUserRegisterDTO\u003e {\n    @Override\n    public void validate(UserRegisterDTO dto, ValidationContext ctx) {\n        Failure.with(ctx)\n                .notBlank(dto.getUsername(), UserCode.USERNAME_REQUIRED)\n                .email(dto.getEmail(), UserCode.EMAIL_INVALID)\n                .verify();  // Contextual mode uses verify()\n    }\n\n    @Override\n    public Class\u003c?\u003e getSupportedType() {\n        return UserRegisterDTO.class;\n    }\n}\n```\n\n**@Validate fast parameter**:\n\n| fast Value       | Behavior                            | Scenario             |\n|:-----------------|:------------------------------------|:---------------------|\n| `true` (Default) | Stops immediately after first error | Performance priority |\n| `false`          | Executes all validation rules       | Show all errors      |\n\n---\n\n### 4.4 Exception Handling \u0026 JSR-303 Compatibility\n\nThe framework provides built-in `FailFastExceptionHandler`, which not only handles its own business exceptions but also\nperfectly integrates with Spring's native JSR-303 (`@Valid` / `@Validated`) validation.\n\n**Features**:\n\n- **Unified Format**: Whether it is an exception thrown by `Failure` or triggered by `@NotNull`, the final response\n  format is completely consistent.\n- **Mode Adaptation**: The `fast` attribute of the `@Validate` annotation also applies to JSR-303 exceptions.\n    - `fast=true` (Default): Even if Hibernate Validator throws multiple errors, only the first one is returned in the\n      response.\n    - `fast=false`: Returns all errors collected by JSR-303.\n\nTo customize, inherit `FailFastExceptionHandler`:\n\n```java\n\n@RestControllerAdvice\npublic class CustomExceptionHandler extends FailFastExceptionHandler {\n\n    @Override\n    @ExceptionHandler(Business.class)\n    public ResponseEntity\u003c?\u003e handleBusinessException(Business e) {\n        // Custom response format\n        Map\u003cString, Object\u003e body = new HashMap\u003c\u003e();\n        body.put(\"success\", false);\n        body.put(\"errorCode\", e.getResponseCode().getCode());\n        body.put(\"errorMessage\", e.getResponseCode().getMessage());\n        body.put(\"detail\", e.getDetail());\n        return ResponseEntity.badRequest().body(body);\n    }\n}\n```\n\n---\n\n## 🎛️ Flow Control \u0026 Lazy Evaluation\n\n### Dynamic Skip (when)\n\nControl whether to execute subsequent validations dynamically.\n\n```java\nFailure.begin()\n    .when(isVip)                // If not VIP\n    .check(vipRule)             // This line will be skipped\n    .when(true)                 // Resume execution\n    .check(commonRule);         // Continue execution\n```\n\n### Lazy Evaluation (defer)\n\nExecute expensive validation logic (via Supplier) only when strictly necessary. If previous validations failed (\nFail-Fast) or were skipped, the supplier will not be executed.\n\n```java\nFailure.begin()\n    .notNull(userId)// Query DB only if userId is not null\n    .defer(() -\u003edbService\n    .isUserActive(userId),UserCode.USER_INACTIVE);\n```\n\n### Lazy invalidValue snapshot (Supplier)\n\nWhen the invalid value snapshot is expensive (e.g., serialization / masking), use the Supplier overload so it is computed only on failure and only when debug snapshot is enabled.\n\n```java\nFailure.begin()\n    .check(user != null, UserCode.USER_NULL, \"User required\", () -\u003e user)\n    .fail();\n```\n\n### Stop on Failure (stopOnFail)\n\nStops subsequent checks if there are any errors (even in strict mode), until `resume()` is called. Essential for\npreventing NPE.\n\n```java\nFailure.strict()\n    .notNull(user, UserCode.REQUIRED)\n    .stopOnFail()      // Stop if user is null\n    .defer(() -\u003euser.isAdmin(),UserCode.NO_PERMISSION); // Safe access\n```\n\n---\n\n## 🔀 Logical Operations (OR)\n\nThe `or()` operator is supported for \"Condition A OR Condition B\" scenarios.\n\n```java\n// Example: User is either ADMIN OR has READ permission\nFailure.begin()\n    .equals(role, Role.ADMIN)       // Condition A: Is Admin\n    .or()                           // OR\n    .hasPermission(user, \"READ\")    // Condition B: Has Read Permission\n    .failNow(UserCode.NO_PERMISSION); // Throws if neither A nor B is satisfied\n```\n\nNote: `or()` only applies to the immediately adjacent conditions. The default logic for chain calls is `AND`.\n`A.or().B.C` is equivalent to `(A || B) \u0026\u0026 C`.\n\n---\n\n## ⚙️ Configuration\n\nConfigure framework behavior in `application.yml`:\n\n```yaml\nfail-fast:\n  shadow-trace: true   # Include class name and line number of the validation point in exception stack trace\n  debug-snapshot: true # Enable debug snapshot to include invalid values (default: false)\n  verbose: true        # Include detailed errors list in multi-error response\n  trace-id:\n    enabled: true\n    header-name: X-Trace-Id\n    generate-if-missing: true\n    response-header: true\n    response-header-name: X-Trace-Id\n  reactive:\n    context-first: true # WebFlux recommended: prefer Reactor Context over ThreadLocal (default: false)\n  code-mapping:\n    http-status:\n      40001: 400       # Error Code 40001 -\u003e HTTP 400\n      40100: 401\n    groups:\n      auth: [ \"40100..40199\" ]      # Range mapping\n      business: [ \"40000..40099\" ]\n```\n\n### WebFlux Context-First (Recommended)\n\nIn WebFlux (reactive) applications, execution may hop across threads and ThreadLocal may lose request context. Enabling `fail-fast.reactive.context-first=true` makes the framework read `traceId/scene/shadow-trace` from Reactor Context first, then fall back to ThreadLocal for compatibility.\n\n### Custom default rules (ErrorPolicy)\n\nFor advanced customization of default response code, default detail generation, and whether to capture invalid values, provide an `ErrorPolicy` Spring bean.\n\n---\n\n## 📖 More Documentation\n\n- **[API_REFERENCE.en.md](docs/API_REFERENCE.en.md)** - Complete API Reference, Design Patterns\n- **[Failure-in-Action](https://github.com/KyrieChao/Failure-in-Action)** - Live Demo Project\n\n---\n## ☕ Support the Author\n\nIf you find this project helpful, consider [supporting me on Aifadian](https://ifdian.net/a/chao242702) to help maintain Failure.\n\nOr simply give it a ⭐ Star to help more people discover this project!\n\n---\n## 🤝 Contributing\n\nIssues and Pull Requests are welcome! Please:\n- Ensure `mvn test` passes.\n- Keep coverage above the JaCoCo thresholds (default: 80%+).\n- Follow the existing code style.\n\n## 📄 License\n\nApache License 2.0 - See [LICENSE](LICENSE) for details.\n\n---\n**Author**: [KyrieChao](https://github.com/KyrieChao)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyriechao%2Ffailure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkyriechao%2Ffailure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkyriechao%2Ffailure/lists"}