diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index a9ce3811..f5c01c89 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -3,8 +3,17 @@
 Thank you for your interest in and support of the Go-Spring project!
 
 To cultivate an open, respectful, inclusive, and professional community, we ask that all participants follow this Code
-of Conduct in every interaction—whether reporting issues, submitting code, engaging in discussions, or contributing in
-any other way.
+of Conduct in every interaction—whether reporting issues, submitting code, engaging in discussions, or participating in
+any official project activity.
+
+## Scope
+
+This Code of Conduct applies to all interactions related to the Go-Spring project, including but not limited to:
+
+* Issues and pull requests on the repository
+* Discussions in forums, chat channels, or mailing lists
+* Contributions to documentation, wiki, or website
+* Participation in online or offline project events
 
 ## Our Commitment
 
@@ -13,30 +22,58 @@ experience level. We value and support diversity and inclusivity within our comm
 
 ## Encouraged Behavior
 
-- Communicate with respect and courtesy.
-- Welcome and consider constructive feedback.
-- Appreciate diverse perspectives and technical choices.
-- Demonstrate patience and empathy in collaborations.
-- Contribute positively to a welcoming and supportive environment.
+* Communicate with respect and courtesy
+* Welcome and consider constructive feedback
+* Appreciate diverse perspectives and technical choices
+* Demonstrate patience and empathy in collaborations
+* Contribute positively to a welcoming and supportive environment
+
+**Examples of encouraged behavior:**
+
+* Providing constructive code reviews
+* Welcoming newcomers with guidance
+* Sharing knowledge in a respectful manner
 
 ## Unacceptable Behavior
 
-- Use of discriminatory, abusive, or offensive language or conduct.
-- Harassment, threats, or personal attacks.
-- Sharing explicit content or inappropriate links.
-- Deliberate disruption of constructive efforts.
-- Impersonation or violation of personal privacy.
+* Use of discriminatory, abusive, or offensive language or conduct
+* Harassment, threats, or personal attacks
+* Sharing explicit content or inappropriate links
+* Deliberate disruption of constructive efforts
+* Impersonation or violation of personal privacy
+
+**Examples of unacceptable behavior:**
+
+* Using slurs or offensive language
+* Harassing contributors for opinions or technical choices
+* Posting inappropriate content or links
 
-## Enforcement
+## Reporting Violations
+
+If you witness or experience a violation of this Code of Conduct, please report it to the project maintainers via email
+or by submitting an anonymous issue.
+
+When reporting a violation, please provide:
+
+* Description of the incident
+* Links to relevant content or screenshots
+* Names of involved participants (if known)
+
+Maintainers will respond within 48 hours and handle all reports confidentially.
+
+## Enforcement and Consequences
 
 Project maintainers are responsible for upholding this Code of Conduct. They have the authority to remove, edit, or
 reject comments, commits, code, wiki edits, issues, or other contributions that violate these guidelines, and to take
 further action as necessary.
 
-## Reporting Violations
+Possible consequences for violations include:
 
-If you witness or experience a violation of this Code of Conduct, please contact the maintainers via email or submit an
-anonymous issue. All reports will be handled with discretion and taken seriously.
+* Removal of contributions or comments
+* Temporary or permanent suspension of project access
+* Reporting to relevant platform authorities
+
+Appeals can be submitted via email to the maintainers.
 
 ---
 
@@ -46,30 +83,67 @@ anonymous issue. All reports will be handled with discretion and taken seriously
 
 为营造一个开放、友善、包容和专业的社区氛围,我们制定了本行为准则。无论你是报告问题、提交代码、参与讨论,还是以其他形式参与项目,都请遵循以下准则。
 
+## 适用范围
+
+本行为准则适用于与 Go-Spring 项目相关的所有活动,包括但不限于:
+
+* 仓库中的 Issue 和 Pull Request
+* 论坛、聊天群或邮件列表中的讨论
+* 文档、Wiki 或网站的贡献
+* 线上或线下的官方项目活动
+
 ## 我们的承诺
 
-我们承诺为每一位参与者提供一个免受骚扰、歧视和攻击性行为干扰的环境。我们欢迎来自不同背景、经验和身份的贡献者,共同建设一个多元、包容的社区。
+我们承诺为每一位参与者提供一个免受骚扰干扰的环境,无论年龄、背景、身份或经验水平。我们欢迎来自不同背景的贡献者,共同建设一个多元、包容的社区。
 
 ## 倡导的行为
 
-- 保持尊重、礼貌的沟通方式;
-- 乐于接受建设性的意见与反馈;
-- 尊重不同的观点与技术选择;
-- 在协作中体现耐心与包容;
-- 积极参与,共建积极向上的社区氛围。
+* 保持尊重、礼貌的沟通方式
+* 乐于接受建设性的意见与反馈
+* 尊重不同的观点与技术选择
+* 在协作中体现耐心与包容
+* 积极参与,共建积极向上的社区氛围
+
+**正面行为示例:**
+
+* 提供建设性的代码审查
+* 欢迎新人并提供指导
+* 尊重他人分享知识
 
 ## 不被接受的行为
 
-- 使用歧视性、侮辱性或攻击性的言语与行为;
-- 进行人身攻击、骚扰或威胁;
-- 发布淫秽内容或不当链接;
-- 蓄意干扰他人正常贡献;
-- 冒充他人或侵犯他人隐私。
+* 使用歧视性、侮辱性或攻击性的言语与行为
+* 人身攻击、骚扰或威胁他人
+* 发布淫秽内容或不当链接
+* 蓄意干扰他人正常贡献
+* 冒充他人或侵犯他人隐私
+
+**违规行为示例:**
+
+* 使用侮辱性词语或攻击性语言
+* 因观点或技术选择骚扰贡献者
+* 发布不当内容或链接
+
+## 举报违规行为
+
+若你遇到或目击违反本行为准则的情况,请通过电子邮件联系项目维护者,或通过 Issue 匿名举报。
+
+提交举报时,请尽量提供以下信息:
+
+* 事件描述
+* 相关内容链接或截图
+* 相关人员姓名(如已知)
+
+维护者将在 48 小时内回复,并确保信息保密。
+
+## 执行与后果
 
-## 执行与维护
+项目维护者有权也有责任删除、修改或拒绝任何违反行为准则的评论、提交、代码、Wiki 编辑、Issue 或其他形式的贡献内容,并在必要时采取进一步措施。
 
-项目维护者有权也有责任删除、修改或拒绝任何违反行为准则的评论、提交、代码、Wiki 编辑、Issue 或其他形式的贡献内容,必要时可采取进一步措施。
+违规可能导致的后果包括:
 
-## 如何报告问题
+* 删除贡献内容或评论
+* 暂停或永久禁用项目访问权限
+* 向相关平台报告
 
-若你遇到违反行为准则的情况,请通过电子邮件联系项目维护者,或通过 Issue 匿名举报。我们承诺认真对待每一份举报,并确保信息保密。
+如对处理结果有异议,可通过电子邮件向维护者提交申诉。
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c3deaec3..cd653b91 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,98 +1,188 @@
 # Contributing to Go-Spring
 
-First of all, thank you for your interest in and support of the Go-Spring project!
+First of all, thank you for your interest in and support of the Go-Spring project!  
+Before contributing, please read our [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
 
 We welcome all kinds of contributions, including reporting issues, improving documentation, fixing bugs, and developing
-new features. Please follow the guidelines below to contribute:
+new features. Please follow the guidelines below to contribute.
+
+## Table of Contents
+
+- [Submitting Issues](#submitting-issues)
+- [Submitting Pull Requests](#submitting-pull-requests)
+- [Branch Naming Guidelines](#branch-naming-guidelines)
+- [Local Development Environment](#local-development-environment)
+- [Testing](#testing)
+- [Contact Us](#contact-us)
 
 ## Submitting Issues
 
-- Before submitting, please search existing issues to avoid duplicates.
+- Search existing issues before submitting to avoid duplicates.
 - Provide clear reproduction steps, expected behavior, and actual results.
-- If available, include error logs and relevant environment information.
+- Include error logs and environment information if applicable.
 
 ## Submitting Pull Requests
 
 1. **Fork the repository and create a new branch**
+
    ```bash
    git checkout -b feature/your-feature-name
-   ```
+   ````
 
 2. **Maintain consistent coding style**
-    - Follow Go’s official style guidelines (use `gofmt`, `golint`, `go vet`)
-    - It’s recommended to use [`golangci-lint`](https://github.com/golangci/golangci-lint) for local linting
+
+    * Follow Go’s official style guidelines (`gofmt`, `golint`, `go vet`).
+    * Recommended: [`golangci-lint`](https://github.com/golangci/golangci-lint) for local linting.
 
 3. **Write tests**
-    - All new features must include unit tests
-    - Use Go’s built-in `testing` package, and name test files as `xxx_test.go`
 
-4. **Update documentation (if applicable)**
+    * All new features or bug fixes must include unit tests.
+    * Use Go’s `testing` package; test files should be named `xxx_test.go`.
+    * Example:
+
+      ```go
+      func TestAdd(t *testing.T) {
+          result := Add(1, 2)
+          if result != 3 {
+              t.Errorf("expected 3, got %d", result)
+          }
+      }
+      ```
+
+4. **Update documentation**
+
+    * If your changes affect usage or APIs, update README or code comments.
 
 5. **Submit and create a Pull Request**
-    - Clearly describe the purpose, changes made, and testing results
-    - Link relevant issues (if any)
+
+    * Clearly describe:
+
+        * **What**: What changes are made
+        * **Why**: Why the changes are needed
+        * **How**: How it was implemented
+        * **Testing**: How it was tested
+    * Link related issues if applicable.
+
+## Branch Naming Guidelines
+
+* `feature/xxx` – New feature
+* `fix/xxx` – Bug fix
+* `doc/xxx` – Documentation updates
+* `refactor/xxx` – Code refactoring
 
 ## Local Development Environment
 
-- Go version: Latest stable release is recommended (e.g., `go1.21+`)
-- Use Go Modules for dependency management
-- Make sure all tests pass:
+* Recommended Go version: latest stable release (e.g., `go1.21+`)
+* Use Go Modules for dependency management.
+* Make sure all tests pass before submitting:
+
   ```bash
   go test ./...
   ```
 
+## Testing
+
+* Run `go test ./...` to ensure all tests pass.
+* For examples or integration tests, provide instructions if needed.
+
 ## Contact Us
 
-If you have any questions, feel free to open an issue or join the discussion forum.
+* Open an issue on GitHub for questions or feedback.
+* Join project discussions via the community forum or chat.
 
-Thank you for contributing!
+Thank you for contributing to Go-Spring!
 
---- 
+---
 
-# Contributing to Go-Spring
+# 贡献 Go-Spring 的指南
 
 首先,感谢你关注并支持 Go-Spring 项目!
+在贡献之前,请先阅读我们的 [贡献者行为准则](CODE_OF_CONDUCT.md)。
+
+我们欢迎各种形式的贡献,包括提交 Issue、完善文档、修复 Bug、开发新功能等。请按照以下指引参与贡献。
 
-我们欢迎各种形式的贡献,包括但不限于 Issue 提交、文档完善、Bug 修复、功能开发等。请按照以下指引参与贡献:
+## 目录
+
+* [提交 Issue](#提交-issue)
+* [提交 Pull Request](#提交-pull-request)
+* [分支命名规范](#分支命名规范)
+* [本地开发环境要求](#本地开发环境要求)
+* [测试](#测试)
+* [联系我们](#联系我们)
 
 ## 提交 Issue
 
-- 在提交前,请先搜索现有的 Issue,避免重复提交。
-- 请提供清晰的复现步骤、预期行为以及实际结果。
-- 如有错误日志或运行环境信息,请一并附上。
+* 在提交前,请先搜索现有 Issue,避免重复。
+* 提供清晰的复现步骤、预期行为以及实际结果。
+* 如有错误日志或运行环境信息,请一并附上。
 
 ## 提交 Pull Request
 
 1. **Fork 仓库并创建新分支**
+
    ```bash
    git checkout -b feature/your-feature-name
    ```
 
 2. **保持一致的代码风格**
-    - 遵循 Go 官方代码规范(使用 `gofmt`、`golint`、`go vet`)
-    - 推荐使用 [`golangci-lint`](https://github.com/golangci/golangci-lint) 进行本地代码检查
+
+    * 遵循 Go 官方代码规范(使用 `gofmt`、`golint`、`go vet`)。
+    * 推荐使用 [`golangci-lint`](https://github.com/golangci/golangci-lint) 进行本地检查。
 
 3. **编写测试用例**
-    - 所有新功能必须配备单元测试
-    - 使用 Go 内置的 `testing` 包,测试文件应命名为 `xxx_test.go`
 
-4. **更新相关文档(如有变更)**
+    * 所有新功能或 Bug 修复必须配备单元测试。
+    * 使用 Go 内置 `testing` 包,测试文件命名为 `xxx_test.go`。
+    * 示例:
+
+      ```go
+      func TestAdd(t *testing.T) {
+          result := Add(1, 2)
+          if result != 3 {
+              t.Errorf("expected 3, got %d", result)
+          }
+      }
+      ```
+
+4. **更新文档**
+
+    * 如果变更影响使用或接口,请同步更新 README 或代码注释。
 
 5. **提交并创建 Pull Request**
-    - 说明 PR 的目的、变更内容、测试情况等
-    - 关联相关 Issue(如有)
+
+    * 清晰说明:
+
+        * **What**:本次修改的内容
+        * **Why**:修改原因
+        * **How**:实现方式
+        * **Testing**:测试情况
+    * 关联相关 Issue(如有)。
+
+## 分支命名规范
+
+* `feature/xxx` – 新功能
+* `fix/xxx` – Bug 修复
+* `doc/xxx` – 文档更新
+* `refactor/xxx` – 代码重构
 
 ## 本地开发环境要求
 
-- Go 版本:推荐使用最新版稳定版(如 `go1.21+`)
-- 使用 Go Modules 进行依赖管理
-- 确保测试全部通过:
+* Go 版本:推荐使用最新版稳定版(如 `go1.21+`)
+* 使用 Go Modules 管理依赖
+* 提交前确保所有测试通过:
+
   ```bash
   go test ./...
   ```
 
+## 测试
+
+* 运行 `go test ./...` 确保所有测试通过
+* 对于示例或集成测试,请提供使用说明(如适用)
+
 ## 联系我们
 
-如有疑问,欢迎通过 Issue 与我们联系,或参与项目的讨论区。
+* 可通过 GitHub Issue 提问或反馈
+* 参与项目讨论区交流
 
-感谢你的贡献!
+感谢你为 Go-Spring 做出的贡献!
diff --git a/README.md b/README.md
index f50051a8..5870b128 100644
--- a/README.md
+++ b/README.md
@@ -210,7 +210,6 @@ Go-Spring provides multiple ways to register Beans:
 
 - **`gs.Object(obj)`** - Registers an existing object as a Bean
 - **`gs.Provide(ctor, args...)`** - Uses a constructor to generate and register a Bean
-- **`gs.Register(bd)`** - Registers a complete Bean definition (suitable for low-level encapsulation or advanced usage)
 
 Example:
 
@@ -218,7 +217,6 @@ Example:
 gs.Object(&Service{})  // Register a struct instance
 gs.Provide(NewService) // Register using a constructor
 gs.Provide(NewRepo, gs.ValueArg("db")) // Constructor with parameters
-gs.Register(gs.NewBean(NewService)) // Complete definition registration
 ```
 
 ### 2️⃣ Injection Methods
diff --git a/README_CN.md b/README_CN.md
index aa26cb28..dc9d51d5 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -188,7 +188,6 @@ Go-Spring 提供多种方式注册 Bean:
 
 - **`gs.Object(obj)`** - 将已有对象注册为 Bean
 - **`gs.Provide(ctor, args...)`** - 使用构造函数生成并注册 Bean
-- **`gs.Register(bd)`** - 注册完整 Bean 定义(适合底层封装或高级用法)
 
 示例:
 
@@ -196,7 +195,6 @@ Go-Spring 提供多种方式注册 Bean:
 gs.Object(&Service{})  // 注册结构体实例
 gs.Provide(NewService) // 使用构造函数注册
 gs.Provide(NewRepo, gs.ValueArg("db")) // 构造函数带参数
-gs.Register(gs.NewBean(NewService))    // 完整定义注册
 ```
 
 ### 2️⃣ 注入方式
diff --git a/conf/bind.go b/conf/bind.go
index 6cc20dbd..a50654d4 100644
--- a/conf/bind.go
+++ b/conf/bind.go
@@ -23,22 +23,37 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/go-spring/spring-core/util"
-	"github.com/go-spring/spring-core/util/errutil"
+	"github.com/go-spring/spring-base/util"
 )
 
 var (
-	ErrNotExist      = errors.New("not exist")
-	ErrInvalidSyntax = errors.New("invalid syntax")
+	ErrNotExist      = util.FormatError(nil, "not exist")
+	ErrInvalidSyntax = util.FormatError(nil, "invalid syntax")
 )
 
-// ParsedTag represents a parsed configuration tag, including key,
-// default value, and optional custom splitter for list parsing.
+// ParsedTag represents a parsed configuration tag that encodes
+// metadata for binding configuration values from property sources.
+//
+// A tag string generally follows the pattern:
+//
+//	${key:=default}>>splitter
+//
+// - "key":        the property key used to look up a value.
+// - "default":    optional fallback value if the key does not exist.
+// - "splitter":   optional custom function name to split strings into slices.
+//
+// Examples:
+//
+//	"${db.host:=localhost}"       -> key=db.host, default=localhost
+//	"${ports:=8080,9090}>>csv"    -> key=ports, default=8080,9090, splitter=csv
+//	"${:=foo}"                    -> empty key, only default value "foo"
+//
+// The parsing logic is strict; malformed tags will result in ErrInvalidSyntax.
 type ParsedTag struct {
 	Key      string // short property key
-	Def      string // default value
-	HasDef   bool   // has default value
-	Splitter string // splitter's name
+	Def      string // default value string
+	HasDef   bool   // indicates whether a default value exists
+	Splitter string // optional splitter function name for slice parsing
 }
 
 func (tag ParsedTag) String() string {
@@ -57,46 +72,67 @@ func (tag ParsedTag) String() string {
 	return sb.String()
 }
 
-// ParseTag parses a tag string in the format `${key:=default}>>splitter`
-// into a ParsedTag struct. Returns an error if the format is invalid.
+// ParseTag parses a tag string into a ParsedTag struct.
+//
+// Supported syntax: `${key:=default}>>splitter`
+//
+// - The `${...}` block is mandatory.
+// - ":=" introduces an optional default value.
+// - ">>splitter" is optional and specifies a custom splitter.
+//
+// Example parses:
+//
+//	"${foo}"               -> Key="foo"
+//	"${foo:=bar}"          -> Key="foo", HasDef=true, Def="bar"
+//	"${foo:=bar}>>csv"     -> Key="foo", HasDef=true, Def="bar", Splitter="csv"
+//	"${:=fallback}"        -> Key="", HasDef=true, Def="fallback"
+//
+// Errors:
+//   - Returns ErrInvalidSyntax if the string does not follow the pattern.
 func ParseTag(tag string) (ret ParsedTag, err error) {
-	i := strings.LastIndex(tag, ">>")
-	if i == 0 {
-		err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax)
-		return
-	}
 	j := strings.LastIndex(tag, "}")
 	if j <= 0 {
-		err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax)
+		err = util.FormatError(ErrInvalidSyntax, "parse tag '%s' error", tag)
 		return
 	}
 	k := strings.Index(tag, "${")
 	if k < 0 {
-		err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax)
+		err = util.FormatError(ErrInvalidSyntax, "parse tag '%s' error", tag)
 		return
 	}
-	if i > j {
+	if i := strings.LastIndex(tag, ">>"); i > j {
 		ret.Splitter = strings.TrimSpace(tag[i+2:])
 	}
 	ss := strings.SplitN(tag[k+2:j], ":=", 2)
-	ret.Key = ss[0]
+	ret.Key = strings.TrimSpace(ss[0])
 	if len(ss) > 1 {
 		ret.HasDef = true
-		ret.Def = ss[1]
+		ret.Def = strings.TrimSpace(ss[1])
 	}
 	return
 }
 
-// BindParam holds binding metadata for a single configuration value.
+// BindParam holds metadata needed to bind a single configuration value
+// to a Go struct field, slice element, or map entry.
 type BindParam struct {
-	Key      string            // full key
-	Path     string            // full path
+	Key      string            // full property key
+	Path     string            // full property path
 	Tag      ParsedTag         // parsed tag
-	Validate reflect.StructTag // full field tag
+	Validate reflect.StructTag // original struct field tag for validation
 }
 
-// BindTag parses and binds the configuration tag to the BindParam.
-// It handles default values and nested key expansion.
+// BindTag parses the tag string, stores the ParsedTag in BindParam,
+// and resolves nested key expansion.
+//
+// Special cases:
+// - "${:=default}" -> Key is empty, only default is set.
+// - "${ROOT}"      -> explicitly resets Key to an empty string.
+//
+// If a BindParam already has a Key, new keys are appended hierarchically,
+// e.g. parent Key="db", tag="${host}" -> final Key="db.host".
+//
+// Errors:
+// - ErrInvalidSyntax if the tag string is malformed or empty without default.
 func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error {
 	param.Validate = validate
 	parsedTag, err := ParseTag(tag)
@@ -108,7 +144,7 @@ func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error {
 			param.Tag = parsedTag
 			return nil
 		}
-		return fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax)
+		return util.FormatError(ErrInvalidSyntax, "parse tag '%s' error", tag)
 	}
 	if parsedTag.Key == "ROOT" {
 		parsedTag.Key = ""
@@ -127,20 +163,34 @@ type Filter interface {
 	Do(i any, param BindParam) (bool, error)
 }
 
-// BindValue binds a value from properties `p` to the reflect.Value `v` of type `t`
-// using metadata in `param`. It supports primitives, structs, maps, and slices.
+// BindValue attempts to bind a property value from the property source `p`
+// into the given reflect.Value `v`, based on metadata in `param`.
+//
+// Supported binding targets:
+// - Primitive types (string, int, float, bool, etc.).
+// - Structs (recursively bound field by field).
+// - Maps (bound by iterating subkeys).
+// - Slices (bound by either indexed keys or split strings).
+//
+// Errors:
+// - Returns ErrNotExist if the property is missing without a default.
+// - Returns type conversion errors if parsing fails.
+// - Returns wrapped errors with context (path, type).
 func BindValue(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) {
 
 	if !util.IsPropBindingTarget(t) {
-		err := errors.New("target should be value type")
-		return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err)
+		err := util.FormatError(nil, "target should be value type")
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
+	// run validation if "expr" tag is defined and no prior error
 	defer func() {
 		if RetErr == nil {
 			tag, ok := param.Validate.Lookup("expr")
 			if ok && len(tag) > 0 {
-				RetErr = validateField(tag, v.Interface())
+				if RetErr = validateField(tag, v.Interface()); RetErr != nil {
+					RetErr = util.FormatError(RetErr, "validate path=%s type=%s error", param.Path, v.Type().String())
+				}
 			}
 		}
 	}()
@@ -151,8 +201,8 @@ func BindValue(p Properties, v reflect.Value, t reflect.Type, param BindParam, f
 	case reflect.Slice:
 		return bindSlice(p, v, t, param, filter)
 	case reflect.Array:
-		err := errors.New("use slice instead of array")
-		return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err)
+		err := util.FormatError(nil, "use slice instead of array")
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	default: // for linter
 	}
 
@@ -164,22 +214,25 @@ func BindValue(p Properties, v reflect.Value, t reflect.Type, param BindParam, f
 		return nil
 	}
 
+	// resolve property value (with default and references)
 	val, err := resolve(p, param)
 	if err != nil {
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
+	// try converter function first
 	if fn != nil {
 		fnValue := reflect.ValueOf(fn)
 		out := fnValue.Call([]reflect.Value{reflect.ValueOf(val)})
 		if !out[1].IsNil() {
 			err = out[1].Interface().(error)
-			return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+			return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 		}
 		v.Set(out[0])
 		return nil
 	}
 
+	// fallback: parse string into basic types
 	switch v.Kind() {
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 		var u uint64
@@ -187,41 +240,51 @@ func BindValue(p Properties, v reflect.Value, t reflect.Type, param BindParam, f
 			v.SetUint(u)
 			return nil
 		}
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		var i int64
 		if i, err = strconv.ParseInt(val, 0, 0); err == nil {
 			v.SetInt(i)
 			return nil
 		}
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	case reflect.Float32, reflect.Float64:
 		var f float64
 		if f, err = strconv.ParseFloat(val, 64); err == nil {
 			v.SetFloat(f)
 			return nil
 		}
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	case reflect.Bool:
 		var b bool
 		if b, err = strconv.ParseBool(val); err == nil {
 			v.SetBool(b)
 			return nil
 		}
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	default:
+		// treat everything else as string
 		v.SetString(val)
 		return nil
 	}
 }
 
-// bindSlice binds properties to a slice value.
+// bindSlice binds configuration values into a slice of type []T.
+//
+// Supported input formats:
+//  1. Indexed keys in the property source:
+//     e.g. "list[0]=a", "list[1]=b"
+//  2. A single delimited string:
+//     e.g. "list=a,b,c"  (split by "," or custom splitter)
+//
+// The slice is always reset (v.Set(slice)) before return,
+// even if binding fails midway.
 func bindSlice(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
 
-	et := t.Elem()
-	p, err := getSlice(p, et, param)
+	elemType := t.Elem()
+	p, err := getSlice(p, elemType, param)
 	if err != nil {
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
 	slice := reflect.MakeSlice(t, 0, 0)
@@ -232,46 +295,55 @@ func bindSlice(p Properties, v reflect.Value, t reflect.Type, param BindParam, f
 	}
 
 	for i := 0; ; i++ {
-		ev := reflect.New(et).Elem()
+		subValue := reflect.New(elemType).Elem()
 		subParam := BindParam{
 			Key:  fmt.Sprintf("%s[%d]", param.Key, i),
 			Path: fmt.Sprintf("%s[%d]", param.Path, i),
 		}
-		err = BindValue(p, ev, et, subParam, filter)
+		err = BindValue(p, subValue, elemType, subParam, filter)
 		if errors.Is(err, ErrNotExist) {
+			// stop when no more indexed elements
 			break
 		}
 		if err != nil {
-			return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+			return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 		}
-		slice = reflect.Append(slice, ev)
+		slice = reflect.Append(slice, subValue)
 	}
 	return nil
 }
 
-// getSlice retrieves and splits a string into slice elements,
-// creating a new Properties instance if necessary.
+// getSlice prepares a Properties object representing slice elements
+// derived from either:
+//
+// - Explicit indexed properties (preferred).
+// - A single delimited string property, split into multiple elements.
+//
+// Errors:
+// - ErrNotExist if property is missing and no default is provided.
+// - Unknown splitter name if specified splitter is not registered.
+// - Converter missing for non-primitive element types.
 func getSlice(p Properties, et reflect.Type, param BindParam) (Properties, error) {
 
-	// properties that defined as list.
+	// case 1: properties already defined as list (e.g. key[0], key[1]...)
 	if p.Has(param.Key + "[0]") {
 		return p, nil
 	}
 
-	// properties that defined as string and needs to split into []string.
+	// case 2: property is a single string -> split into slice
 	var strVal string
 	{
 		if p.Has(param.Key) {
 			strVal = p.Get(param.Key)
 		} else {
 			if !param.Tag.HasDef {
-				return nil, fmt.Errorf("property %q %w", param.Key, ErrNotExist)
+				return nil, util.FormatError(nil, "property %q %w", param.Key, ErrNotExist)
 			}
 			if param.Tag.Def == "" {
 				return nil, nil
 			}
 			if !util.IsPrimitiveValueType(et) && converters[et] == nil {
-				return nil, fmt.Errorf("can't find converter for %s", et.String())
+				return nil, util.FormatError(nil, "can't find converter for %s", et.String())
 			}
 			strVal = param.Tag.Def
 		}
@@ -285,17 +357,19 @@ func getSlice(p Properties, et reflect.Type, param BindParam) (Properties, error
 		arrVal []string
 	)
 
+	// split string into elements
 	if s := param.Tag.Splitter; s == "" {
 		arrVal = strings.Split(strVal, ",")
 		for i := range arrVal {
 			arrVal[i] = strings.TrimSpace(arrVal[i])
 		}
 	} else if fn, ok := splitters[s]; ok && fn != nil {
+		// use custom splitter function
 		if arrVal, err = fn(strVal); err != nil {
-			return nil, fmt.Errorf("split error: %w, value: %q", err, strVal)
+			return nil, util.FormatError(err, "split %q error", strVal)
 		}
 	} else {
-		return nil, fmt.Errorf("unknown splitter '%s'", s)
+		return nil, util.FormatError(nil, "unknown splitter '%s'", s)
 	}
 
 	r := New()
@@ -306,39 +380,54 @@ func getSlice(p Properties, et reflect.Type, param BindParam) (Properties, error
 	return r, nil
 }
 
-// bindMap binds properties to a map value.
+// bindMap binds configuration properties into a Go map[K]V.
+//
+// Example:
+//
+//	Properties:
+//	  "users.alice.age" = 20
+//	  "users.bob.age"   = 30
+//
+//	Binding into map[string]User produces:
+//	  {"alice": User{Age:20}, "bob": User{Age:30}}
+//
+// Errors:
+// - Returns error if property is missing without default.
+// - Propagates binding errors from element binding.
 func bindMap(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
 
 	if param.Tag.HasDef && param.Tag.Def != "" {
-		err := errors.New("map can't have a non-empty default value")
-		return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err)
+		err := util.FormatError(nil, "map can't have a non-empty default value")
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
-	et := t.Elem()
+	elemType := t.Elem()
 	ret := reflect.MakeMap(t)
 	defer func() { v.Set(ret) }()
 
-	// 当成默认值处理
+	// handle empty key as default value placeholder
 	if param.Tag.Key == "" {
 		if param.Tag.HasDef {
 			return nil
 		}
 	}
 
+	// ensure property exists
 	if !p.Has(param.Key) {
 		if param.Tag.HasDef {
 			return nil
 		}
-		return fmt.Errorf("property %q %w", param.Key, ErrNotExist)
+		return util.FormatError(nil, "property %q %w", param.Key, ErrNotExist)
 	}
 
+	// fetch subkeys under the current key prefix
 	keys, err := p.SubKeys(param.Key)
 	if err != nil {
-		return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
 	for _, key := range keys {
-		e := reflect.New(et).Elem()
+		subValue := reflect.New(elemType).Elem()
 		subKey := key
 		if param.Key != "" {
 			subKey = param.Key + "." + key
@@ -347,26 +436,44 @@ func bindMap(p Properties, v reflect.Value, t reflect.Type, param BindParam, fil
 			Key:  subKey,
 			Path: param.Path,
 		}
-		if err = BindValue(p, e, et, subParam, filter); err != nil {
+		if err = BindValue(p, subValue, elemType, subParam, filter); err != nil {
 			return err // no wrap
 		}
-		ret.SetMapIndex(reflect.ValueOf(key), e)
+		ret.SetMapIndex(reflect.ValueOf(key), subValue)
 	}
 	return nil
 }
 
-// bindStruct binds properties to a struct value.
+// bindStruct binds configuration properties into a struct.
+//
+// Example:
+//
+//	type Config struct {
+//	    Host string `value:"${db.host:=localhost}"`
+//	    Port int    `value:"${db.port:=3306}"`
+//	}
+//
+//	With properties:
+//	  db.host=127.0.0.1
+//	Result:
+//	  Config{Host:"127.0.0.1", Port:3306}
+//
+// Errors:
+// - Invalid syntax in tag.
+// - Binding or conversion failures in nested fields.
+// - Infinite recursion is avoided for embedded pointer structs.
 func bindStruct(p Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error {
 
 	if param.Tag.HasDef && param.Tag.Def != "" {
-		err := errors.New("struct can't have a non-empty default value")
-		return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err)
+		err := util.FormatError(nil, "struct can't have a non-empty default value")
+		return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 	}
 
 	for i := range t.NumField() {
 		ft := t.Field(i)
 		fv := v.Field(i)
 
+		// skip unexported fields
 		if !fv.CanInterface() {
 			continue
 		}
@@ -378,12 +485,12 @@ func bindStruct(p Properties, v reflect.Value, t reflect.Type, param BindParam,
 
 		if tag, ok := ft.Tag.Lookup("value"); ok {
 			if err := subParam.BindTag(tag, ft.Tag); err != nil {
-				return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+				return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 			}
 			if filter != nil {
 				ret, err := filter.Do(fv.Addr().Interface(), subParam)
 				if err != nil {
-					return errutil.WrapError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
+					return util.FormatError(err, "bind path=%s type=%s error", param.Path, v.Type().String())
 				}
 				if ret {
 					continue
@@ -408,7 +515,16 @@ func bindStruct(p Properties, v reflect.Value, t reflect.Type, param BindParam,
 	return nil
 }
 
-// resolve returns property references processed property value.
+// resolve fetches the final string value of a property key,
+// applying default values and resolving references recursively.
+//
+// Example:
+//
+//	Properties:
+//	  "host" = "localhost"
+//	  "url"  = "http://${host}:8080"
+//
+//	resolve(url) -> "http://localhost:8080"
 func resolve(p Properties, param BindParam) (string, error) {
 	const defVal = "@@def@@"
 	val := p.Get(param.Key, defVal)
@@ -416,15 +532,35 @@ func resolve(p Properties, param BindParam) (string, error) {
 		return resolveString(p, val)
 	}
 	if p.Has(param.Key) {
-		return "", fmt.Errorf("property %s isn't simple value", param.Key)
+		return "", util.FormatError(nil, "property %q isn't simple value", param.Key)
 	}
 	if param.Tag.HasDef {
 		return resolveString(p, param.Tag.Def)
 	}
-	return "", fmt.Errorf("property %s %w", param.Key, ErrNotExist)
+	return "", util.FormatError(nil, "property %q %w", param.Key, ErrNotExist)
 }
 
-// resolveString returns property references processed string.
+// resolveString expands property references of the form ${key}
+// inside a string, recursively resolving nested expressions.
+//
+// Supported features:
+// - Nested references: e.g. "${outer${inner}}"
+// - Default values:    "${key:=fallback}"
+// - Arbitrary string concatenation around references.
+//
+// Example:
+//
+//	Properties:
+//	  "host" = "localhost"
+//	  "port" = "8080"
+//	Input:
+//	  "http://${host}:${port}"
+//	Output:
+//	  "http://localhost:8080"
+//
+// Errors:
+// - ErrInvalidSyntax if braces are unbalanced.
+// - Propagates errors from resolve().
 func resolveString(p Properties, s string) (string, error) {
 
 	// If there is no property reference, return the original string.
@@ -434,19 +570,19 @@ func resolveString(p Properties, s string) (string, error) {
 	}
 
 	var (
-		length = len(s)
-		count  = 1
-		end    = -1
+		level = 1
+		end   = -1
 	)
 
-	for i := start + 2; i < length; i++ {
+	// scan for matching closing brace, handling nested references
+	for i := start + 2; i < len(s); i++ {
 		if s[i] == '$' {
-			if i+1 < length && s[i+1] == '{' {
-				count++
+			if i+1 < len(s) && s[i+1] == '{' {
+				level++
 			}
 		} else if s[i] == '}' {
-			count--
-			if count == 0 {
+			level--
+			if level == 0 {
 				end = i
 				break
 			}
@@ -455,21 +591,24 @@ func resolveString(p Properties, s string) (string, error) {
 
 	if end < 0 {
 		err := ErrInvalidSyntax
-		return "", fmt.Errorf("resolve string %q error: %w", s, err)
+		return "", util.FormatError(err, "resolve string %q error", s)
 	}
 
 	var param BindParam
 	_ = param.BindTag(s[start:end+1], "")
 
-	s1, err := resolve(p, param)
+	// resolve the referenced property
+	resolved, err := resolve(p, param)
 	if err != nil {
-		return "", errutil.WrapError(err, "resolve string %q error", s)
+		return "", util.FormatError(err, "resolve string %q error", s)
 	}
 
-	s2, err := resolveString(p, s[end+1:])
+	// resolve the remaining part of the string
+	suffix, err := resolveString(p, s[end+1:])
 	if err != nil {
-		return "", errutil.WrapError(err, "resolve string %q error", s)
+		return "", util.FormatError(err, "resolve string %q error", s)
 	}
 
-	return s[:start] + s1 + s2, nil
+	// combine: prefix + resolved + suffix
+	return s[:start] + resolved + suffix, nil
 }
diff --git a/conf/bind_test.go b/conf/bind_test.go
index e12cc9bb..7481a5d3 100644
--- a/conf/bind_test.go
+++ b/conf/bind_test.go
@@ -25,7 +25,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 	"github.com/go-spring/spring-core/conf"
 	"github.com/spf13/cast"
 )
@@ -69,77 +69,109 @@ func TestConverter(t *testing.T) {
 		Duration time.Duration `value:"${duration:=10s}"`
 	}
 
-	t.Run("success", func(t *testing.T) {
+	t.Run("built-in types", func(t *testing.T) {
 		err := conf.New().Bind(&s)
 		assert.That(t, err).Nil()
 		assert.That(t, s.Time).Equal(time.Date(2025, 2, 1, 0, 0, 0, 0, time.UTC))
 		assert.That(t, s.Duration).Equal(10 * time.Second)
 	})
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("invalid time format", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"time": "2025-02-01M00:00:00",
 		})
 		err := p.Bind(&s)
-		assert.ThatError(t, err).Matches("unable to parse date: 2025-02-01M00:00:00")
+		assert.Error(t, err).Matches("unable to parse date: 2025-02-01M00:00:00")
 	})
 }
 
 func TestSplitter(t *testing.T) {
 
-	t.Run("success", func(t *testing.T) {
+	t.Run("split points success", func(t *testing.T) {
 		var points []image.Point
 		err := conf.New().Bind(&points, "${:=(1,2)(3,4)}>>PointSplitter")
 		assert.That(t, err).Nil()
 		assert.That(t, points).Equal([]image.Point{{X: 1, Y: 2}, {X: 3, Y: 4}})
 	})
 
-	t.Run("split error", func(t *testing.T) {
+	t.Run("split points error", func(t *testing.T) {
 		var points []image.Point
 		err := conf.New().Bind(&points, "${:=(1}>>PointSplitter")
-		assert.ThatError(t, err).Matches("split error")
+		assert.Error(t, err).Matches("split error")
 	})
 
 	t.Run("unknown splitter", func(t *testing.T) {
 		var points []image.Point
 		err := conf.New().Bind(&points, "${:=(1}>>UnknownSplitter")
-		assert.ThatError(t, err).Matches("unknown splitter 'UnknownSplitter'")
+		assert.Error(t, err).Matches("unknown splitter 'UnknownSplitter'")
+	})
+}
+
+func TestSplitterError(t *testing.T) {
+	conf.RegisterSplitter("ErrorSplitter", func(str string) ([]string, error) {
+		return nil, errors.New("splitter error")
+	})
+
+	t.Run("splitter returns error", func(t *testing.T) {
+		var strs []string
+		err := conf.New().Bind(&strs, "${strs:=a,b,c}>>ErrorSplitter")
+		assert.Error(t, err).Matches("splitter error")
 	})
 }
 
 func TestParseTag(t *testing.T) {
 
-	t.Run("normal", func(t *testing.T) {
+	t.Run("simple tag", func(t *testing.T) {
 		tag, err := conf.ParseTag("${a}")
 		assert.That(t, err).Nil()
 		assert.That(t, tag.String()).Equal("${a}")
 	})
 
-	t.Run("default", func(t *testing.T) {
+	t.Run("with default", func(t *testing.T) {
 		tag, err := conf.ParseTag("${a:=123}")
 		assert.That(t, err).Nil()
 		assert.That(t, tag.String()).Equal("${a:=123}")
 	})
 
-	t.Run("splitter", func(t *testing.T) {
+	t.Run("with splitter", func(t *testing.T) {
 		tag, err := conf.ParseTag("${a:=1,2,3}>>splitter")
 		assert.That(t, err).Nil()
 		assert.That(t, tag.String()).Equal("${a:=1,2,3}>>splitter")
 	})
 
-	t.Run("error - 1", func(t *testing.T) {
+	t.Run("missing dollar brace", func(t *testing.T) {
 		_, err := conf.ParseTag(">>splitter")
-		assert.ThatError(t, err).Matches("parse tag .* error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
 	})
 
-	t.Run("error - 2", func(t *testing.T) {
+	t.Run("unmatched braces", func(t *testing.T) {
 		_, err := conf.ParseTag("${a:=1,2,3")
-		assert.ThatError(t, err).Matches("parse tag .* error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
 	})
 
-	t.Run("error - 3", func(t *testing.T) {
+	t.Run("missing dollar sign", func(t *testing.T) {
 		_, err := conf.ParseTag("{a:=1,2,3}")
-		assert.ThatError(t, err).Matches("parse tag .* error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
+	})
+
+	t.Run("empty key with default", func(t *testing.T) {
+		tag, err := conf.ParseTag("${:=default}")
+		assert.That(t, err).Nil()
+		assert.That(t, tag).Equal(conf.ParsedTag{
+			Key:    "",
+			Def:    "default",
+			HasDef: true,
+		})
+	})
+
+	t.Run("key with special chars", func(t *testing.T) {
+		tag, err := conf.ParseTag("${key-with.dots_and_underscores:=value}")
+		assert.That(t, err).Nil()
+		assert.That(t, tag).Equal(conf.ParsedTag{
+			Key:    "key-with.dots_and_underscores",
+			Def:    "value",
+			HasDef: true,
+		})
 	})
 }
 
@@ -206,16 +238,32 @@ func TestBindParam(t *testing.T) {
 		})
 	})
 
-	t.Run("error - 1", func(t *testing.T) {
+	t.Run("invalid format", func(t *testing.T) {
 		var param conf.BindParam
 		err := param.BindTag("a:=123", "")
-		assert.ThatError(t, err).Matches("parse tag .* error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
 	})
 
-	t.Run("error - 2", func(t *testing.T) {
+	t.Run("empty tag", func(t *testing.T) {
 		var param conf.BindParam
 		err := param.BindTag("${}", "")
-		assert.ThatError(t, err).Matches("parse tag .* error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
+	})
+
+	t.Run("empty tag with default", func(t *testing.T) {
+		var param conf.BindParam
+		err := param.BindTag("${:=}", "")
+		assert.Error(t, err).Nil()
+	})
+
+	t.Run("nested key", func(t *testing.T) {
+		var param = conf.BindParam{
+			Key:  "parent",
+			Path: "Parent",
+		}
+		err := param.BindTag("${child.key:=value}", "")
+		assert.That(t, err).Nil()
+		assert.That(t, param.Key).Equal("parent.child.key")
 	})
 }
 
@@ -271,6 +319,19 @@ type UnnamedDefault struct {
 	Map  map[string]int `value:"${:=}"`
 }
 
+type AdvancedTypes struct {
+	BoolSlice   []bool          `value:"${boolSlice:=true,false,true}"`
+	IntSlice    []int           `value:"${intSlice:=1,2,3}"`
+	StringSlice []string        `value:"${stringSlice:=a,b,c}"`
+	NestedMap   map[string]Data `value:"${nestedMap}"`
+	EmptyStruct Data            `value:"${emptyStruct}"`
+}
+
+type Data struct {
+	Name string `value:"${name}"`
+	Age  int    `value:"${age}"`
+}
+
 func TestProperties_Bind(t *testing.T) {
 
 	t.Run("unnamed default", func(t *testing.T) {
@@ -284,20 +345,20 @@ func TestProperties_Bind(t *testing.T) {
 		})
 	})
 
-	t.Run("BindTag error", func(t *testing.T) {
+	t.Run("invalid tag", func(t *testing.T) {
 		var i int
 		err := conf.New().Bind(&i, "$")
-		assert.ThatError(t, err).Matches("parse tag '\\$' error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag '\\$' error: invalid syntax")
 	})
 
-	t.Run("target error - 1", func(t *testing.T) {
+	t.Run("non pointer target", func(t *testing.T) {
 		err := conf.New().Bind(5)
-		assert.ThatError(t, err).Matches("should be a ptr")
+		assert.Error(t, err).Matches("should be a pointer but int")
 	})
 
-	t.Run("target error - 1", func(t *testing.T) {
+	t.Run("pointer to pointer target", func(t *testing.T) {
 		err := conf.New().Bind(new(*int))
-		assert.ThatError(t, err).Matches("target should be value type")
+		assert.Error(t, err).Matches("target should be value type")
 	})
 
 	t.Run("validate error", func(t *testing.T) {
@@ -307,57 +368,57 @@ func TestProperties_Bind(t *testing.T) {
 		err := conf.Map(map[string]any{
 			"v": "1",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("validate failed on .* for value 1")
+		assert.Error(t, err).Matches("validate failed on .* for value 1")
 	})
 
 	t.Run("array error", func(t *testing.T) {
 		err := conf.New().Bind(new(struct {
 			Arr [3]string `value:"${arr:=1,2,3}"`
 		}))
-		assert.ThatError(t, err).Matches("use slice instead of array")
+		assert.Error(t, err).Matches("use slice instead of array")
 	})
 
-	t.Run("type error - 1", func(t *testing.T) {
+	t.Run("string to int error", func(t *testing.T) {
 		var s struct {
 			Value int `value:"${v}"`
 		}
 		err := conf.Map(map[string]any{
 			"v": "abc",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("strconv.ParseInt: parsing .*: invalid syntax")
+		assert.Error(t, err).Matches("strconv.ParseInt: parsing .*: invalid syntax")
 	})
 
-	t.Run("type error - 2", func(t *testing.T) {
+	t.Run("string to uint error", func(t *testing.T) {
 		var s struct {
 			Value uint `value:"${v}"`
 		}
 		err := conf.Map(map[string]any{
 			"v": "abc",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("strconv.ParseUint: parsing .*: invalid syntax")
+		assert.Error(t, err).Matches("strconv.ParseUint: parsing .*: invalid syntax")
 	})
 
-	t.Run("type error - 3", func(t *testing.T) {
+	t.Run("string to float error", func(t *testing.T) {
 		var s struct {
 			Value float32 `value:"${v}"`
 		}
 		err := conf.Map(map[string]any{
 			"v": "abc",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("strconv.ParseFloat: parsing .*: invalid syntax")
+		assert.Error(t, err).Matches("strconv.ParseFloat: parsing .*: invalid syntax")
 	})
 
-	t.Run("type error - 4", func(t *testing.T) {
+	t.Run("string to bool error", func(t *testing.T) {
 		var s struct {
 			Value bool `value:"${v}"`
 		}
 		err := conf.Map(map[string]any{
 			"v": "abc",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("strconv.ParseBool: parsing .*: invalid syntax")
+		assert.Error(t, err).Matches("strconv.ParseBool: parsing .*: invalid syntax")
 	})
 
-	t.Run("slice error - 1", func(t *testing.T) {
+	t.Run("slice error", func(t *testing.T) {
 		var s struct {
 			Value []int `value:"${v}"`
 		}
@@ -366,34 +427,34 @@ func TestProperties_Bind(t *testing.T) {
 				"1", "2", "a",
 			},
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("strconv.ParseInt: parsing .*: invalid syntax")
+		assert.Error(t, err).Matches("strconv.ParseInt: parsing .*: invalid syntax")
 	})
 
-	t.Run("slice error - 2", func(t *testing.T) {
+	t.Run("missing slice property", func(t *testing.T) {
 		var s struct {
 			Value []int `value:"${v}"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("property \"v\" not exist")
+		assert.Error(t, err).Matches("property \"v\" not exist")
 	})
 
-	t.Run("slice error - 3", func(t *testing.T) {
+	t.Run("missing converter for slice", func(t *testing.T) {
 		var s struct {
 			Value []image.Rectangle `value:"${v:={(1,2)(3,4)}"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("can't find converter for image.Rectangle")
+		assert.Error(t, err).Matches("can't find converter for image.Rectangle")
 	})
 
-	t.Run("map error - 1", func(t *testing.T) {
+	t.Run("map non empty default", func(t *testing.T) {
 		var s struct {
 			Value map[string]int `value:"${v:=a:b,1:2}"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("map can't have a non-empty default value")
+		assert.Error(t, err).Matches("map can't have a non-empty default value")
 	})
 
-	t.Run("map error - 2", func(t *testing.T) {
+	t.Run("map from slice", func(t *testing.T) {
 		var s struct {
 			Value map[string]int `value:"${v}"`
 		}
@@ -402,38 +463,38 @@ func TestProperties_Bind(t *testing.T) {
 				"1", "2", "3",
 			},
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("property v.0 not exist")
+		assert.Error(t, err).Matches("property \"v.0\" not exist")
 	})
 
-	t.Run("map error - 3", func(t *testing.T) {
+	t.Run("map type conflict", func(t *testing.T) {
 		var s struct {
 			Value map[string]int `value:"${v}"`
 		}
 		err := conf.Map(map[string]any{
 			"v": "a:b,1:2",
 		}).Bind(&s)
-		assert.ThatError(t, err).Matches("property conflict at path v")
+		assert.Error(t, err).Matches("property conflict at path v")
 	})
 
-	t.Run("map error - 4", func(t *testing.T) {
+	t.Run("missing map property", func(t *testing.T) {
 		var s struct {
 			Value map[string]int `value:"${v}"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("property \"v\" not exist")
+		assert.Error(t, err).Matches("property \"v\" not exist")
 	})
 
-	t.Run("struct error - 1", func(t *testing.T) {
+	t.Run("struct non empty default", func(t *testing.T) {
 		var s struct {
 			Value struct {
 				Int int
 			} `value:"${v:={123}}"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("struct can't have a non-empty default value")
+		assert.Error(t, err).Matches("struct can't have a non-empty default value")
 	})
 
-	t.Run("struct error - 2", func(t *testing.T) {
+	t.Run("unexported field", func(t *testing.T) {
 		var s struct {
 			int `value:"${v}"`
 		}
@@ -444,15 +505,15 @@ func TestProperties_Bind(t *testing.T) {
 		assert.That(t, s.int).Equal(0)
 	})
 
-	t.Run("struct error - 3", func(t *testing.T) {
+	t.Run("invalid struct tag", func(t *testing.T) {
 		var s struct {
 			Value int `value:"v"`
 		}
 		err := conf.New().Bind(&s)
-		assert.ThatError(t, err).Matches("parse tag 'v' error: invalid syntax")
+		assert.Error(t, err).Matches("parse tag 'v' error: invalid syntax")
 	})
 
-	t.Run("struct error - 4", func(t *testing.T) {
+	t.Run("embedded interface", func(t *testing.T) {
 		var s struct {
 			io.Reader
 		}
@@ -565,7 +626,59 @@ func TestProperties_Bind(t *testing.T) {
 		assert.That(t, c).Equal(expect)
 	})
 
-	t.Run("filter false", func(t *testing.T) {
+	t.Run("advanced types", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"boolSlice":   "true,false,true",
+			"intSlice":    "1,2,3",
+			"stringSlice": "a,b,c",
+			"nestedMap": map[string]any{
+				"user1": map[string]any{
+					"name": "Alice",
+					"age":  25,
+				},
+				"user2": map[string]any{
+					"name": "Bob",
+					"age":  30,
+				},
+			},
+			"emptyStruct": map[string]any{
+				"name": "Empty",
+				"age":  0,
+			},
+			"pointerField": map[string]any{
+				"name": "Pointer",
+				"age":  40,
+			},
+		})
+
+		var s AdvancedTypes
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+
+		assert.That(t, s.BoolSlice).Equal([]bool{true, false, true})
+		assert.That(t, s.IntSlice).Equal([]int{1, 2, 3})
+		assert.That(t, s.StringSlice).Equal([]string{"a", "b", "c"})
+
+		assert.That(t, s.NestedMap).Equal(map[string]Data{
+			"user1": {Name: "Alice", Age: 25},
+			"user2": {Name: "Bob", Age: 30},
+		})
+
+		assert.That(t, s.EmptyStruct).Equal(Data{Name: "Empty", Age: 0})
+	})
+
+	t.Run("empty collections with defaults", func(t *testing.T) {
+		var s struct {
+			EmptySlice []string       `value:"${emptySlice:=}"`
+			EmptyMap   map[string]int `value:"${emptyMap:=}"`
+		}
+		err := conf.New().Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.EmptySlice).Equal([]string{})
+		assert.That(t, s.EmptyMap).Equal(map[string]int{})
+	})
+
+	t.Run("filter returns false", func(t *testing.T) {
 		var param conf.BindParam
 		err := param.BindTag("${ROOT}", "")
 		assert.That(t, err).Nil()
@@ -583,7 +696,7 @@ func TestProperties_Bind(t *testing.T) {
 		assert.That(t, s.Value).Equal(3)
 	})
 
-	t.Run("filter true", func(t *testing.T) {
+	t.Run("filter returns true", func(t *testing.T) {
 		var param conf.BindParam
 		err := param.BindTag("${ROOT}", "")
 		assert.That(t, err).Nil()
@@ -601,7 +714,7 @@ func TestProperties_Bind(t *testing.T) {
 		assert.That(t, s.Value).Equal(0)
 	})
 
-	t.Run("filter error", func(t *testing.T) {
+	t.Run("filter returns error", func(t *testing.T) {
 		var param conf.BindParam
 		err := param.BindTag("${ROOT}", "")
 		assert.That(t, err).Nil()
@@ -615,7 +728,193 @@ func TestProperties_Bind(t *testing.T) {
 			funcFilter(func(i any, param conf.BindParam) (bool, error) {
 				return false, errors.New("filter error")
 			}))
-		assert.ThatError(t, err).Matches("filter error")
+		assert.Error(t, err).Matches("filter error")
 		assert.That(t, s.Value).Equal(0)
 	})
+
+	t.Run("property reference resolution", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"host": "localhost",
+			"port": "8080",
+			"url":  "http://${host}:${port}",
+		})
+
+		var s struct {
+			URL string `value:"${url}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.URL).Equal("http://localhost:8080")
+	})
+
+	t.Run("nested property reference", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"protocol": "https",
+			"host":     "example.com",
+			"port":     "443",
+			"path":     "api",
+			"url":      "${protocol}://${host}:${port}/${path}",
+		})
+
+		var s struct {
+			URL string `value:"${url}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.URL).Equal("https://example.com:443/api")
+	})
+}
+
+func TestResolveString(t *testing.T) {
+	t.Run("unbalanced braces", func(t *testing.T) {
+		_, err := conf.ParseTag("${key")
+		assert.Error(t, err).Matches("parse tag .* error: invalid syntax")
+	})
+
+	t.Run("missing property", func(t *testing.T) {
+		p := conf.New()
+
+		var s struct {
+			Value string `value:"${missing}"`
+		}
+
+		err := p.Bind(&s)
+		assert.Error(t, err).Matches("property \"missing\" not exist")
+	})
+
+	t.Run("missing property with default", func(t *testing.T) {
+		p := conf.New()
+
+		var s struct {
+			Value string `value:"${missing:=default}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Value).Equal("default")
+	})
+}
+
+func TestMapBinding(t *testing.T) {
+	t.Run("map success", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"config": map[string]any{
+				"a": 1,
+				"b": 2,
+				"c": 3,
+			},
+		})
+
+		var s struct {
+			Config map[string]int `value:"${config}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Config).Equal(map[string]int{
+			"a": 1,
+			"b": 2,
+			"c": 3,
+		})
+	})
+
+	t.Run("empty map", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"config": map[string]any{},
+		})
+
+		var s struct {
+			Config map[string]int `value:"${config}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Config).Equal(map[string]int{})
+	})
+}
+
+func TestSliceBinding(t *testing.T) {
+	t.Run("int slice from comma separated string", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"numbers": "1,2,3,4,5",
+		})
+
+		var s struct {
+			Numbers []int `value:"${numbers}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Numbers).Equal([]int{1, 2, 3, 4, 5})
+	})
+
+	t.Run("string slice with whitespace", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"values": " a , b , c ",
+		})
+
+		var s struct {
+			Values []string `value:"${values}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Values).Equal([]string{"a", "b", "c"})
+	})
+
+	t.Run("bool slice", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"flags": "true,false,true",
+		})
+
+		var s struct {
+			Flags []bool `value:"${flags}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Flags).Equal([]bool{true, false, true})
+	})
+}
+
+func TestStructBinding(t *testing.T) {
+	t.Run("nested struct", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"user": map[string]any{
+				"name": "Alice",
+				"age":  25,
+			},
+		})
+
+		var s struct {
+			User Data `value:"${user}"`
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.User).Equal(Data{
+			Name: "Alice",
+			Age:  25,
+		})
+	})
+
+	t.Run("embedded struct", func(t *testing.T) {
+		p := conf.Map(map[string]any{
+			"name": "Bob",
+			"age":  30,
+		})
+
+		var s struct {
+			Data
+		}
+
+		err := p.Bind(&s)
+		assert.That(t, err).Nil()
+		assert.That(t, s.Data).Equal(Data{
+			Name: "Bob",
+			Age:  30,
+		})
+	})
 }
diff --git a/conf/conf.go b/conf/conf.go
index 329665d2..b2e9c557 100644
--- a/conf/conf.go
+++ b/conf/conf.go
@@ -119,8 +119,6 @@ Validation:
 package conf
 
 import (
-	"errors"
-	"fmt"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -128,7 +126,8 @@ import (
 	"strings"
 	"time"
 
-	"github.com/go-spring/barky"
+	"github.com/go-spring/spring-base/barky"
+	"github.com/go-spring/spring-base/util"
 	"github.com/go-spring/spring-core/conf/reader/json"
 	"github.com/go-spring/spring-core/conf/reader/prop"
 	"github.com/go-spring/spring-core/conf/reader/toml"
@@ -144,21 +143,32 @@ var (
 
 func init() {
 
+	// built-in readers
 	RegisterReader(json.Read, ".json")
 	RegisterReader(prop.Read, ".properties")
 	RegisterReader(yaml.Read, ".yaml", ".yml")
 	RegisterReader(toml.Read, ".toml", ".tml")
 
+	// time.Time
 	RegisterConverter(func(s string) (time.Time, error) {
-		return cast.ToTimeE(strings.TrimSpace(s))
+		v, err := cast.ToTimeE(strings.TrimSpace(s))
+		if err != nil {
+			return time.Time{}, util.FormatError(err, "invalid time format: %s", s)
+		}
+		return v, nil
 	})
 
+	// time.Duration
 	RegisterConverter(func(s string) (time.Duration, error) {
-		return time.ParseDuration(strings.TrimSpace(s))
+		v, err := time.ParseDuration(strings.TrimSpace(s))
+		if err != nil {
+			return time.Duration(0), util.FormatError(err, "invalid duration format: %s", s)
+		}
+		return v, nil
 	})
 }
 
-// Reader parses []byte into nested map[string]any.
+// Reader parses raw bytes into a nested map[string]any.
 type Reader func(b []byte) (map[string]any, error)
 
 // RegisterReader registers its Reader for some kind of file extension.
@@ -168,10 +178,10 @@ func RegisterReader(r Reader, ext ...string) {
 	}
 }
 
-// Splitter splits string into []string by some characters.
+// Splitter splits a string into a slice of strings using custom logic.
 type Splitter func(string) ([]string, error)
 
-// RegisterSplitter registers a Splitter and named it.
+// RegisterSplitter registers a Splitter with a given name.
 func RegisterSplitter(name string, fn Splitter) {
 	splitters[name] = fn
 }
@@ -179,30 +189,30 @@ func RegisterSplitter(name string, fn Splitter) {
 // Converter converts a string to a target type T.
 type Converter[T any] func(string) (T, error)
 
-// RegisterConverter registers its converter for non-primitive type such as
-// time.Time, time.Duration, or other user-defined value type.
+// RegisterConverter registers a Converter for a non-primitive type such as
+// time.Time, time.Duration, or other user-defined value types.
 func RegisterConverter[T any](fn Converter[T]) {
 	t := reflect.TypeFor[T]()
 	converters[t] = fn
 }
 
-// Properties is the interface for read-only properties.
+// Properties defines the read-only interface for accessing configuration data.
 type Properties interface {
-	// Data returns key-value pairs of the properties.
+	// Data returns all key-value pairs as a flat map.
 	Data() map[string]string
-	// Keys returns keys of the properties.
+	// Keys returns all keys.
 	Keys() []string
-	// SubKeys returns the sorted sub keys of the key.
+	// SubKeys returns the sorted sub-keys of a given key.
 	SubKeys(key string) ([]string, error)
-	// Has returns whether the key exists.
+	// Has checks whether a key exists.
 	Has(key string) bool
-	// Get returns key's value.
+	// Get returns the value for a given key, with an optional default.
 	Get(key string, def ...string) string
-	// Resolve resolves string that contains references.
+	// Resolve resolves placeholders inside a string (e.g. ${key:=default}).
 	Resolve(s string) (string, error)
-	// Bind binds properties into a value.
+	// Bind binds property values into a target object (struct, map, slice, or primitive).
 	Bind(i any, tag ...string) error
-	// CopyTo copies properties into another by override.
+	// CopyTo copies properties into another instance, overriding existing values.
 	CopyTo(out *MutableProperties) error
 }
 
@@ -210,11 +220,13 @@ var _ Properties = (*MutableProperties)(nil)
 
 // MutableProperties stores the data with map[string]string and the keys are case-sensitive,
 // you can get one of them by its key, or bind some of them to a value.
+//
 // There are too many formats of configuration files, and too many conflicts between
 // them. Each format of configuration file provides its special characteristics, but
 // usually they are not all necessary, and complementary. For example, `conf` disabled
 // Java properties' expansion when reading file, but also provides similar function
 // when getting or binding properties.
+//
 // A good rule of thumb is that treating application configuration as a tree, but not
 // all formats of configuration files designed as a tree or not ideal, for instance
 // Java properties isn't strictly verified. Although configuration can store as a tree,
@@ -224,38 +236,40 @@ type MutableProperties struct {
 	*barky.Storage
 }
 
-// New creates empty *MutableProperties.
+// New creates a new empty MutableProperties instance.
 func New() *MutableProperties {
 	return &MutableProperties{
 		Storage: barky.NewStorage(),
 	}
 }
 
-// Load creates *MutableProperties from file.
+// Load creates a MutableProperties instance from a configuration file.
+// Returns an error if the file type is not supported or parsing fails.
 func Load(file string) (*MutableProperties, error) {
 	b, err := os.ReadFile(file)
 	if err != nil {
-		return nil, err
+		return nil, util.FormatError(err, "read file %s error", file)
 	}
 	ext := filepath.Ext(file)
 	r, ok := readers[ext]
 	if !ok {
-		return nil, fmt.Errorf("unsupported file type %s", ext)
+		err = util.FormatError(nil, "unsupported file type %s", ext)
+		return nil, util.FormatError(err, "read file %s error", file)
 	}
 	m, err := r(b)
 	if err != nil {
-		return nil, err
+		return nil, util.FormatError(err, "read file %s error", file)
 	}
 	p := New()
 	_ = p.merge(barky.FlattenMap(m), file)
 	return p, nil
 }
 
-// Map creates *MutableProperties from map.
-func Map(m map[string]any) *MutableProperties {
+// Map creates a MutableProperties instance directly from a map.
+func Map(data map[string]any) *MutableProperties {
 	p := New()
 	_, file, _, _ := runtime.Caller(1)
-	_ = p.merge(barky.FlattenMap(m), file)
+	_ = p.merge(barky.FlattenMap(data), file)
 	return p
 }
 
@@ -270,29 +284,32 @@ func (p *MutableProperties) merge(m map[string]string, file string) error {
 	return nil
 }
 
-// Resolve resolves string value that contains references to other
-// properties, the references are defined by ${key:=def}.
+// Resolve resolves placeholders in a string, replacing references like
+// ${key:=default} with their actual values from the properties.
 func (p *MutableProperties) Resolve(s string) (string, error) {
 	return resolveString(p, s)
 }
 
-// Bind binds properties to a value, the bind value can be primitive type,
-// map, slice, struct. When binding to struct, the tag 'value' indicates
-// which properties should be bind. The 'value' tag are defined by
-// value:"${a:=b}>>splitter", 'a' is the key, 'b' is the default value,
-// 'splitter' is the Splitter's name when you want split string value
-// into []string value.
+// Bind maps property values into the provided target object.
+// Supported targets: primitive values, maps, slices, and structs.
+// Struct binding uses the `value` tag in the form:
+//
+//	value:"${key:=default}>>splitter"
+//
+// - key: property key
+// - default: default value if key is missing
+// - splitter: registered splitter name for splitting into []string
 func (p *MutableProperties) Bind(i any, tag ...string) error {
 
 	var v reflect.Value
 	{
-		switch e := i.(type) {
+		switch refVal := i.(type) {
 		case reflect.Value:
-			v = e
+			v = refVal
 		default:
 			v = reflect.ValueOf(i)
 			if v.Kind() != reflect.Ptr {
-				return errors.New("should be a ptr")
+				return util.FormatError(nil, "should be a pointer but %T", i)
 			}
 			v = v.Elem()
 		}
@@ -300,25 +317,26 @@ func (p *MutableProperties) Bind(i any, tag ...string) error {
 
 	t := v.Type()
 	typeName := t.Name()
-	if typeName == "" { // primitive type has no name
+	if typeName == "" { // primitive types have no name
 		typeName = t.String()
 	}
 
 	s := "${ROOT}"
-	if len(tag) > 0 {
+	if len(tag) > 0 && tag[0] != "" {
 		s = tag[0]
 	}
 
 	var param BindParam
 	err := param.BindTag(s, "")
 	if err != nil {
-		return err
+		return util.FormatError(err, "bind tag '%s' error", s)
 	}
 	param.Path = typeName
 	return BindValue(p, v, t, param, nil)
 }
 
-// CopyTo copies properties into another by override.
+// CopyTo copies all properties into another MutableProperties instance,
+// overriding values if keys already exist.
 func (p *MutableProperties) CopyTo(out *MutableProperties) error {
 	rawFile := p.RawFile()
 	newfile := make(map[string]int8)
diff --git a/conf/conf_test.go b/conf/conf_test.go
index ebd441ba..75917aed 100644
--- a/conf/conf_test.go
+++ b/conf/conf_test.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 	"github.com/go-spring/spring-core/conf"
 )
 
@@ -42,32 +42,33 @@ func TestProperties_Load(t *testing.T) {
 
 	t.Run("file not exist", func(t *testing.T) {
 		_, err := conf.Load("./testdata/config/app.tcl")
-		assert.ThatError(t, err).Matches("no such file or directory")
+		assert.Error(t, err).Matches("no such file or directory")
 	})
 
 	t.Run("unsupported ext", func(t *testing.T) {
 		_, err := conf.Load("./testdata/config/app.unknown")
-		assert.ThatError(t, err).Matches("unsupported file type .unknown")
+		assert.Error(t, err).Matches("unsupported file type .unknown")
 	})
 
 	t.Run("syntax error", func(t *testing.T) {
 		_, err := conf.Load("./testdata/config/err.yaml")
-		assert.ThatError(t, err).Matches("did not find expected node content")
+		assert.Error(t, err).Matches("did not find expected node content")
 	})
 }
 
 func TestProperties_Resolve(t *testing.T) {
 
-	t.Run("success - 1", func(t *testing.T) {
+	t.Run("success", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
+
 		s, err := p.Resolve("${a.b.c[0]}")
 		assert.That(t, err).Nil()
 		assert.That(t, s).Equal("3")
 	})
 
-	t.Run("success - 2", func(t *testing.T) {
+	t.Run("success with default", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
@@ -76,7 +77,7 @@ func TestProperties_Resolve(t *testing.T) {
 		assert.That(t, s).Equal("3")
 	})
 
-	t.Run("default", func(t *testing.T) {
+	t.Run("key with default", func(t *testing.T) {
 		p := conf.New()
 		s, err := p.Resolve("${a.b.c:=123}")
 		assert.That(t, err).Nil()
@@ -86,36 +87,35 @@ func TestProperties_Resolve(t *testing.T) {
 	t.Run("key not exist", func(t *testing.T) {
 		p := conf.New()
 		_, err := p.Resolve("${a.b.c}")
-		assert.ThatError(t, err).Matches("property a.b.c not exist")
+		assert.Error(t, err).Matches("property \"a.b.c\" not exist")
 	})
 
-	t.Run("syntax error - 1", func(t *testing.T) {
+	t.Run("array property as string", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
 		_, err := p.Resolve("${a.b.c}")
-		assert.ThatError(t, err).Matches("property a.b.c isn't simple value")
+		assert.Error(t, err).Matches("property \"a.b.c\" isn't simple value")
 	})
 
-	t.Run("syntax error - 2", func(t *testing.T) {
+	t.Run("missing bracket", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
 		_, err := p.Resolve("${a.b.c")
-		assert.ThatError(t, err).Matches("resolve string .* error: invalid syntax")
+		assert.Error(t, err).Matches("resolve string .* error: invalid syntax")
 	})
 
-	t.Run("syntax error - 3", func(t *testing.T) {
+	t.Run("invalid expression", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
 		_, err := p.Resolve("${a.b.c[0]}==${a.b.c}")
-		assert.ThatError(t, err).Matches("property a.b.c isn't simple value")
+		assert.Error(t, err).Matches("property \"a.b.c\" isn't simple value")
 	})
 }
 
 func TestProperties_CopyTo(t *testing.T) {
-
 	t.Run("success", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
@@ -155,7 +155,7 @@ func TestProperties_CopyTo(t *testing.T) {
 		})
 	})
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("type conflict", func(t *testing.T) {
 		p := conf.Map(map[string]any{
 			"a.b.c": []string{"3"},
 		})
@@ -169,7 +169,7 @@ func TestProperties_CopyTo(t *testing.T) {
 		assert.That(t, s.Get("a.b.c")).Equal("3")
 
 		err := p.CopyTo(s)
-		assert.ThatError(t, err).Matches("property conflict at path a.b.c\\[0]")
+		assert.Error(t, err).Matches("property conflict at path a.b.c\\[0]")
 	})
 }
 
diff --git a/conf/expr.go b/conf/expr.go
index e71054cd..01a24d17 100644
--- a/conf/expr.go
+++ b/conf/expr.go
@@ -17,10 +17,10 @@
 package conf
 
 import (
-	"fmt"
 	"maps"
 
 	"github.com/expr-lang/expr"
+	"github.com/go-spring/spring-base/util"
 )
 
 // ValidateFunc defines a type for validation functions, which accept
@@ -44,14 +44,14 @@ func validateField(tag string, i any) error {
 	maps.Copy(env, validateFuncs)
 	r, err := expr.Eval(tag, env)
 	if err != nil {
-		return fmt.Errorf("eval %q returns error, %w", tag, err)
+		return util.FormatError(err, "eval %q returns error", tag)
 	}
 	ret, ok := r.(bool)
 	if !ok {
-		return fmt.Errorf("eval %q doesn't return bool value", tag)
+		return util.FormatError(nil, "eval %q doesn't return bool value", tag)
 	}
 	if !ret {
-		return fmt.Errorf("validate failed on %q for value %v", tag, i)
+		return util.FormatError(nil, "validate failed on %q for value %v", tag, i)
 	}
 	return nil
 }
diff --git a/conf/expr_test.go b/conf/expr_test.go
index 1aeb14ca..4c565453 100644
--- a/conf/expr_test.go
+++ b/conf/expr_test.go
@@ -19,7 +19,7 @@ package conf_test
 import (
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 	"github.com/go-spring/spring-core/conf"
 )
 
@@ -28,7 +28,7 @@ func TestExpr(t *testing.T) {
 		return i < 5
 	})
 
-	t.Run("success", func(t *testing.T) {
+	t.Run("basic function validation", func(t *testing.T) {
 		var v struct {
 			A int `value:"${a}" expr:"checkInt($)"`
 		}
@@ -40,7 +40,31 @@ func TestExpr(t *testing.T) {
 		assert.That(t, 4).Equal(v.A)
 	})
 
-	t.Run("return false", func(t *testing.T) {
+	t.Run("constant expression", func(t *testing.T) {
+		var v struct {
+			A int `value:"${a}" expr:"$ < 10"`
+		}
+		p := conf.Map(map[string]any{
+			"a": 5,
+		})
+		err := p.Bind(&v)
+		assert.That(t, err).Nil()
+		assert.That(t, 5).Equal(v.A)
+	})
+
+	t.Run("complex expression", func(t *testing.T) {
+		var v struct {
+			A int `value:"${a}" expr:"$ >= 1 && $ <= 3"`
+		}
+		p := conf.Map(map[string]any{
+			"a": 2,
+		})
+		err := p.Bind(&v)
+		assert.That(t, err).Nil()
+		assert.That(t, 2).Equal(v.A)
+	})
+
+	t.Run("validation failure", func(t *testing.T) {
 		var v struct {
 			A int `value:"${a}" expr:"checkInt($)"`
 		}
@@ -48,7 +72,7 @@ func TestExpr(t *testing.T) {
 			"a": 14,
 		})
 		err := p.Bind(&v)
-		assert.ThatError(t, err).Matches("validate failed on .* for value 14")
+		assert.Error(t, err).Matches("validate failed on .* for value 14")
 	})
 
 	t.Run("syntax error", func(t *testing.T) {
@@ -59,7 +83,7 @@ func TestExpr(t *testing.T) {
 			"a": 4,
 		})
 		err := p.Bind(&v)
-		assert.ThatError(t, err).Matches("eval .* returns error")
+		assert.Error(t, err).Matches("eval .* returns error")
 	})
 
 	t.Run("return not bool", func(t *testing.T) {
@@ -70,6 +94,29 @@ func TestExpr(t *testing.T) {
 			"a": 4,
 		})
 		err := p.Bind(&v)
-		assert.ThatError(t, err).Matches("eval .* doesn't return bool value")
+		assert.Error(t, err).Matches("eval .* doesn't return bool value")
+	})
+
+	t.Run("unregistered function", func(t *testing.T) {
+		var v struct {
+			A int `value:"${a}" expr:"unknownFunc($)"`
+		}
+		p := conf.Map(map[string]any{
+			"a": 5,
+		})
+		err := p.Bind(&v)
+		assert.Error(t, err).Matches("eval .* returns error")
+	})
+
+	t.Run("empty expression", func(t *testing.T) {
+		var v struct {
+			A int `value:"${a}" expr:""`
+		}
+		p := conf.Map(map[string]any{
+			"a": 5,
+		})
+		err := p.Bind(&v)
+		assert.That(t, err).Nil()
+		assert.That(t, 5).Equal(v.A)
 	})
 }
diff --git a/conf/reader/json/json.go b/conf/reader/json/json.go
index 654613c4..2f6ef603 100644
--- a/conf/reader/json/json.go
+++ b/conf/reader/json/json.go
@@ -18,14 +18,15 @@ package json
 
 import (
 	"encoding/json"
+
+	"github.com/go-spring/spring-base/util"
 )
 
 // Read parses []byte in the json format into map.
 func Read(b []byte) (map[string]any, error) {
 	var ret map[string]any
-	err := json.Unmarshal(b, &ret)
-	if err != nil {
-		return nil, err
+	if err := json.Unmarshal(b, &ret); err != nil {
+		return nil, util.FormatError(err, "read json error")
 	}
 	return ret, nil
 }
diff --git a/conf/reader/json/json_test.go b/conf/reader/json/json_test.go
index 1943e700..9b9f8aaf 100644
--- a/conf/reader/json/json_test.go
+++ b/conf/reader/json/json_test.go
@@ -19,17 +19,17 @@ package json
 import (
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 )
 
 func TestRead(t *testing.T) {
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("invalid json format", func(t *testing.T) {
 		_, err := Read([]byte(`{`))
-		assert.ThatError(t, err).Matches("unexpected end of JSON input")
+		assert.Error(t, err).Matches("unexpected end of JSON input")
 	})
 
-	t.Run("basic type", func(t *testing.T) {
+	t.Run("basic data types", func(t *testing.T) {
 		r, err := Read([]byte(`{
 			"empty": "",
 			"bool": false,
@@ -50,4 +50,52 @@ func TestRead(t *testing.T) {
 			"time":   "2018-02-17T15:02:31+08:00",
 		})
 	})
+
+	t.Run("nested objects", func(t *testing.T) {
+		r, err := Read([]byte(`{
+			"user": {
+				"name": "Alice",
+				"age": 30
+			},
+			"active": true
+		}`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{
+			"user": map[string]any{
+				"name": "Alice",
+				"age":  float64(30),
+			},
+			"active": true,
+		})
+	})
+
+	t.Run("arrays", func(t *testing.T) {
+		r, err := Read([]byte(`{
+			"tags": ["go", "spring"],
+			"numbers": [1, 2]
+		}`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{
+			"tags":    []any{"go", "spring"},
+			"numbers": []any{float64(1), float64(2)},
+		})
+	})
+
+	t.Run("null values", func(t *testing.T) {
+		r, err := Read([]byte(`{
+			"value": null,
+			"name": "test"
+		}`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{
+			"value": nil,
+			"name":  "test",
+		})
+	})
+
+	t.Run("empty object", func(t *testing.T) {
+		r, err := Read([]byte(`{}`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{})
+	})
 }
diff --git a/conf/reader/prop/prop.go b/conf/reader/prop/prop.go
index 98356c15..0f4e43c6 100644
--- a/conf/reader/prop/prop.go
+++ b/conf/reader/prop/prop.go
@@ -16,7 +16,10 @@
 
 package prop
 
-import "github.com/magiconair/properties"
+import (
+	"github.com/go-spring/spring-base/util"
+	"github.com/magiconair/properties"
+)
 
 // Read parses []byte in the properties format into map.
 func Read(b []byte) (map[string]any, error) {
@@ -24,7 +27,7 @@ func Read(b []byte) (map[string]any, error) {
 	p := properties.NewProperties()
 	p.DisableExpansion = true
 	if err := p.Load(b, properties.UTF8); err != nil {
-		return nil, err
+		return nil, util.FormatError(err, "read properties error")
 	}
 
 	ret := make(map[string]any)
diff --git a/conf/reader/prop/prop_test.go b/conf/reader/prop/prop_test.go
index 6eaf781b..ebfaad0a 100644
--- a/conf/reader/prop/prop_test.go
+++ b/conf/reader/prop/prop_test.go
@@ -19,14 +19,14 @@ package prop
 import (
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 )
 
 func TestRead(t *testing.T) {
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("invalid properties format", func(t *testing.T) {
 		_, err := Read([]byte(`=1`))
-		assert.ThatError(t, err).Matches(`properties: Line 1: "1"`)
+		assert.Error(t, err).Matches(`properties: Line 1: "1"`)
 	})
 
 	t.Run("basic type", func(t *testing.T) {
@@ -92,7 +92,6 @@ func TestRead(t *testing.T) {
 	})
 
 	t.Run("map with struct", func(t *testing.T) {
-
 		r, err := Read([]byte(`
 			map.k1.bool=false
 			map.k1.int=3
@@ -115,4 +114,32 @@ func TestRead(t *testing.T) {
 			"map.k2.string": "hello",
 		})
 	})
+
+	t.Run("escape sequences", func(t *testing.T) {
+		r, err := Read([]byte(`
+			key1=value\nwith\nnewlines
+			key2=value\twith\ttabs
+			key3=unicode\u0041
+		`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{
+			"key1": "value\nwith\nnewlines",
+			"key2": "value\twith\ttabs",
+			"key3": "unicodeA",
+		})
+	})
+
+	t.Run("special characters", func(t *testing.T) {
+		r, err := Read([]byte(`
+			key.with.dots=value
+			key-with-dashes=value
+			unicode_key=值
+		`))
+		assert.That(t, err).Nil()
+		assert.That(t, r).Equal(map[string]any{
+			"key.with.dots":   "value",
+			"key-with-dashes": "value",
+			"unicode_key":     "值",
+		})
+	})
 }
diff --git a/conf/reader/toml/toml.go b/conf/reader/toml/toml.go
index e290dd06..3994035e 100644
--- a/conf/reader/toml/toml.go
+++ b/conf/reader/toml/toml.go
@@ -17,6 +17,7 @@
 package toml
 
 import (
+	"github.com/go-spring/spring-base/util"
 	"github.com/pelletier/go-toml"
 )
 
@@ -24,7 +25,7 @@ import (
 func Read(b []byte) (map[string]any, error) {
 	tree, err := toml.LoadBytes(b)
 	if err != nil {
-		return nil, err
+		return nil, util.FormatError(err, "read toml error")
 	}
 	return tree.ToMap(), nil
 }
diff --git a/conf/reader/toml/toml_test.go b/conf/reader/toml/toml_test.go
index 0d0267f0..58641f85 100644
--- a/conf/reader/toml/toml_test.go
+++ b/conf/reader/toml/toml_test.go
@@ -19,14 +19,14 @@ package toml
 import (
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 )
 
 func TestRead(t *testing.T) {
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("invalid toml format", func(t *testing.T) {
 		_, err := Read([]byte(`{`))
-		assert.ThatError(t, err).Matches("parsing error: keys cannot contain { character")
+		assert.Error(t, err).Matches("parsing error: keys cannot contain { character")
 	})
 
 	t.Run("basic type", func(t *testing.T) {
diff --git a/conf/reader/yaml/yaml.go b/conf/reader/yaml/yaml.go
index 3a1507b0..8f61a212 100644
--- a/conf/reader/yaml/yaml.go
+++ b/conf/reader/yaml/yaml.go
@@ -17,15 +17,15 @@
 package yaml
 
 import (
+	"github.com/go-spring/spring-base/util"
 	"gopkg.in/yaml.v2"
 )
 
 // Read parses []byte in the yaml format into map.
 func Read(b []byte) (map[string]any, error) {
 	ret := make(map[string]any)
-	err := yaml.Unmarshal(b, &ret)
-	if err != nil {
-		return nil, err
+	if err := yaml.Unmarshal(b, &ret); err != nil {
+		return nil, util.FormatError(err, "read yaml error")
 	}
 	return ret, nil
 }
diff --git a/conf/reader/yaml/yaml_test.go b/conf/reader/yaml/yaml_test.go
index 89296225..2eb4f7a6 100644
--- a/conf/reader/yaml/yaml_test.go
+++ b/conf/reader/yaml/yaml_test.go
@@ -20,14 +20,14 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/go-spring/gs-assert/assert"
+	"github.com/go-spring/spring-base/testing/assert"
 )
 
 func TestRead(t *testing.T) {
 
-	t.Run("error", func(t *testing.T) {
+	t.Run("invalid yaml format", func(t *testing.T) {
 		_, err := Read([]byte(`{`))
-		assert.ThatError(t, err).Matches("did not find expected node content")
+		assert.Error(t, err).Matches("did not find expected node content")
 	})
 
 	t.Run("basic type", func(t *testing.T) {
diff --git a/docs/0. overview/overview.md b/docs/0. overview/overview.md
deleted file mode 100644
index 5a0e6a0d..00000000
--- a/docs/0. overview/overview.md	
+++ /dev/null
@@ -1 +0,0 @@
-todo 项目概览、目标、特性,解决了什么问题
\ No newline at end of file
diff --git a/docs/1. getting-started/getting-started.md b/docs/1. getting-started/getting-started.md
deleted file mode 100644
index 66818101..00000000
--- a/docs/1. getting-started/getting-started.md	
+++ /dev/null
@@ -1 +0,0 @@
-todo 快速开始、安装、HelloWorld、项目结构引导
\ No newline at end of file
diff --git a/docs/2. concepts/concepts.md b/docs/2. concepts/concepts.md
deleted file mode 100644
index 15bcb69a..00000000
--- a/docs/2. concepts/concepts.md	
+++ /dev/null
@@ -1 +0,0 @@
-todo 核心架构理念、DI、配置系统、生命周期管理等
\ No newline at end of file
diff --git a/docs/3. guides/guides.md b/docs/3. guides/guides.md
deleted file mode 100644
index 2bbc33ce..00000000
--- a/docs/3. guides/guides.md	
+++ /dev/null
@@ -1 +0,0 @@
-todo 常见操作,如创建服务、注入 Bean、读取配置、测试
\ No newline at end of file
diff --git a/docs/4. examples/bookman/README.md b/docs/4. examples/bookman/README.md
deleted file mode 100644
index e11d631e..00000000
--- a/docs/4. examples/bookman/README.md	
+++ /dev/null
@@ -1,77 +0,0 @@
-# BookMan
-
-[中文](README_CN.md)
-
-## 1. Directory Structure
-
-```text
-conf/           Configuration files
-log/            Log files
-public/         Static files
-src/            Source code
-  app/          Startup phase files
-    bootstrap/  Bootstrap files
-    common/     Common modules for startup
-      handlers/ Startup component handlers
-        log/    Logging component
-      httpsvr/  HTTP server module
-    controller/ Controller modules
-  biz/          Business logic modules
-    job/        Background job modules
-    service/    Business service modules
-  dao/          Data access layer
-  idl/          Interface definition files
-    http/       HTTP service interfaces
-      proto/    Generated protocol code
-  sdk/          Wrapped SDK modules
-```
-
-**Directory Structure Features**:
-
-- **Modular design** with clear separation of responsibilities.
-- **Classic structure** for easy development, management, and scalability.
-- **Maintainability** supporting continuous iteration for large-scale applications.
-
-## 2. Functionality Overview
-
-### 2.1 Bootstrap Phase Configuration Management
-
-- Fetch configuration files remotely and save them locally.
-- Register configuration refresh beans during the startup phase.
-- Related file: `src/app/bootstrap/bootstrap.go`
-
-### 2.2 Logging Component Initialization
-
-- Load and parse local configuration files during the startup phase.
-- Create logging components based on the configuration.
-- Related file: `src/app/common/handlers/log/log.go`
-
-### 2.3 HTTP Server Initialization
-
-- Create an HTTP server during the startup phase.
-- Register HTTP service routes.
-- Related file: `src/app/common/httpsvr/httpsvr.go`
-
-### 2.4 Controller Grouping and Management
-
-- Group controller methods based on functionality.
-- Independently inject and manage each sub-controller.
-- Related files:
-    - `src/app/controller/controller.go`
-    - `src/app/controller/controller-book.go`
-
-### 2.5 Dynamic Configuration Refresh
-
-- Support dynamic configuration refresh at runtime.
-- Related file: `src/biz/service/book_service/book_service.go`
-
-### 2.6 Graceful Shutdown of Background Jobs
-
-- Ensure background tasks shut down gracefully, preserving data integrity and releasing resources properly.
-- Related file: `src/biz/job/job.go`
-
-## 3. Summary
-
-This project follows modular, clear, maintainable, and extensible design principles, making it suitable for the
-development needs of medium to large-scale systems. It implements a complete and robust architecture with modules for
-bootstrapping, logging management, HTTP services, dynamic configuration refreshing, and background job handling.
\ No newline at end of file
diff --git a/docs/4. examples/bookman/README_CN.md b/docs/4. examples/bookman/README_CN.md
deleted file mode 100644
index 474f8bf3..00000000
--- a/docs/4. examples/bookman/README_CN.md	
+++ /dev/null
@@ -1,76 +0,0 @@
-# BookMan
-
-[English](README.md)
-
-## 一、目录结构
-
-```text
-conf/            配置文件目录
-log/             日志文件目录
-public/          静态文件目录
-src/             源文件目录
-  app/           启动阶段文件
-    bootstrap/   引导阶段文件
-    common/      启动阶段公共模块
-      handlers/  启动阶段组件
-        log/     日志组件
-      httpsvr/   HTTP 服务模块
-    controller/  控制器模块
-  biz/           业务逻辑模块
-    job/         后台任务模块
-    service/     业务服务模块
-  dao/           数据访问层
-  idl/           接口描述文件
-    http/        HTTP 服务接口
-      proto/     生成的协议代码
-  sdk/           封装的 SDK 文件
-```
-
-**目录结构特点**:
-
-- **模块化设计**,清晰划分各层职责。
-- **经典结构**,便于开发、管理和扩展。
-- **易于维护**,支持大规模应用持续迭代。
-
-## 二、功能描述
-
-### 2.1 引导阶段配置管理
-
-- 从远程拉取配置文件并保存至本地。
-- 向启动阶段注册配置刷新的 Bean。
-- 相关文件:`src/app/bootstrap/bootstrap.go`
-
-### 2.2 日志组件初始化
-
-- 启动阶段读取并解析本地配置。
-- 根据配置创建日志组件。
-- 相关文件:`src/app/common/handlers/log/log.go`
-
-### 2.3 HTTP 服务器启动
-
-- 启动阶段创建 HTTP 服务器。
-- 注册 HTTP 服务路由。
-- 相关文件:`src/app/common/httpsvr/httpsvr.go`
-
-### 2.4 控制器功能分组管理
-
-- 根据功能对 Controller 方法进行分组。
-- 每个子 Controller 独立注入和管理。
-- 相关文件:
-    - `src/app/controller/controller.go`
-    - `src/app/controller/controller-book.go`
-
-### 2.5 动态配置刷新
-
-- 支持运行时动态刷新配置。
-- 相关文件:`src/biz/service/book_service/book_service.go`
-
-### 2.6 后台任务优雅退出
-
-- 后台任务支持优雅停止,保证数据安全和资源释放。
-- 相关文件:`src/biz/job/job.go`
-
-## 三、总结
-
-本项目遵循模块化、清晰、可维护、可扩展的设计原则,适合中大型系统的开发需求。通过引导配置、日志管理、HTTP
-服务、动态刷新、后台任务管理等功能模块,实现了完整、健壮的应用架构。
\ No newline at end of file
diff --git a/docs/4. examples/bookman/conf/app-test.properties b/docs/4. examples/bookman/conf/app-test.properties
deleted file mode 100755
index 0f7d5d27..00000000
--- a/docs/4. examples/bookman/conf/app-test.properties	
+++ /dev/null
@@ -1,7 +0,0 @@
-server.addr=0.0.0.0:9090
-
-log.biz.name=biz.log
-log.biz.dir=./log
-
-log.dao.name=dao.log
-log.dao.dir=./log
\ No newline at end of file
diff --git a/docs/4. examples/bookman/conf/app.properties b/docs/4. examples/bookman/conf/app.properties
deleted file mode 100755
index 1d1906b2..00000000
--- a/docs/4. examples/bookman/conf/app.properties	
+++ /dev/null
@@ -1 +0,0 @@
-server.addr=0.0.0.0:8080
\ No newline at end of file
diff --git a/docs/4. examples/bookman/go.mod b/docs/4. examples/bookman/go.mod
deleted file mode 100644
index e415794f..00000000
--- a/docs/4. examples/bookman/go.mod	
+++ /dev/null
@@ -1,22 +0,0 @@
-module bookman
-
-go 1.24
-
-require (
-	github.com/go-spring/gs-assert v1.0.2
-	github.com/go-spring/spring-core v0.0.0
-	github.com/lvan100/go-loop v0.0.3
-)
-
-require (
-	github.com/expr-lang/expr v1.17.5 // indirect
-	github.com/go-spring/barky v1.0.3 // indirect
-	github.com/go-spring/gs-mock v0.0.4 // indirect
-	github.com/go-spring/log v0.0.5 // indirect
-	github.com/magiconair/properties v1.8.10 // indirect
-	github.com/pelletier/go-toml v1.9.5 // indirect
-	github.com/spf13/cast v1.9.2 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
-)
-
-replace github.com/go-spring/spring-core => ../../../
diff --git a/docs/4. examples/bookman/go.sum b/docs/4. examples/bookman/go.sum
deleted file mode 100644
index 213c55c3..00000000
--- a/docs/4. examples/bookman/go.sum	
+++ /dev/null
@@ -1,32 +0,0 @@
-github.com/expr-lang/expr v1.17.5 h1:i1WrMvcdLF249nSNlpQZN1S6NXuW9WaOfF5tPi3aw3k=
-github.com/expr-lang/expr v1.17.5/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/go-spring/barky v1.0.3 h1:24U2IX47es7JfuAx7WkkOqBEV0bOy49/ZW4GCkVvD7Q=
-github.com/go-spring/barky v1.0.3/go.mod h1:IlEMJj9d//EQs2oin0tuGKGACslZe73khbHcDPzF9KE=
-github.com/go-spring/gs-assert v1.0.2 h1:9vDppl7ZwMvQE4c83ac7GzN0VxZC5BrQue7dND7NclQ=
-github.com/go-spring/gs-assert v1.0.2/go.mod h1:FfibkqWz4HUBpbig1cKMlzW8Ha7RywTB93f1Q/NuF9I=
-github.com/go-spring/gs-mock v0.0.4 h1:f34YN+ntXflfn13aLa3ZVCB78IG7wWZGK4y5tB+OGpI=
-github.com/go-spring/gs-mock v0.0.4/go.mod h1:QK0PqZ+Vu9F+BU97zl8fip5XKibvDSoN+ofky413Z6Q=
-github.com/go-spring/log v0.0.5 h1:a8yiGmZTS7MPYvYvePXtc0hIdaQ76pLdsXt8iJwgQBQ=
-github.com/go-spring/log v0.0.5/go.mod h1:9SWgPEVWSGgloRTGR7niBliqfwC5UCjPUOl2jyJOimM=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/lvan100/go-loop v0.0.3 h1:s9nlQpPalVITSsMwlxTR+PlaC4CqODnL2qJJYGHwKsU=
-github.com/lvan100/go-loop v0.0.3/go.mod h1:YDtb+ZmMigBqRJqSwD4S/3mGFyY2cF/LCezXEWfwvG4=
-github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
-github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
-github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/docs/4. examples/bookman/init.go b/docs/4. examples/bookman/init.go
deleted file mode 100644
index 54932e4f..00000000
--- a/docs/4. examples/bookman/init.go	
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package main
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"runtime"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-const banner = `
-  ____                 _     __  __               
- | __ )   ___    ___  | | __|  \/  |  __ _  _ __  
- |  _ \  / _ \  / _ \ | |/ /| |\/| | / _' || '_ \ 
- | |_) || (_) || (_) ||   < | |  | || (_| || | | |
- |____/  \___/  \___/ |_|\_\|_|  |_| \__,_||_| |_|
-`
-
-func init() {
-	gs.Banner(banner)
-}
-
-// init sets the working directory of the application to the directory
-// where this source file resides.
-// This ensures that any relative file operations are based on the source file location,
-// not the process launch path.
-func init() {
-	var execDir string
-	_, filename, _, ok := runtime.Caller(0)
-	if ok {
-		execDir = filepath.Dir(filename)
-	}
-	err := os.Chdir(execDir)
-	if err != nil {
-		panic(err)
-	}
-	workDir, err := os.Getwd()
-	if err != nil {
-		panic(err)
-	}
-	fmt.Println(workDir)
-}
diff --git a/docs/4. examples/bookman/main.go b/docs/4. examples/bookman/main.go
deleted file mode 100644
index 7ee46a03..00000000
--- a/docs/4. examples/bookman/main.go	
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package main
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"net/http"
-	"time"
-
-	"github.com/go-spring/spring-core/gs"
-	"github.com/lvan100/go-loop"
-
-	_ "bookman/src/app"
-	_ "bookman/src/biz"
-)
-
-func init() {
-	gs.SetActiveProfiles("online")
-	gs.EnableSimplePProfServer(true)
-	gs.FuncJob(runTest).Name("#job")
-}
-
-func main() {
-	gs.Run()
-}
-
-// runTest performs a simple test.
-func runTest(ctx context.Context) error {
-	time.Sleep(time.Millisecond * 500)
-
-	loop.Times(5, func(_ int) {
-		url := "http://127.0.0.1:9090/books"
-		resp, err := http.Get(url)
-		if err != nil {
-			panic(err)
-		}
-		b, err := io.ReadAll(resp.Body)
-		if err != nil {
-			panic(err)
-		}
-		defer func() {
-			err = resp.Body.Close()
-			_ = err
-		}()
-		fmt.Print(string(b))
-		time.Sleep(time.Millisecond * 400)
-	})
-
-	// Shut down the application gracefully
-	gs.ShutDown()
-	return nil
-}
diff --git a/docs/4. examples/bookman/public/index.html b/docs/4. examples/bookman/public/index.html
deleted file mode 100644
index 615e8730..00000000
--- a/docs/4. examples/bookman/public/index.html	
+++ /dev/null
@@ -1 +0,0 @@
-It Works!
\ No newline at end of file
diff --git a/docs/4. examples/bookman/src/app/app.go b/docs/4. examples/bookman/src/app/app.go
deleted file mode 100644
index ae5a7abb..00000000
--- a/docs/4. examples/bookman/src/app/app.go	
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package app
-
-import (
-	_ "bookman/src/app/bootstrap"
-	_ "bookman/src/app/common/handlers/log"
-	_ "bookman/src/app/common/httpsvr"
-	_ "bookman/src/app/controller"
-)
diff --git a/docs/4. examples/bookman/src/app/bootstrap/bootstrap.go b/docs/4. examples/bookman/src/app/bootstrap/bootstrap.go
deleted file mode 100644
index 5384a5f2..00000000
--- a/docs/4. examples/bookman/src/app/bootstrap/bootstrap.go	
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package bootstrap
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"time"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	// Register a function runner to initialize the remote configuration setup.
-	gs.B.FuncRunner(initRemoteConfig).OnProfiles("online")
-}
-
-// initRemoteConfig initializes the remote configuration setup.
-// It first attempts to retrieve remote config, then starts a background job
-// to periodically refresh the configuration.
-func initRemoteConfig() error {
-	if err := getRemoteConfig(); err != nil {
-		return err
-	}
-	// Register a function job to refresh the configuration.
-	// A bean can be registered into the app during the bootstrap phase.
-	gs.FuncJob(refreshRemoteConfig)
-	return nil
-}
-
-// getRemoteConfig fetches and writes the remote configuration to a local file.
-// It creates necessary directories and generates a properties file containing.
-func getRemoteConfig() error {
-	err := os.MkdirAll("./conf/remote", os.ModePerm)
-	if err != nil {
-		return err
-	}
-
-	const data = `
-		server.addr=0.0.0.0:9090
-		
-		log.access.name=access.log
-		log.access.dir=./log
-		
-		log.biz.name=biz.log
-		log.biz.dir=./log
-		
-		log.dao.name=dao.log
-		log.dao.dir=./log
-		
-		refresh_time=%v
-	`
-
-	const file = "conf/remote/app-online.properties"
-	str := fmt.Sprintf(data, time.Now().UnixMilli())
-	return os.WriteFile(file, []byte(str), os.ModePerm)
-}
-
-// refreshRemoteConfig runs a continuous loop to periodically update configuration.
-// It refreshes every 500ms until context cancellation.
-func refreshRemoteConfig(ctx context.Context) error {
-	for {
-		select {
-		case <-ctx.Done(): // Gracefully exit when context is canceled.
-			// The context (ctx) is derived from the app instance.
-			// When the app exits, the context is canceled,
-			// allowing the loop to terminate gracefully.
-			fmt.Println("config updater exit")
-			return nil
-		case <-time.After(time.Millisecond * 500):
-			if err := getRemoteConfig(); err != nil {
-				fmt.Println("get remote config error:", err)
-				return err
-			}
-			// Refreshes the app configuration.
-			if err := gs.RefreshProperties(); err != nil {
-				fmt.Println("refresh properties error:", err)
-				return err
-			}
-			fmt.Println("refresh properties success")
-		}
-	}
-}
diff --git a/docs/4. examples/bookman/src/app/common/handlers/log/log.go b/docs/4. examples/bookman/src/app/common/handlers/log/log.go
deleted file mode 100644
index 9cb7225a..00000000
--- a/docs/4. examples/bookman/src/app/common/handlers/log/log.go	
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package log
-
-import (
-	"log/slog"
-	"os"
-	"path/filepath"
-
-	"github.com/go-spring/spring-core/conf"
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Module(nil, func(p conf.Properties) error {
-
-		var loggers map[string]struct {
-			Name string `value:"${name}"` // Log file name
-			Dir  string `value:"${dir}"`  // Directory where the log file will be stored
-		}
-
-		// Bind configuration from the "${log}" node into the 'loggers' map.
-		err := p.Bind(&loggers, "${log}")
-		if err != nil {
-			return err
-		}
-
-		for k, l := range loggers {
-			var (
-				f    *os.File
-				flag = os.O_WRONLY | os.O_CREATE | os.O_APPEND
-			)
-
-			// Open (or create) the log file
-			f, err = os.OpenFile(filepath.Join(l.Dir, l.Name), flag, os.ModePerm)
-			if err != nil {
-				return err
-			}
-
-			// Create a new slog.Logger instance with a text handler writing to the file
-			o := slog.New(slog.NewTextHandler(f, nil))
-
-			// Wrap the logger into a Bean with a destroy hook to close the file
-			gs.Object(o).Name(k).Destroy(func(_ *slog.Logger) {
-				_ = f.Close()
-			})
-		}
-		return nil
-	})
-}
diff --git a/docs/4. examples/bookman/src/app/common/httpsvr/httpsvr.go b/docs/4. examples/bookman/src/app/common/httpsvr/httpsvr.go
deleted file mode 100644
index 30c80a75..00000000
--- a/docs/4. examples/bookman/src/app/common/httpsvr/httpsvr.go	
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package httpsvr
-
-import (
-	"fmt"
-	"log/slog"
-	"net/http"
-
-	"bookman/src/app/controller"
-	"bookman/src/idl/http/proto"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	// Registers a custom ServeMux to replace the default implementation.
-	gs.Provide(
-		NewServeMux,
-		gs.IndexArg(1, gs.TagArg("access")),
-	)
-}
-
-// NewServeMux creates a new HTTP request multiplexer and registers
-// routes with access logging middleware.
-func NewServeMux(c *controller.Controller, logger *slog.Logger) *http.ServeMux {
-	mux := http.NewServeMux()
-	proto.RegisterRouter(mux, c, Access(logger))
-
-	// Users can customize routes by adding handlers to the mux
-	mux.Handle("GET /", http.FileServer(http.Dir("./public")))
-	return mux
-}
-
-// Access is a middleware to log incoming HTTP requests.
-func Access(logger *slog.Logger) func(http.Handler) http.Handler {
-	return func(next http.Handler) http.Handler {
-		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-			logger.Info(fmt.Sprintf("access %s %s", r.Method, r.URL.Path))
-			next.ServeHTTP(w, r)
-		})
-	}
-}
diff --git a/docs/4. examples/bookman/src/app/controller/controller-book.go b/docs/4. examples/bookman/src/app/controller/controller-book.go
deleted file mode 100644
index 151fd231..00000000
--- a/docs/4. examples/bookman/src/app/controller/controller-book.go	
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package controller
-
-import (
-	"encoding/json"
-	"net/http"
-
-	"bookman/src/biz/service/book_service"
-	"bookman/src/dao/book_dao"
-)
-
-type BookController struct {
-	BookService *book_service.BookService `autowire:""`
-}
-
-// ListBooks handles the HTTP request to list all books.
-func (c *BookController) ListBooks(w http.ResponseWriter, r *http.Request) {
-	books, err := c.BookService.ListBooks()
-	if err != nil {
-		_, _ = w.Write([]byte(err.Error()))
-		return
-	}
-	_ = json.NewEncoder(w).Encode(books)
-}
-
-// GetBook handles the HTTP request to get details of a specific book by ISBN.
-func (c *BookController) GetBook(w http.ResponseWriter, r *http.Request) {
-	isbn := r.PathValue("isbn")
-	book, err := c.BookService.GetBook(isbn)
-	if err != nil {
-		_, _ = w.Write([]byte(err.Error()))
-		return
-	}
-	_ = json.NewEncoder(w).Encode(book)
-}
-
-// SaveBook handles the HTTP request to save a new book.
-func (c *BookController) SaveBook(w http.ResponseWriter, r *http.Request) {
-	var book book_dao.Book
-	if err := json.NewDecoder(r.Body).Decode(&book); err != nil {
-		_, _ = w.Write([]byte(err.Error()))
-		return
-	}
-	if err := c.BookService.SaveBook(book); err != nil {
-		_, _ = w.Write([]byte(err.Error()))
-		return
-	}
-	_ = json.NewEncoder(w).Encode("OK!")
-}
-
-// DeleteBook handles the HTTP request to delete a book by ISBN.
-func (c *BookController) DeleteBook(w http.ResponseWriter, r *http.Request) {
-	isbn := r.PathValue("isbn")
-	err := c.BookService.DeleteBook(isbn)
-	if err != nil {
-		_, _ = w.Write([]byte(err.Error()))
-		return
-	}
-	_ = json.NewEncoder(w).Encode("OK!")
-}
diff --git a/docs/4. examples/bookman/src/app/controller/controller.go b/docs/4. examples/bookman/src/app/controller/controller.go
deleted file mode 100644
index f5ac3416..00000000
--- a/docs/4. examples/bookman/src/app/controller/controller.go	
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package controller
-
-import (
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Object(&Controller{})
-}
-
-// Controller implements the controller interface defined in the idl package.
-// In practice, controller methods can be grouped into different controllers.
-// Each sub-controller can have its own dependencies and be tested independently,
-// making the codebase more modular and maintainable.
-type Controller struct {
-	BookController
-}
diff --git a/docs/4. examples/bookman/src/biz/biz.go b/docs/4. examples/bookman/src/biz/biz.go
deleted file mode 100644
index e7edc7b5..00000000
--- a/docs/4. examples/bookman/src/biz/biz.go	
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package biz
-
-import (
-	_ "bookman/src/biz/job"
-)
diff --git a/docs/4. examples/bookman/src/biz/job/job.go b/docs/4. examples/bookman/src/biz/job/job.go
deleted file mode 100644
index ef9bb335..00000000
--- a/docs/4. examples/bookman/src/biz/job/job.go	
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package job
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Object(&Job{}).AsJob()
-}
-
-type Job struct{}
-
-// Run executes the background job until the context is canceled.
-func (x *Job) Run(ctx context.Context) error {
-	for {
-		select {
-		case <-ctx.Done():
-			// Gracefully exit when the context is canceled
-			fmt.Println("job exit")
-			return nil
-		default:
-			// Check if the app is shutting down.
-			// In long-running background tasks, checking for shutdown signals
-			// during idle periods or between stages helps ensure timely resource cleanup.
-			if gs.Exiting() {
-				return nil
-			}
-			time.Sleep(time.Millisecond * 300)
-			fmt.Println(time.Now().UnixMilli(), "job sleep end")
-		}
-	}
-}
diff --git a/docs/4. examples/bookman/src/biz/service/book_service/book_service.go b/docs/4. examples/bookman/src/biz/service/book_service/book_service.go
deleted file mode 100644
index 22813956..00000000
--- a/docs/4. examples/bookman/src/biz/service/book_service/book_service.go	
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package book_service
-
-import (
-	"fmt"
-	"log/slog"
-	"strconv"
-
-	"bookman/src/dao/book_dao"
-	"bookman/src/idl/http/proto"
-	"bookman/src/sdk/book_sdk"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Object(&BookService{})
-}
-
-type BookService struct {
-	BookDao     *book_dao.BookDao `autowire:""`
-	BookSDK     *book_sdk.BookSDK `autowire:""`
-	Logger      *slog.Logger      `autowire:"biz"`
-	RefreshTime gs.Dync[int64]    `value:"${refresh_time:=0}"`
-}
-
-// ListBooks retrieves all books from the database and enriches them with
-// pricing and refresh time.
-func (s *BookService) ListBooks() ([]proto.Book, error) {
-	books, err := s.BookDao.ListBooks()
-	if err != nil {
-		s.Logger.Error(fmt.Sprintf("ListBooks return err: %s", err.Error()))
-		return nil, err
-	}
-	ret := make([]proto.Book, 0, len(books))
-	for _, book := range books {
-		ret = append(ret, proto.Book{
-			ISBN:        book.ISBN,
-			Title:       book.Title,
-			Author:      book.Author,
-			Publisher:   book.Publisher,
-			Price:       s.BookSDK.GetPrice(book.ISBN),
-			RefreshTime: strconv.FormatInt(s.RefreshTime.Value(), 10),
-		})
-	}
-	return ret, nil
-}
-
-// GetBook retrieves a single book by its ISBN and enriches it with
-// pricing and refresh time.
-func (s *BookService) GetBook(isbn string) (proto.Book, error) {
-	book, err := s.BookDao.GetBook(isbn)
-	if err != nil {
-		s.Logger.Error(fmt.Sprintf("GetBook return err: %s", err.Error()))
-		return proto.Book{}, err
-	}
-	return proto.Book{
-		ISBN:        book.ISBN,
-		Title:       book.Title,
-		Author:      book.Author,
-		Publisher:   book.Publisher,
-		Price:       s.BookSDK.GetPrice(book.ISBN),
-		RefreshTime: strconv.FormatInt(s.RefreshTime.Value(), 10),
-	}, nil
-}
-
-// SaveBook stores a new book in the database.
-func (s *BookService) SaveBook(book book_dao.Book) error {
-	return s.BookDao.SaveBook(book)
-}
-
-// DeleteBook removes a book from the database by its ISBN.
-func (s *BookService) DeleteBook(isbn string) error {
-	return s.BookDao.DeleteBook(isbn)
-}
diff --git a/docs/4. examples/bookman/src/biz/service/book_service/book_service_test.go b/docs/4. examples/bookman/src/biz/service/book_service/book_service_test.go
deleted file mode 100644
index 75e66577..00000000
--- a/docs/4. examples/bookman/src/biz/service/book_service/book_service_test.go	
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package book_service
-
-import (
-	"testing"
-
-	"bookman/src/dao/book_dao"
-
-	"github.com/go-spring/gs-assert/assert"
-	"github.com/go-spring/spring-core/gs"
-	"github.com/go-spring/spring-core/gs/gstest"
-)
-
-func init() {
-	// Mock the BookDao with initial test data
-	gstest.MockFor[*book_dao.BookDao]().With(&book_dao.BookDao{
-		Store: map[string]book_dao.Book{
-			"978-0132350884": {
-				Title:     "Clean Code",
-				Author:    "Robert C. Martin",
-				ISBN:      "978-0132350884",
-				Publisher: "Prentice Hall",
-			},
-		},
-	})
-	// Load local configuration files
-	gs.Config().LocalFile.AddDir("../../../../conf")
-}
-
-func TestMain(m *testing.M) {
-	gstest.TestMain(m)
-}
-
-func TestBookService(t *testing.T) {
-
-	x := gstest.Wire(t, new(struct {
-		SvrAddr string            `value:"${server.addr}"`
-		Service *BookService      `autowire:""`
-		BookDao *book_dao.BookDao `autowire:""`
-	}))
-
-	// Verify server address configuration
-	assert.That(t, x.SvrAddr).Equal("0.0.0.0:9090")
-
-	s, o := x.Service, x.BookDao
-	assert.NotNil(t, o)
-
-	// Test listing books
-	books, err := s.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(1)
-	assert.That(t, books[0].ISBN).Equal("978-0132350884")
-
-	// Test saving a new book
-	err = s.SaveBook(book_dao.Book{
-		Title:     "Introduction to Algorithms",
-		Author:    "Thomas H. Cormen, Charles E. Leiserson, ...",
-		ISBN:      "978-0262033848",
-		Publisher: "MIT Press",
-	})
-	assert.Nil(t, err)
-
-	// Verify book was added successfully
-	books, err = s.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(2)
-	assert.That(t, books[1].ISBN).Equal("978-0262033848")
-	assert.That(t, books[1].Title).Equal("Introduction to Algorithms")
-
-	// Test retrieving a book by ISBN
-	book, err := s.GetBook("978-0132350884")
-	assert.Nil(t, err)
-	assert.That(t, book.ISBN).Equal("978-0132350884")
-	assert.That(t, book.Title).Equal("Clean Code")
-
-	// Test deleting a book
-	err = s.DeleteBook("978-0132350884")
-	assert.Nil(t, err)
-
-	// Verify book deletion
-	books, err = s.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(1)
-}
diff --git a/docs/4. examples/bookman/src/dao/book_dao/book_dao.go b/docs/4. examples/bookman/src/dao/book_dao/book_dao.go
deleted file mode 100644
index 69007707..00000000
--- a/docs/4. examples/bookman/src/dao/book_dao/book_dao.go	
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package book_dao
-
-import (
-	"log/slog"
-	"maps"
-	"slices"
-	"sort"
-
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Object(&BookDao{Store: map[string]Book{
-		"978-0134190440": {
-			Title:     "The Go Programming Language",
-			Author:    "Alan A. A. Donovan, Brian W. Kernighan",
-			ISBN:      "978-0134190440",
-			Publisher: "Addison-Wesley",
-		},
-	}})
-}
-
-type Book struct {
-	Title     string `json:"title"`
-	Author    string `json:"author"`
-	ISBN      string `json:"isbn"`
-	Publisher string `json:"publisher"`
-}
-
-type BookDao struct {
-	Store  map[string]Book
-	Logger *slog.Logger `autowire:"dao"`
-}
-
-// ListBooks returns a sorted list of all books in the store.
-func (dao *BookDao) ListBooks() ([]Book, error) {
-	r := slices.Collect(maps.Values(dao.Store))
-	sort.Slice(r, func(i, j int) bool {
-		return r[i].ISBN < r[j].ISBN
-	})
-	return r, nil
-}
-
-// GetBook retrieves a book by its ISBN.
-func (dao *BookDao) GetBook(isbn string) (Book, error) {
-	r, ok := dao.Store[isbn]
-	_ = ok
-	return r, nil
-}
-
-// SaveBook adds or updates a book in the store.
-func (dao *BookDao) SaveBook(book Book) error {
-	dao.Store[book.ISBN] = book
-	return nil
-}
-
-// DeleteBook removes a book from the store by its ISBN.
-func (dao *BookDao) DeleteBook(isbn string) error {
-	delete(dao.Store, isbn)
-	return nil
-}
diff --git a/docs/4. examples/bookman/src/dao/book_dao/book_dao_test.go b/docs/4. examples/bookman/src/dao/book_dao/book_dao_test.go
deleted file mode 100644
index e425c670..00000000
--- a/docs/4. examples/bookman/src/dao/book_dao/book_dao_test.go	
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package book_dao
-
-import (
-	"os"
-	"testing"
-
-	"github.com/go-spring/gs-assert/assert"
-	"github.com/go-spring/spring-core/gs/gstest"
-)
-
-func init() {
-	_ = os.Setenv("GS_SPRING_APP_CONFIG-LOCAL_DIR", "../../../conf")
-}
-
-func TestMain(m *testing.M) {
-	gstest.TestMain(m)
-}
-
-func TestBookDao(t *testing.T) {
-
-	// Wire dependencies and retrieve the server address
-	x := gstest.Wire(t, &struct {
-		SvrAddr string `value:"${server.addr}"`
-	}{})
-	assert.That(t, x.SvrAddr).Equal("0.0.0.0:9090")
-
-	// Retrieve BookDao instance
-	o := gstest.Get[*BookDao](t)
-	assert.NotNil(t, o)
-
-	// Test listing books
-	books, err := o.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(1)
-	assert.That(t, books[0].ISBN).Equal("978-0134190440")
-	assert.That(t, books[0].Title).Equal("The Go Programming Language")
-
-	// Test saving a new book
-	err = o.SaveBook(Book{
-		Title:     "Clean Code",
-		Author:    "Robert C. Martin",
-		ISBN:      "978-0132350884",
-		Publisher: "Prentice Hall",
-	})
-	assert.That(t, err).Equal(nil)
-
-	// Verify book was added
-	books, err = o.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(2)
-	assert.That(t, books[0].ISBN).Equal("978-0132350884")
-	assert.That(t, books[0].Title).Equal("Clean Code")
-
-	// Test retrieving a book by ISBN
-	book, err := o.GetBook("978-0132350884")
-	assert.Nil(t, err)
-	assert.That(t, book.Title).Equal("Clean Code")
-	assert.That(t, book.Publisher).Equal("Prentice Hall")
-
-	// Test deleting a book
-	err = o.DeleteBook("978-0132350884")
-	assert.Nil(t, err)
-
-	// Verify book was deleted
-	books, err = o.ListBooks()
-	assert.Nil(t, err)
-	assert.That(t, len(books)).Equal(1)
-	assert.That(t, books[0].ISBN).Equal("978-0134190440")
-}
diff --git a/docs/4. examples/bookman/src/idl/http/proto/proto.go b/docs/4. examples/bookman/src/idl/http/proto/proto.go
deleted file mode 100644
index c1b7de76..00000000
--- a/docs/4. examples/bookman/src/idl/http/proto/proto.go	
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Package proto defines the interfaces and route registrations generated from IDL files.
-package proto
-
-import (
-	"net/http"
-)
-
-// Book represents the structure of a book entity.
-type Book struct {
-	Title       string `json:"title"`
-	Author      string `json:"author"`
-	ISBN        string `json:"isbn"`
-	Publisher   string `json:"publisher"`
-	Price       string `json:"price"`
-	RefreshTime string `json:"refreshTime"`
-}
-
-// Controller defines the service interface for book-related operations.
-type Controller interface {
-	ListBooks(w http.ResponseWriter, r *http.Request)
-	GetBook(w http.ResponseWriter, r *http.Request)
-	SaveBook(w http.ResponseWriter, r *http.Request)
-	DeleteBook(w http.ResponseWriter, r *http.Request)
-}
-
-// RegisterRouter registers the HTTP routes for the Controller interface.
-// It maps each method to its corresponding HTTP endpoint,
-// and applies the given middleware (wrap) to each handler.
-func RegisterRouter(mux *http.ServeMux, c Controller, wrap func(next http.Handler) http.Handler) {
-	mux.Handle("GET /books", wrap(http.HandlerFunc(c.ListBooks)))
-	mux.Handle("GET /books/{isbn}", wrap(http.HandlerFunc(c.GetBook)))
-	mux.Handle("POST /books", wrap(http.HandlerFunc(c.SaveBook)))
-	mux.Handle("DELETE /books/{isbn}", wrap(http.HandlerFunc(c.DeleteBook)))
-}
diff --git a/docs/4. examples/bookman/src/sdk/book_sdk/book_sdk.go b/docs/4. examples/bookman/src/sdk/book_sdk/book_sdk.go
deleted file mode 100644
index c7bc250e..00000000
--- a/docs/4. examples/bookman/src/sdk/book_sdk/book_sdk.go	
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2025 The Go-Spring Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package book_sdk
-
-import (
-	"github.com/go-spring/spring-core/gs"
-)
-
-func init() {
-	gs.Object(&BookSDK{})
-}
-
-type BookSDK struct{}
-
-// GetPrice returns a fixed price for any book.
-func (s *BookSDK) GetPrice(isbn string) string {
-	return "¥10"
-}
diff --git a/docs/4. examples/chatAI/chatAI.html b/docs/4. examples/chatAI/chatAI.html
deleted file mode 100644
index 6afd2246..00000000
--- a/docs/4. examples/chatAI/chatAI.html	
+++ /dev/null
@@ -1,181 +0,0 @@
-
-
-
-    
-    Chat AI
-    
-
-
-
-    
🧠 Chat AI
-    
-        
-        
-    
-    
-