6、反射

反射的基本概念

在Go编程中,反射(Reflection)是一项强大而复杂的特性,它允许程序在运行时检查和修改自己的结构和行为。虽然在日常编程中我们可能会尽量避免过度使用反射(因为它会带来复杂性和性能损失),但了解反射机制对于理解许多高级库和框架的工作原理至关重要。

反射是程序在运行时访问、检查和修改其自身结构的能力。在Go中,反射主要通过reflect包提供支持,它使我们能够:

反射在某种程度上打破了Go的静态类型系统,为程序提供了更高的灵活性,但同时也带来了复杂性和潜在的运行时错误。

反射三大定律

理解Go反射的核心是掌握Rob Pike提出的"反射三大法则":

为什么需要反射

在某些场景下,反射是非常有用甚至是必不可少的:

反射的缺点

reflect包

Go的反射机制主要通过reflect包中的两个核心类型来实现:

这两个类型是Go反射机制的基础,几乎所有反射操作都围绕它们展开。

Type和Kind

Type表示具体的类型,而Kind表示类型的基础类别。例如:

Go的反射系统定义了23种基本Kind:

type Kind uint

const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Ptr
    Slice
    String
    Struct
    UnsafePointer
)

Value类型

Value类型表示一个Go值,它提供了访问和修改值的方法:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // 通过ValueOf获取值的反射表示
    var x int = 100
    v := reflect.ValueOf(x)
    
    fmt.Println("值:", v.Interface())             // 输出: 100
    fmt.Println("类型:", v.Type())                 // 输出: int
    fmt.Println("种类:", v.Kind())                 // 输出: int
    fmt.Println("是否可寻址:", v.CanAddr())          // 输出: false (传值方式)
    fmt.Println("是否可设置:", v.CanSet())           // 输出: false (传值方式)
    
    // 创建可设置的Value
    y := 200
    pv := reflect.ValueOf(&y)      // 获取y的地址的反射值
    ev := pv.Elem()                // 获取指针指向的Value
    
    fmt.Println("是否可寻址:", ev.CanAddr())         // 输出: true
    fmt.Println("是否可设置:", ev.CanSet())          // 输出: true
    
    // 修改值
    if ev.CanSet() {
        ev.SetInt(300)
        fmt.Println("修改后的y值:", y)               // 输出: 300
    }
    
    // 结构体值
    type Person struct {
        Name string
        Age  int
    }
    
    p := Person{"李四", 25}
    pv = reflect.ValueOf(&p)
    ev = pv.Elem()
    
    // 获取并修改字段值
    nameField := ev.FieldByName("Name")
    if nameField.CanSet() {
        nameField.SetString("王五")
    }
    
    ageField := ev.FieldByName("Age")
    if ageField.CanSet() {
        ageField.SetInt(30)
    }
    
    fmt.Printf("修改后的person: %+v\n", p)          // 输出: {Name:王五 Age:30}
}

Value类型常用方法:

从接口值到反射对象

反射的第一法则是"从接口值到反射对象"。在Go中,这通过两个基本函数实现:

当我们传递一个值给reflect.TypeOf()reflect.ValueOf()时,该值会被转换为一个空接口interface{},然后反射系统从这个接口值中提取类型和值信息。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // 基本类型
    i := 42
    s := "Hello, reflection!"
    
    // 获取类型信息
    iType := reflect.TypeOf(i)
    sType := reflect.TypeOf(s)
    
    fmt.Printf("Type of i: %v\n", iType)         // int
    fmt.Printf("Type of s: %v\n", sType)         // string
    
    // 获取种类信息
    fmt.Printf("Kind of i: %v\n", iType.Kind())  // int
    fmt.Printf("Kind of s: %v\n", sType.Kind())  // string
    
    // 获取值信息
    iValue := reflect.ValueOf(i)
    sValue := reflect.ValueOf(s)
    
    fmt.Printf("Value of i: %v\n", iValue)       // 42
    fmt.Printf("Value of s: %v\n", sValue)       // Hello, reflection!
    
    // 获取值的实际内容
    fmt.Printf("Value of i (int): %v\n", iValue.Int())         // 42
    fmt.Printf("Value of s (string): %v\n", sValue.String())   // Hello, reflection!
}

从反射对象到接口值

反射的第二法则是"从反射对象到接口值"。我们可以使用Interface()方法将reflect.Value转换回接口值:

这个过程是从接口值到反射对象的逆过程,将反射值转换回Go值。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    i := 42
    v := reflect.ValueOf(i)
    
    // 从反射值到接口值
    iface := v.Interface()
    
    // 使用类型断言获取具体类型的值
    i2 := iface.(int)
    
    fmt.Println(i2) // 输出: 42
}

修改反射对象的值

反射的第三法则是"要修改反射对象,其值必须可设置(可寻址)"。简单来说,只有当原始值可以被修改时,其对应的反射值才能被修改。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // 示例1: 无法修改不可设置的值
    x := 42
    v := reflect.ValueOf(x)
    
    // 这会导致panic
    // v.SetInt(21) // panic: reflect.Value.SetInt using unaddressable value
    
    // 示例2: 修改可设置的值
    y := 42
    p := reflect.ValueOf(&y) // 获取y的地址的反射值
    v = p.Elem()            // 获取指针指向的值
    
    fmt.Println("CanSet:", v.CanSet()) // true
    v.SetInt(21)
    fmt.Println(y) // 输出: 21
}

使用反射检查类型

反射最常见的用途之一是检查变量类型的各个方面。特别是对于复杂的结构体类型,反射提供了强大的功能来检查其字段、方法和标签。

检查结构体类型

结构体是Go中最常用的复合类型之一。通过反射,我们可以检查结构体的字段、标签和方法:

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    ID        int    `json:"id" validate:"required"`
    Name      string `json:"name" validate:"required,min=3"`
    Email     string `json:"email" validate:"required,email"`
    Age       int    `json:"age" validate:"gte=0,lte=130"`
    CreatedAt string `json:"created_at" validate:"-"`
}

func (u User) GetFullInfo() string {
    return fmt.Sprintf("User %s (ID: %d, Email: %s)", u.Name, u.ID, u.Email)
}

func (u *User) UpdateName(newName string) {
    u.Name = newName
}

func inspectStruct(v interface{}) {
    t := reflect.TypeOf(v)
    
    // 如果是指针,获取其指向的元素类型
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    
    // 确保是结构体
    if t.Kind() != reflect.Struct {
        fmt.Println("不是结构体类型")
        return
    }
    
    fmt.Printf("类型名称: %s\n", t.Name())
    fmt.Printf("字段数量: %d\n", t.NumField())
    fmt.Println("字段列表:")
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("  %d: %s (%s)\n", i, field.Name, field.Type)
        
        // 打印字段标签
        if tag := field.Tag; tag != "" {
            fmt.Printf("    标签: %s\n", tag)
            fmt.Printf("    json标签: %s\n", tag.Get("json"))
            fmt.Printf("    validate标签: %s\n", tag.Get("validate"))
        }
    }
    
    // 检查方法
    fmt.Println("方法列表:")
    
    // 值接收者方法
    vt := reflect.TypeOf(v)
    for i := 0; i < t.NumMethod(); i++ {
        method := t.Method(i)
        fmt.Printf("  值接收者方法 %d: %s\n", i, method.Name)
        fmt.Printf("    类型: %s\n", method.Type)
    }
    
    // 指针接收者方法
    if vt.Kind() != reflect.Ptr {
        vt = reflect.PtrTo(t)
    }
    
    for i := 0; i < vt.NumMethod(); i++ {
        method := vt.Method(i)
        fmt.Printf("  指针接收者方法 %d: %s\n", i, method.Name)
        fmt.Printf("    类型: %s\n", method.Type)
    }
}

func main() {
    user := User{
        ID:        1,
        Name:      "张三",
        Email:     "zhangsan@example.com",
        Age:       30,
        CreatedAt: "2023-01-01",
    }
    
    inspectStruct(user)
}

检查接口实现

反射还可以用来检查一个类型是否实现了特定接口:

package main

import (
    "fmt"
    "io"
    "os"
    "reflect"
)

// 检查类型是否实现接口
func implementsInterface(v interface{}, interfaceType reflect.Type) bool {
    vType := reflect.TypeOf(v)
    return vType.Implements(interfaceType)
}

// 获取接口类型
func getInterfaceType(interfaceValue interface{}) reflect.Type {
    return reflect.TypeOf(interfaceValue).Elem()
}

func main() {
    // 检查*os.File是否实现io.Reader接口
    file, _ := os.Open("example.txt")
    defer file.Close()
    
    var reader io.Reader
    readerType := getInterfaceType(&reader)
    
    fmt.Printf("*os.File是否实现io.Reader: %v\n", 
               implementsInterface(file, readerType))
    
    // 检查string是否实现io.Reader
    var s string = "hello"
    fmt.Printf("string是否实现io.Reader: %v\n", 
               implementsInterface(s, readerType))
}

使用反射修改值

除了检查类型和值外,反射还允许我们在运行时修改变量的值,只要这些值是可寻址和可设置的。

修改基本类型值

var x int = 100
v := reflect.ValueOf(&x)  // 获取指针的反射值
e := v.Elem()            // 获取指针指向的值

fmt.Println("旧值:", x)
e.SetInt(200)
fmt.Println("新值:", x)

修改结构体字段

package main

import (
    "fmt"
    "reflect"
)

type Product struct {
    ID       int
    Name     string
    Price    float64
    Quantity int
    Tags     []string
    Metadata map[string]string
}

func main() {
    p := Product{
        ID:       1,
        Name:     "笔记本电脑",
        Price:    5999.99,
        Quantity: 10,
        Tags:     []string{"电子", "计算机"},
        Metadata: map[string]string{"brand": "品牌A", "color": "银色"},
    }
    
    fmt.Printf("原始产品: %+v\n", p)
    
    // 获取可设置的反射值
    v := reflect.ValueOf(&p).Elem()
    
    // 修改基本类型字段
    if idField := v.FieldByName("ID"); idField.IsValid() && idField.CanSet() {
        idField.SetInt(2)
    }
    
    if nameField := v.FieldByName("Name"); nameField.IsValid() && nameField.CanSet() {
        nameField.SetString("高性能服务器")
    }
    
    if priceField := v.FieldByName("Price"); priceField.IsValid() && priceField.CanSet() {
        priceField.SetFloat(19999.99)
    }
    
    // 修改切片字段
    if tagsField := v.FieldByName("Tags"); tagsField.IsValid() && tagsField.CanSet() {
        // 创建一个新的切片
        newTags := reflect.MakeSlice(tagsField.Type(), 3, 3)
        newTags.Index(0).SetString("服务器")
        newTags.Index(1).SetString("企业级")
        newTags.Index(2).SetString("高性能")
        
        // 设置整个切片
        tagsField.Set(newTags)
    }
    
    // 修改映射字段
    if metaField := v.FieldByName("Metadata"); metaField.IsValid() && metaField.CanSet() {
        // 创建一个新的映射
        newMeta := reflect.MakeMap(metaField.Type())
        newMeta.SetMapIndex(reflect.ValueOf("brand"), reflect.ValueOf("品牌B"))
        newMeta.SetMapIndex(reflect.ValueOf("color"), reflect.ValueOf("黑色"))
        newMeta.SetMapIndex(reflect.ValueOf("series"), reflect.ValueOf("专业系列"))
        
        // 设置整个映射
        metaField.Set(newMeta)
    }
    
    fmt.Printf("修改后的产品: %+v\n", p)
}

创建新的值

反射提供了创建新值的能力,这在需要动态创建对象时非常有用:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // 创建基本类型值
    intType := reflect.TypeOf(0)
    intValue := reflect.New(intType).Elem() // 创建int类型的可设置Value
    intValue.SetInt(42)
    fmt.Println("创建的int值:", intValue.Interface())
    
    // 创建切片
    sliceType := reflect.TypeOf([]string{})
    sliceValue := reflect.MakeSlice(sliceType, 3, 5) // 长度3,容量5
    
    // 设置切片元素
    sliceValue.Index(0).SetString("Go")
    sliceValue.Index(1).SetString("Java")
    sliceValue.Index(2).SetString("Python")
    
    fmt.Println("创建的切片:", sliceValue.Interface())
    
    // 创建映射
    mapType := reflect.TypeOf(map[string]int{})
    mapValue := reflect.MakeMap(mapType)
    
    // 添加键值对
    mapValue.SetMapIndex(reflect.ValueOf("one"), reflect.ValueOf(1))
    mapValue.SetMapIndex(reflect.ValueOf("two"), reflect.ValueOf(2))
    mapValue.SetMapIndex(reflect.ValueOf("three"), reflect.ValueOf(3))
    
    fmt.Println("创建的映射:", mapValue.Interface())
    
    // 创建结构体
    type Person struct {
        Name string
        Age  int
    }
    
    personType := reflect.TypeOf(Person{})
    personPtr := reflect.New(personType)       // 创建*Person类型的Value
    personValue := personPtr.Elem()            // 获取Person类型的Value
    
    personValue.FieldByName("Name").SetString("张三")
    personValue.FieldByName("Age").SetInt(30)
    
    fmt.Println("创建的结构体:", personValue.Interface())
    
    // 将反射值转换为具体类型
    person := personPtr.Interface().(*Person)
    fmt.Printf("转换后的Person: %+v\n", *person)
}

使用反射调用函数和方法

反射的另一个强大功能是能够在运行时动态调用函数和方法。

调用函数

通过反射调用函数需要准备好函数和参数的反射值:

package main

import (
    "fmt"
    "reflect"
)

func add(a, b int) int {
    return a + b
}

func concat(strs ...string) string {
    result := ""
    for _, s := range strs {
        result += s
    }
    return result
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为零")
    }
    return a / b, nil
}

func callFunction() {
    // 获取函数的反射值
    addFunc := reflect.ValueOf(add)
    
    // 准备参数
    args := []reflect.Value{
        reflect.ValueOf(10),
        reflect.ValueOf(20),
    }
    
    // 调用函数
    results := addFunc.Call(args)
    
    // 处理返回值
    if len(results) > 0 {
        fmt.Println("add(10, 20) =", results[0].Interface())
    }
    
    // 调用可变参数函数
    concatFunc := reflect.ValueOf(concat)
    concatArgs := []reflect.Value{
        reflect.ValueOf("Hello"),
        reflect.ValueOf(", "),
        reflect.ValueOf("World"),
        reflect.ValueOf("!"),
    }
    
    concatResults := concatFunc.Call(concatArgs)
    if len(concatResults) > 0 {
        fmt.Println("concat结果:", concatResults[0].Interface())
    }
    
    // 调用返回多个值的函数
    divideFunc := reflect.ValueOf(divide)
    divideArgs := []reflect.Value{
        reflect.ValueOf(10.0),
        reflect.ValueOf(2.0),
    }
    
    divideResults := divideFunc.Call(divideArgs)
    if len(divideResults) > 0 {
        fmt.Printf("除法结果: %.2f\n", divideResults[0].Interface())
        
        // 检查错误
        if len(divideResults) > 1 && !divideResults[1].IsNil() {
            fmt.Println("错误:", divideResults[1].Interface())
        }
    }
}

func main() {
    callFunction()
}

调用方法

调用结构体的方法与调用函数类似,但需要先获取方法的反射值:

package main

import (
    "fmt"
    "reflect"
)

type Calculator struct {
    Brand string
}

func (c Calculator) Add(a, b int) int {
    return a + b
}

func (c Calculator) Multiply(a, b int) int {
    return a * b
}

func (c *Calculator) SetBrand(brand string) {
    c.Brand = brand
}

func (c Calculator) GetInfo() string {
    return fmt.Sprintf("Calculator [%s]", c.Brand)
}

func callMethod() {
    // 创建计算器实例
    calc := Calculator{Brand: "科学计算器"}
    fmt.Println("原始计算器:", calc)
    
    // 获取值接收者方法
    addMethod := reflect.ValueOf(calc).MethodByName("Add")
    if addMethod.IsValid() {
        args := []reflect.Value{
            reflect.ValueOf(10),
            reflect.ValueOf(20),
        }
        
        result := addMethod.Call(args)
        fmt.Println("Add方法结果:", result[0].Interface())
    }
    
    // 获取指针接收者方法 - 需要传递指针的反射值
    calcPtr := reflect.ValueOf(&calc)
    setBrandMethod := calcPtr.MethodByName("SetBrand")
    if setBrandMethod.IsValid() {
        args := []reflect.Value{
            reflect.ValueOf("高级科学计算器"),
        }
        
        setBrandMethod.Call(args)
        fmt.Println("修改后的计算器:", calc)
    }
    
    // 动态选择要调用的方法
    methodName := "Multiply"
    method := reflect.ValueOf(calc).MethodByName(methodName)
    
    if method.IsValid() {
        args := []reflect.Value{
            reflect.ValueOf(5),
            reflect.ValueOf(7),
        }
        
        result := method.Call(args)
        fmt.Printf("%s方法结果: %v\n", methodName, result[0].Interface())
            } else {
        fmt.Printf("方法%s不存在\n", methodName)
    }
}

func main() {
    callMethod()
}

反射的实际应用

JSON序列化和反序列化

Go标准库中的encoding/json包使用反射来实现JSON的序列化(Marshal)和反序列化(Unmarshal)。这使得任何符合特定规则的Go结构体都可以自动转换为JSON,而无需为每种结构编写专门的编码/解码代码。

package main

import (
    "encoding/json"
    "fmt"
)

    type Person struct {
    Name      string   `json:"name"`
    Age       int      `json:"age"`
    Email     string   `json:"email,omitempty"`
    Addresses []string `json:"addresses"`
    private   string   // 不会被序列化
}

func main() {
    person := Person{
        Name:      "张三",
        Age:       30,
        Addresses: []string{"北京市", "上海市"},
        private:   "私有字段",
    }
    
    // 序列化为JSON
    data, err := json.Marshal(person)
    if err != nil {
        fmt.Println("序列化错误:", err)
        return
    }
    
    fmt.Println("JSON:", string(data))
    
    // 反序列化
    var decodedPerson Person
    err = json.Unmarshal(data, &decodedPerson)
    if err != nil {
        fmt.Println("反序列化错误:", err)
        return
    }
    
    fmt.Printf("反序列化结果: %+v\n", decodedPerson)
}

结构体验证

许多验证库,如validator,使用反射来根据结构体标签验证字段值:

package main

import (
    "fmt"
    "reflect"
    "regexp"
    "strconv"
    "strings"
)

// 验证器接口
type Validator interface {
    Validate(val interface{}) bool
    Message() string
}

// 电子邮件验证器
type EmailValidator struct{}

func (v EmailValidator) Validate(val interface{}) bool {
    str, ok := val.(string)
    if !ok {
        return false
    }
    return regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`).MatchString(str)
}

func (v EmailValidator) Message() string {
    return "必须是有效的电子邮件地址"
}

// 长度验证器
type LengthValidator struct {
    Min int
    Max int
}

func (v LengthValidator) Validate(val interface{}) bool {
    str, ok := val.(string)
    if !ok {
        return false
    }
    
    length := len(str)
    if v.Min > 0 && length < v.Min {
        return false
    }
    if v.Max > 0 && length > v.Max {
        return false
    }
    return true
}

func (v LengthValidator) Message() string {
    if v.Min > 0 && v.Max > 0 {
        return fmt.Sprintf("长度必须在%d到%d之间", v.Min, v.Max)
    }
    if v.Min > 0 {
        return fmt.Sprintf("长度必须大于等于%d", v.Min)
    }
    return fmt.Sprintf("长度必须小于等于%d", v.Max)
}

// 验证结构体
func ValidateStruct(obj interface{}) []string {
    var errors []string
    v := reflect.ValueOf(obj)
    
    // 处理指针
    if v.Kind() == reflect.Ptr {
        v = v.Elem()
    }
    
    // 确保是结构体
    if v.Kind() != reflect.Struct {
        return []string{"只能验证结构体"}
    }
    
    t := v.Type()
    
    // 遍历所有字段
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        
        // 检查validate标签
        validateTag := field.Tag.Get("validate")
        if validateTag == "" {
            continue
        }
        
        fieldValue := v.Field(i)
        
        // 解析验证规则
        validations := strings.Split(validateTag, ",")
        for _, validation := range validations {
            var validator Validator
            
            if validation == "email" {
                validator = EmailValidator{}
            } else if strings.HasPrefix(validation, "length=") {
                params := strings.TrimPrefix(validation, "length=")
                parts := strings.Split(params, ":")
                min, _ := strconv.Atoi(parts[0])
            max := 0
        if len(parts) > 1 {
                    max, _ = strconv.Atoi(parts[1])
                }
                validator = LengthValidator{Min: min, Max: max}
            } else {
            continue
        }
        
            // 验证字段值
            if !validator.Validate(fieldValue.Interface()) {
                errors = append(errors, 
                    fmt.Sprintf("字段 %s: %s", field.Name, validator.Message()))
            }
        }
    }
    
    return errors
}

func main() {
    type User struct {
        Username string `validate:"length=3:20"`
        Email    string `validate:"email"`
        Bio      string `validate:"length=0:100"`
    }
    
    user := User{
        Username: "a",
        Email:    "invalid-email",
        Bio:      strings.Repeat("很长的简介 ", 30),
    }
    
    errors := ValidateStruct(user)
    if len(errors) > 0 {
        fmt.Println("验证错误:")
        for _, err := range errors {
            fmt.Println("- " + err)
        }
    } else {
        fmt.Println("验证通过")
    }
}

依赖注入

反射可用于实现依赖注入系统,在运行时动态装配对象:

package main

import (
    "fmt"
    "reflect"
)

// 服务接口
type Logger interface {
    Log(message string)
}

type UserRepository interface {
    FindUser(id int) string
}

// 服务实现
type ConsoleLogger struct{}

func (l *ConsoleLogger) Log(message string) {
    fmt.Printf("[LOG] %s\n", message)
}

type DatabaseUserRepository struct {
    Logger Logger `inject:"logger"`
}

func (r *DatabaseUserRepository) FindUser(id int) string {
    r.Logger.Log(fmt.Sprintf("查找用户ID: %d", id))
    return fmt.Sprintf("用户_%d", id)
}

// 控制器
type UserController struct {
    Repository UserRepository `inject:"userRepository"`
    Logger     Logger         `inject:"logger"`
}

func (c *UserController) GetUser(id int) {
    c.Logger.Log(fmt.Sprintf("控制器处理GetUser请求,ID: %d", id))
    user := c.Repository.FindUser(id)
    fmt.Printf("找到用户: %s\n", user)
}

// 简单的依赖注入容器
type Container struct {
    services map[string]interface{}
}

func NewContainer() *Container {
    return &Container{
        services: make(map[string]interface{}),
    }
}

func (c *Container) Register(name string, service interface{}) {
    c.services[name] = service
}

func (c *Container) Resolve(target interface{}) error {
    v := reflect.ValueOf(target)
    
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("目标必须是结构体指针")
    }
    
    e := v.Elem()
    t := e.Type()
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        injectTag := field.Tag.Get("inject")
        
        if injectTag == "" {
            continue
        }
        
        service, exists := c.services[injectTag]
        if !exists {
            return fmt.Errorf("服务不存在: %s", injectTag)
        }
        
        sv := reflect.ValueOf(service)
        fieldValue := e.Field(i)
        
        if !sv.Type().AssignableTo(fieldValue.Type()) {
            return fmt.Errorf(
                "类型不匹配: 服务 %s 类型为 %s, 但字段 %s 类型为 %s",
                injectTag, sv.Type(), field.Name, fieldValue.Type(),
            )
        }
        
        fieldValue.Set(sv)
        
        // 递归解析依赖
        if sv.Kind() == reflect.Ptr && sv.Elem().Kind() == reflect.Struct {
            c.Resolve(service)
        }
    }
    
    return nil
}

func main() {
    // 创建容器
    container := NewContainer()
    
    // 注册服务
    logger := &ConsoleLogger{}
    repository := &DatabaseUserRepository{}
    controller := &UserController{}
    
    container.Register("logger", logger)
    container.Register("userRepository", repository)
    
    // 解析依赖
    if err := container.Resolve(repository); err != nil {
        fmt.Printf("解析仓库依赖错误: %v\n", err)
        return
    }
    
    if err := container.Resolve(controller); err != nil {
        fmt.Printf("解析控制器依赖错误: %v\n", err)
        return
    }
    
    // 使用控制器
    controller.GetUser(42)
}

StructTag 结构体标签

通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签(StructTag)。结构体标签是对结构体字段的额外信息标签。

JSON、BSON 等格式进行序列化及对象关系映射(Object Relational Mapping,简称 ORM)系统都会用到结构体标签,这些系统使用标签设定字段在处理时应该具备的特殊属性和可能发生的行为。这些信息都是静态的,无须实例化结构体,可以通过反射获取到。

StructTag具有两个方法

func (tag StructTag) Get(key string) string //根据 Tag 中的键获取对应的值
func (tag StructTag) Lookup(key string) (value string, ok bool) //根据 Tag 中的键,查询值是否存在
type Stu struct {
    name string `json:"name" yaml:"n"`
    age  int    `json:"age" yaml:"a"`
   //表示json序列化的时候该字段忽略
}

s := Stu{name: "lucy", age: 18}
typ := reflect.TypeOf(s)

tag1 := typ.Field(0).Tag
tag2 := typ.Field(1).Tag
//name字段Tag为,json:name,yaml:n
fmt.Printf("name字段Tag为,json:%s,yaml:%s\n", tag1.Get("json"), tag1.Get("yaml"))
//age字段Tag为,json:age,yaml:a
fmt.Printf("age字段Tag为,json:%s,yaml:%s\n", tag2.Get("json"), tag2.Get("yaml"))

常见结构体标签

JSON

`json:"fieldname,omitempty"` // omitempty表示值为零值时忽略

DB

`db:"column_name"`

表单

`form:"field_name"`

验证

`validate:"required,min=3,max=50"`