Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 进阶篇

    • 🚀 进阶篇
    • 方法
    • 接口
    • 错误处理
    • Goroutine
    • Channel
    • 包管理
    • 单元测试

包管理

Go的包管理让代码组织变得简单清晰,Go Modules是现代Go项目的标准。我刚开始学Go的时候,还是 GOPATH 时代,那种目录结构的限制让我很不适应。后来 Go Modules 出来了,真的是解放了生产力!现在管理依赖跟 npm 一样方便。

包的基础

什么是包?

包(Package)是Go代码组织的基本单位,一个目录下的所有.go文件属于同一个包。

myproject/
├── main.go          // package main
├── utils/
│   ├── string.go    // package utils
│   └── math.go      // package utils
└── models/
    └── user.go      // package models

包声明

// 每个.go文件第一行
package main    // 可执行程序
package utils   // 库包

导入包

import "fmt"                    // 单个包
import "github.com/gin-gonic/gin"  // 第三方包

import (                        // 多个包
    "fmt"
    "strings"
    "myproject/utils"           // 本地包
)

import (
    "fmt"
    str "strings"              // 别名
    . "math"                   // 点导入(不推荐)
    _ "database/sql"           // 空白导入(只执行init)
)

Go Modules

初始化模块

# 创建新模块
mkdir myproject && cd myproject
go mod init github.com/username/myproject

# 或者简单名称
go mod init myproject

生成 go.mod 文件:

module myproject

go 1.21

添加依赖

# 自动下载依赖
go get github.com/gin-gonic/gin

# 指定版本
go get github.com/gin-gonic/gin@v1.9.0

# 更新依赖
go get -u github.com/gin-gonic/gin

# 清理未使用的依赖
go mod tidy

go.mod 文件

module myproject

go 1.21

require (
    github.com/gin-gonic/gin v1.9.0
    github.com/go-sql-driver/mysql v1.7.0
)

require (
    // 间接依赖
    github.com/bytedance/sonic v1.8.0 // indirect
)

go.sum 文件

记录依赖的校验和,保证可重复构建:

github.com/gin-gonic/gin v1.9.0 h1:...
github.com/gin-gonic/gin v1.9.0/go.mod h1:...

项目结构

标准项目布局

myproject/
├── cmd/                    # 可执行程序入口
│   └── myapp/
│       └── main.go
├── internal/               # 私有代码(不能被其他项目导入)
│   ├── config/
│   │   └── config.go
│   └── service/
│       └── user.go
├── pkg/                    # 可被外部导入的库
│   └── utils/
│       └── string.go
├── api/                    # API定义(如proto文件)
├── web/                    # 静态资源
├── configs/                # 配置文件
├── scripts/                # 脚本
├── go.mod
├── go.sum
└── README.md

简单项目

myapp/
├── main.go
├── handler/
│   └── user.go
├── model/
│   └── user.go
├── service/
│   └── user.go
├── go.mod
└── go.sum

可见性规则

Go使用首字母大小写控制可见性:

package user

// 大写:公开(可被其他包访问)
type User struct {
    ID   int     // 公开
    Name string  // 公开
    age  int     // 私有(小写)
}

func NewUser() *User { }     // 公开
func validateUser() bool { } // 私有

// 公开常量
const MaxAge = 150

// 私有变量
var defaultAge = 18

创建和使用自己的包

项目结构

myproject/
├── main.go
├── mathutil/
│   └── math.go
└── go.mod

mathutil/math.go

package mathutil

// Add 两数相加(公开函数)
func Add(a, b int) int {
    return a + b
}

// Multiply 两数相乘
func Multiply(a, b int) int {
    return a * b
}

// max 返回较大值(私有函数)
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

// Max 公开的最大值函数
func Max(a, b int) int {
    return max(a, b)
}

main.go

package main

import (
    "fmt"
    "myproject/mathutil"
)

func main() {
    sum := mathutil.Add(1, 2)
    product := mathutil.Multiply(3, 4)
    
    fmt.Println("Sum:", sum)         // 3
    fmt.Println("Product:", product) // 12
}

init 函数

每个包可以有多个init函数,在main之前自动执行:

package config

import "fmt"

var AppName string

func init() {
    fmt.Println("config包初始化")
    AppName = "MyApp"
}

// 可以有多个init
func init() {
    fmt.Println("config包第二个init")
}

执行顺序:

  1. 导入的包的init
  2. 当前包的包级变量
  3. 当前包的init函数
  4. main函数

常用命令

# 初始化模块
go mod init <module-name>

# 添加依赖
go get <package>

# 下载依赖
go mod download

# 整理依赖
go mod tidy

# 查看依赖
go list -m all

# 查看可用版本
go list -m -versions github.com/gin-gonic/gin

# 为什么需要这个依赖
go mod why github.com/some/package

# 生成vendor目录
go mod vendor

# 验证依赖
go mod verify

私有仓库

配置GOPRIVATE

# 设置私有仓库
go env -w GOPRIVATE=github.com/mycompany/*,gitlab.mycompany.com/*

# 或在环境变量中
export GOPRIVATE=github.com/mycompany/*

使用SSH

# 配置git使用SSH
git config --global url."git@github.com:".insteadOf "https://github.com/"

实战案例:日志库

创建一个简单的日志库:

logger/
├── go.mod
├── logger.go
├── formatter.go
└── level.go

go.mod

module github.com/myuser/logger

go 1.21

level.go

package logger

// 日志级别
type Level int

const (
    DEBUG Level = iota
    INFO
    WARN
    ERROR
    FATAL
)

func (l Level) String() string {
    switch l {
    case DEBUG:
        return "DEBUG"
    case INFO:
        return "INFO"
    case WARN:
        return "WARN"
    case ERROR:
        return "ERROR"
    case FATAL:
        return "FATAL"
    default:
        return "UNKNOWN"
    }
}

formatter.go

package logger

import (
    "fmt"
    "time"
)

// Formatter 日志格式化器接口
type Formatter interface {
    Format(level Level, msg string) string
}

// TextFormatter 文本格式化器
type TextFormatter struct{}

func (f *TextFormatter) Format(level Level, msg string) string {
    return fmt.Sprintf("[%s] %s - %s\n",
        time.Now().Format("2006-01-02 15:04:05"),
        level.String(),
        msg)
}

// JSONFormatter JSON格式化器
type JSONFormatter struct{}

func (f *JSONFormatter) Format(level Level, msg string) string {
    return fmt.Sprintf(`{"time":"%s","level":"%s","msg":"%s"}`+"\n",
        time.Now().Format(time.RFC3339),
        level.String(),
        msg)
}

logger.go

package logger

import (
    "io"
    "os"
    "sync"
)

// Logger 日志记录器
type Logger struct {
    mu        sync.Mutex
    level     Level
    output    io.Writer
    formatter Formatter
}

// 默认logger
var defaultLogger = New()

// New 创建新的Logger
func New() *Logger {
    return &Logger{
        level:     INFO,
        output:    os.Stdout,
        formatter: &TextFormatter{},
    }
}

// SetLevel 设置日志级别
func (l *Logger) SetLevel(level Level) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.level = level
}

// SetOutput 设置输出目标
func (l *Logger) SetOutput(w io.Writer) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.output = w
}

// SetFormatter 设置格式化器
func (l *Logger) SetFormatter(f Formatter) {
    l.mu.Lock()
    defer l.mu.Unlock()
    l.formatter = f
}

// log 内部日志方法
func (l *Logger) log(level Level, msg string) {
    if level < l.level {
        return
    }
    
    l.mu.Lock()
    defer l.mu.Unlock()
    
    formatted := l.formatter.Format(level, msg)
    l.output.Write([]byte(formatted))
}

// Debug 调试日志
func (l *Logger) Debug(msg string) {
    l.log(DEBUG, msg)
}

// Info 信息日志
func (l *Logger) Info(msg string) {
    l.log(INFO, msg)
}

// Warn 警告日志
func (l *Logger) Warn(msg string) {
    l.log(WARN, msg)
}

// Error 错误日志
func (l *Logger) Error(msg string) {
    l.log(ERROR, msg)
}

// Fatal 致命错误日志
func (l *Logger) Fatal(msg string) {
    l.log(FATAL, msg)
    os.Exit(1)
}

// 包级别函数,使用默认logger
func Debug(msg string) { defaultLogger.Debug(msg) }
func Info(msg string)  { defaultLogger.Info(msg) }
func Warn(msg string)  { defaultLogger.Warn(msg) }
func Error(msg string) { defaultLogger.Error(msg) }
func Fatal(msg string) { defaultLogger.Fatal(msg) }

func SetLevel(level Level)      { defaultLogger.SetLevel(level) }
func SetOutput(w io.Writer)     { defaultLogger.SetOutput(w) }
func SetFormatter(f Formatter)  { defaultLogger.SetFormatter(f) }

使用日志库

package main

import (
    "github.com/myuser/logger"
)

func main() {
    // 使用默认logger
    logger.Info("应用启动")
    logger.Debug("这条不会显示,级别不够")
    
    // 设置级别
    logger.SetLevel(logger.DEBUG)
    logger.Debug("现在可以显示了")
    
    // 使用JSON格式
    logger.SetFormatter(&logger.JSONFormatter{})
    logger.Info("JSON格式日志")
    
    // 创建新的logger
    myLogger := logger.New()
    myLogger.SetLevel(logger.WARN)
    myLogger.Warn("警告信息")
}

版本管理

语义化版本

v1.2.3
│ │ │
│ │ └─ 补丁版本(bug修复)
│ └─── 次版本(新功能,向后兼容)
└───── 主版本(不兼容的变更)

发布版本

# 打标签
git tag v1.0.0
git push origin v1.0.0

# v2及以上需要修改go.mod
module github.com/myuser/mylib/v2

引入v2

import "github.com/myuser/mylib/v2"

练习

  1. 创建一个包含多个包的项目
  2. 实现一个简单的配置读取包
  3. 使用go mod管理第三方依赖
参考答案
// 项目结构
// myapp/
// ├── main.go
// ├── config/
// │   └── config.go
// └── go.mod

// go.mod
module myapp

go 1.21

// config/config.go
package config

import (
    "encoding/json"
    "os"
)

type Config struct {
    AppName  string `json:"app_name"`
    Port     int    `json:"port"`
    Debug    bool   `json:"debug"`
    Database struct {
        Host     string `json:"host"`
        Port     int    `json:"port"`
        Username string `json:"username"`
        Password string `json:"password"`
        DBName   string `json:"dbname"`
    } `json:"database"`
}

func Load(filename string) (*Config, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    config := &Config{}
    decoder := json.NewDecoder(file)
    if err := decoder.Decode(config); err != nil {
        return nil, err
    }
    
    return config, nil
}

// main.go
package main

import (
    "fmt"
    "myapp/config"
)

func main() {
    cfg, err := config.Load("config.json")
    if err != nil {
        fmt.Println("加载配置失败:", err)
        return
    }
    
    fmt.Printf("应用: %s\n", cfg.AppName)
    fmt.Printf("端口: %d\n", cfg.Port)
    fmt.Printf("数据库: %s:%d\n", cfg.Database.Host, cfg.Database.Port)
}

包管理让项目井井有条,下一节学习单元测试!

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
Channel
Next
单元测试