启动一个最小 Go-Spring 应用

发布时间:2026/6/26 5:03:00

启动一个最小 Go-Spring 应用 第一步咱们先不写业务代码只来确认 Go-Spring 应用怎么启动。代码如下func main() { gs.Run() }完整代码在 examples/01-run-only/main.go。上面这段代码虽然看起来很短但是已经足够让程序进入 Go-Spring 的应用生命周期。gs.Run()会创建应用加载配置初始化日志刷新 IoC 容器启动内置 HTTP Server并监听SIGINT/SIGTERM最后在进程退出时还能执行优雅关闭。使用下面的命令运行示例cd examples/01-run-only go run .此时控制台会打印如下信息____ ___ ____ ____ ____ ___ _ _ ____ / ___| / _ \ / ___| | _ \ | _ \ |_ _| | \ | | / ___| | | _ | | | | _____ \___ \ | |_) | | |_) | | | | \| | | | _ | |_| | | |_| | |_____| ___) | | __/ | _ | | | |\ | | |_| | \____| \___/ |____/ |_| |_| \_\ |___| |_| \_| \____| go-springv1.3.0 https://github.com/go-spring/ [INFO][2026-05-02T19:13:07.837][...ing/spring-core/gs/internal/gs_app/app.go:289] _app_def||msgready to serve requestsready to serve requests表示应用已经启动并且成功监听:9090。使用下面的命令访问根路径curl http://127.0.0.1:9090/会得到404 page not found这里 404 是预期结果。它说明 HTTP Server 已经启动了只是还没有 handler 能处理这个路径。按下CtrlC可以停止程序接着 Go-Spring 进入关闭流程。2. 添加一个标准库 HTTP 路由上一章的应用已经能够启动但是还没有业务入口所以任何请求都会返回 404。现在咱们先不着急引入 IoC而是用 Go 标准库注册一个最普通的 HTTP handler。代码如下func main() { http.HandleFunc(/hello, func(w http.ResponseWriter, r *http.Request) { _, _ w.Write([]byte(hello from net/http\n)) }) gs.Run() }完整代码在 examples/02-stdlib-http/main.go。使用下面的命令运行示例cd examples/02-stdlib-http go run .然后访问新增的/hello路由curl http://127.0.0.1:9090/hello这次不再是 404 了而是返回预期中的响应hello from net/http可以看到应用已经从“只能启动”变成了“能处理 HTTP 请求”。不过 handler 目前还是一个匿名函数业务状态和配置都写不进去。3. 把业务对象注册为 root bean在上一章中/hello是直接写在main函数里的匿名函数。它能验证 HTTP 处理有效但不好继续扩展。因为一旦问候语、目标用户、校验规则需要变成配置匿名函数就会显得很别扭。所以本章会创建一个业务对象GreetingRoot让它持有配置并且把它的方法实现成 handler。代码如下type GreetingRoot struct { Greeting string value:${demo.greeting:Hello} expr:$ ! Audience string value:${demo.audience:Go-Spring} expr:$ ! } func (g *GreetingRoot) Hello(w http.ResponseWriter, r *http.Request) { _, _ fmt.Fprintf(w, %s, %s!\n, g.Greeting, g.Audience) } func main() { root : GreetingRoot{} http.HandleFunc(/hello, root.Hello) gs.Configure(func(app gs.App) { app.Root(root) }).Run() }完整代码在 examples/03-configure-root-bean/main.go。与上一章相比这次的代码有两个实质性变化handler 不再是匿名函数而是GreetingRoot.Hello方法业务状态进入了结构体。root被传给了app.Root(root)所以 Go-Spring 会在启动过程中处理它的字段标签。GreetingRoot 字段上的valuetag 表示配置绑定关系${demo.greeting:Hello}表示读取配置项demo.greeting的值如果没有在任何地方配置就使用默认值Hello。expr:$ ! 表示绑定后的值不能为空如果不满足条件应用就会在启动阶段失败而不是等到请求进来才暴露问题。使用下面的命令运行示例cd examples/03-configure-root-bean go run .然后访问/hello路由curl http://127.0.0.1:9090/hello我们会得到预期中的响应Hello, Go-Spring!这里的Hello和Go-Spring都来自字段 tag 中的默认值。也就是说尽管应用仍然使用标准库路由但业务对象已经进入 Go-Spring 的配置绑定流程了。4. 用外部配置覆盖默认值上一章咱们已经把配置绑定关系写进了GreetingRoot但运行结果还完全依赖 tag 里的默认值。真实应用一般不会只靠默认值运行环境之间的差异常常会放在配置文件、环境变量或启动参数里。这一章仍然沿用上一章的代码代码没有任何变化只是在示例目录中增加一个配置文件。GreetingRoot仍然绑定同样的两个配置项type GreetingRoot struct { Greeting string value:${demo.greeting:Hello} expr:$ ! Audience string value:${demo.audience:Go-Spring} expr:$ ! }但在./conf目录下新增一个配置文件 app.properties内容如下demo.greetingHello from ./conf/app.properties demo.audienceconfig file完整代码在 examples/04-config-overrides/main.go。使用下面的命令运行示例cd examples/04-config-overrides go run .然后访问/hello路由curl http://127.0.0.1:9090/hello这时响应会从默认值变成配置文件里的值Hello from ./conf/app.properties, config file!不改变配置文件的内容咱们可以用环境变量覆盖其中一个配置项比如GS_DEMO_AUDIENCE它会映射成demo.audienceGS_DEMO_AUDIENCEenv var go run . curl http://127.0.0.1:9090/hello执行上面的命令会看到响应从配置文件里的值变成了环境变量里的值Hello from ./conf/app.properties, env var!咱们还可以使用命令行参数覆盖配置写法是-Dkeyvaluego run . -Ddemo.audiencecmd arg curl http://127.0.0.1:9090/hello执行上面的命令会看到响应从配置文件里的值变成了命令行参数里的值Hello from ./conf/app.properties, cmd arg!这一章咱们没有改变任何代码就让上一章的配置绑定变得更方便运维了。不过GreetingRoot虽然已经可以被 Go-Spring 绑定配置了但它还是在main中手动创建的。5. 用容器装配 HTTP mux 和中间件前面几章咱们一直把对象创建和路由注册写在main函数里虽然适合入门但是当请求日志、耗时统计、请求 ID、panic recovery 这类横切逻辑出现时HTTP 入口就不应该散落在main中了。因此这一章咱们使用 Go-Spring 来构造所有的组件。首先咱们把配置收拢成一个配置结构体。注意这里的 tag 不再写demo.greeting而是写greeting和audience因为注册构造函数时会指定整体前缀${demo}。代码如下type GreetingConfig struct { Greeting string value:${greeting:Hello} expr:$ ! Audience string value:${audience:Go-Spring} expr:$ ! } type Controller struct { cfg GreetingConfig } func NewController(cfg GreetingConfig) *Controller { return Controller{cfg: cfg} } func (c *Controller) Hello(w http.ResponseWriter, r *http.Request) { _, _ fmt.Fprintf(w, %s, %s!\n, c.cfg.Greeting, c.cfg.Audience) }然后咱们显式创建一个*gs.HttpServeMux。它的内部仍然使用标准库http.NewServeMux()只是最终返回给 Go-Spring 的是带中间件的 handler。代码如下func NewHTTPMux(c *Controller) *gs.HttpServeMux { mux : http.NewServeMux() mux.HandleFunc(/hello, c.Hello) return gs.HttpServeMux{Handler: logging(mux)} } func logging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start : time.Now() next.ServeHTTP(w, r) log.Printf(method%s path%s elapsed%s, r.Method, r.URL.Path, time.Since(start)) }) }这次咱们在代码中添加了一个logging中间件可以记录请求的方法、路径和耗时。最后咱们把构造函数注册给容器func init() { gs.Provide(NewController, gs.TagArg(${demo})) gs.Provide(NewHTTPMux) } func main() { gs.Run() }完整代码在 examples/05-http-middleware-mux/main.go。使用下面的命令运行示例cd examples/05-http-middleware-mux go run .然后访问/hello路由curl -i http://127.0.0.1:9090/hello可以看到这次响应变成了下面这样Hello with middleware, custom mux!同时控制台上还会打印请求的方法、路径和耗时说明请求确实经过了logging中间件。这一章咱们完成了一个重要转折main又回到了只负责gs.Run()对象创建、配置绑定、HTTP mux 组装则统统交给容器。6. 把 controller 和 service 拆成多个 bean上一章咱们已经用容器创建了 controller 和 HTTP mux但问候语仍然由 controller 自己拼出来。随着业务增长controller 应该更专注于 HTTP 请求和响应业务逻辑和规则应该放进 service。因此这一章咱们新增一个GreetingService让 controller 通过构造函数依赖 service。代码如下type GreetingService struct { Greeting string value:${demo.greeting:Hello} expr:$ ! } func NewGreetingService() *GreetingService { return GreetingService{} } func (s *GreetingService) Message(audience string) string { return fmt.Sprintf(%s, %s!, s.Greeting, audience) } type Controller struct { service *GreetingService Audience string value:${demo.audience:Go-Spring} expr:$ ! } func NewController(service *GreetingService) *Controller { return Controller{service: service} } func (c *Controller) Hello(w http.ResponseWriter, r *http.Request) { _, _ fmt.Fprintln(w, c.service.Message(c.Audience)) }注册代码也不复杂只需要多提供一个构造函数func init() { gs.Provide(NewGreetingService) gs.Provide(NewController) gs.Provide(NewHTTPMux) }完整代码在 examples/06-multi-bean-di/main.go。使用下面的命令运行示例cd examples/06-multi-bean-di go run .然后访问/hello路由curl http://127.0.0.1:9090/hello可以看到预期中的响应Hello from service, controller config!这一章咱们展示的是构造函数注入。NewController的参数声明了它需要*GreetingServiceGo-Spring 就会先创建 service再把它传给 controller。业务代码不需要自己查找依赖也不需要在main中手工组装对象图。7. 注册一个外部客户端 bean上一章的 service 内部只有一个字段但真实服务通常还会依赖 Redis、数据库、消息队列等外部客户端。为了让示例聚焦在 Go-Spring 的注册方式上本章用一个轻量的RedisClient来模拟外部客户端它会读取配置并打印日志但不会连接真实的 Redis。首先定义 Redis 配置和客户端构造函数type RedisConfig struct { Addr string value:${addr} expr:$ ! Password string value:${password:} } type RedisClient struct {

相关新闻