Go-Spring 轻装上阵,重新出发 v1.0.0 !

2023-11-03 10:25:13 +08:00
 limpo

前言

多年前曾经梦想着能有一天能在 Go 语言中使用上 Spring 那样强大的框架,幸运的是我找到了go-spring,一款类似 Spring 的库,我很喜欢它,并且在我的项目中深度使用它,它给我带来了非常酷的编程体验。

然而很不幸的是最近 go-spring 停止更新了(最近的一次提交停留在九个月之前),一些问题无法得到有效的解决,因此我决定创建一个基于 go-spring 的开源库,我为它修复了一系列 bug ,并添加了一些新得特性,并对代码结构进行了简化,将外部生态从核心库中移除,仅保留核心功能,外部生态预计将来将作为独立的仓库存在,我将持续继续将它维护下去,也希望有感兴趣的人可以一起参与进来一起持续开发它。

仓库地址: https://github.com/go-spring-projects/go-spring

特性

动态属性例子

允许在运行时动态刷新属性,不仅支持基本数据类型,还支持结构、切片和 Map 类型。

package main

import (
	"fmt"
	"log/slog"
	"net/http"

	"github.com/go-spring-projects/go-spring/dync"
	"github.com/go-spring-projects/go-spring/gs"
)

type Handler struct {
	Open dync.Bool `value:"${server.open:=true}"`
}

func (h *Handler) OnInit(ctx gs.Context) error {

	http.HandleFunc("/server/status", func(writer http.ResponseWriter, request *http.Request) {
		if !h.Open.Value() {
			http.Error(writer, "server closed", http.StatusNotAcceptable)
			return
		}

		fmt.Fprint(writer, "server running")
	})
	return nil
}

type Server struct {
	Logger *slog.Logger `logger:""`
}

func (s *Server) OnInit(ctx gs.Context) error {

	props := ctx.(gs.Container).Properties()

	http.HandleFunc("/server/status/open", func(writer http.ResponseWriter, request *http.Request) {
		props.Set("server.open", "true")
		s.Logger.Info("server opened")
	})

	http.HandleFunc("/server/status/close", func(writer http.ResponseWriter, request *http.Request) {
		props.Set("server.open", "false")
		s.Logger.Info("server closed")
	})

	go func() {
		http.ListenAndServe(":7878", nil)
	}()

	return nil
}

func main() {

	gs.Object(new(Handler))
	gs.Object(new(Server))

	if err := gs.Run(); nil != err {
		panic(err)
	}
}


// Output:
// 
// $ curl http://127.0.0.1:7878/server/status
// server running
//
// $ curl http://127.0.0.1:7878/server/status/close
//
// $ curl http://127.0.0.1:7878/server/status
// server closed
//
// $ curl http://127.0.0.1:7878/server/status/open
//
// $ curl http://127.0.0.1:7878/server/status
// server running

参数校验器例子

Go-Spring允许您注册自定义值验证器。如果值验证失败,则Go-Spring将在启动阶段报告一个错误。

在这例子中, 我们将使用 go-validator/validator, 您可以参考这个示例来注册您的自定义验证器。

package main

import (
	"fmt"
	"log/slog"

	"github.com/go-spring-projects/go-spring/conf"
	"github.com/go-spring-projects/go-spring/gs"
	"gopkg.in/validator.v2"
)

const validatorTagName = "validate"

type validatorWrapper struct {
	validator *validator.Validator
}

func (v *validatorWrapper) Field(tag string, i interface{}) error {
	if 0 == len(tag) {
		return nil
	}
	return v.validator.Valid(i, tag)
}

func init() {
	conf.Register(validatorTagName, &validatorWrapper{validator: validator.NewValidator().WithTag(validatorTagName)})
}

//--------------------------------------------------------------------------

type DBOptions struct {
	UserName string `value:"${username}"`
	Password string `value:"${password}"`
	IP       string `value:"${ip}"`
	Port     int32  `value:"${port}" validate:"min=1024,max=65535"`
	DB       string `value:"${db}" validate:"nonzero"`
}

type MysqlDatabase struct {
	Logger  *slog.Logger `logger:""`
	Options DBOptions    `value:"${db}"`
}

func (md *MysqlDatabase) OnInit(ctx gs.Context) error {
	md.Logger.Info("mysql connection summary",
		"url", fmt.Sprintf("mysql://%s:%s@%s:%d/%s", md.Options.UserName, md.Options.Password, md.Options.IP, md.Options.Port, md.Options.DB))
	return nil
}

func main() {

	gs.Property("db.username", "admin")
	gs.Property("db.password", "123456")
	gs.Property("db.ip", "127.0.0.1")
	gs.Property("db.port", "0") // set db.port=0
	gs.Property("db.db", "test")

	gs.Object(new(MysqlDatabase))

	if err := gs.Run(); nil != err {
		panic(err)
	}
}

//
// Output:
// panic: container refresh failed
// ↳object bean "main/main.MysqlDatabase:MysqlDatabase" /projects/go-project/gocase/validator/main.go:58
// ↳bind MysqlDatabase.Options error: validate MysqlDatabase.Options.Port error: less than min

更多特性请参考说明: go-spring-projects/go-spring

最后还要感谢原作者lvan100的辛勤付出。

6888 次点击
所在节点    Go 编程语言
111 条回复
banmuyutian
2023-11-03 10:32:45 +08:00
Go,Spring,轻装上阵,这三个词同时出现有点难绷……
iold
2023-11-03 10:33:55 +08:00
Spring 轻吗
yazinnnn0
2023-11-03 10:37:12 +08:00
讲道理, 只说 spring 是轻量的, 当初 spring 开发出来就是为了取代重量级框架 EJB
sunny352787
2023-11-03 10:39:59 +08:00
为啥要整这么复杂? gin 的路由不好用吗? go 又没有注解,你是不是还得自己加个依赖注入啥的?可别整这么一大坨了,就简单写几个 func 处理逻辑挺好
biu7
2023-11-03 10:41:20 +08:00
你这项目名看上去就不轻装
limpo
2023-11-03 10:43:32 +08:00
@banmuyutian @iold @yazinnnn0 相对原版 go-spring 确实是轻装了许多,精简了很多,只保留了核心的依赖注入相关的东西,而这部分代码是很轻量级的,也才几千行 😀
limpo
2023-11-03 10:44:09 +08:00
@biu7 哈哈 因为确实从 Java Spring 启发而来的 😂
limpo
2023-11-03 10:46:30 +08:00
@sunny352787 简单的项目确实不需要它,直接撸就行,当你的项目有一定规模的时候,这个时候维护依赖初始化,以及各种属性初始化,就会变得繁琐,也许为了加一个配置参数,要修改很多相关的多个接口
ixx
2023-11-03 10:47:48 +08:00
这东西应该只对 java 转 go 的友好但也是一种限制
毕竟 原生写 go 的人应该都不习惯 java 这一套
所以用习惯了自己觉得会 go 了 可能一看别人代码还是会懵 毕竟实现思路不一样
xiaocaiji111
2023-11-03 10:47:54 +08:00
啊这,我还是用 spring 吧,轻量级项目用 gin 甚至不用框架都搞定了。
abcdexx
2023-11-03 10:48:26 +08:00
g...g.... gava?
tinyzilan123
2023-11-03 10:51:55 +08:00
可以改名成 go-summer
简称还是 gs
Oktfolio
2023-11-03 10:53:29 +08:00
Java 警告
limpo
2023-11-03 10:54:39 +08:00
@ixx 是的,我开始接触到依赖注入这个概念的时候,大呼魔法啊,怎么加个注解对象就有了,后来一顿摸索发现这个东西真的是写业务的神器啊,可以全身贯注到业务上去而不必费心依赖/配置怎么传,或者初始化来
ZZ74
2023-11-03 10:55:22 +08:00
写 Go 不都是奔着 轻快 去的的么....来个 spring 怎么说呢 应该会有意见吧
limpo
2023-11-03 10:56:16 +08:00
@xiaocaiji111 是的 Java 里面有很多好东西,比如 netty 的流水线思想,所以我也搞了一个 go-netty ,https://github.com/go-netty/go-netty 😂
limpo
2023-11-03 10:57:18 +08:00
@ZZ74 不矛盾啊,Go 也有写大规模项目的需求,小规模的项目自己随便弄都行,上了规模它的好处就显现出来了
limpo
2023-11-03 10:57:45 +08:00
@Oktfolio 真香警告 😀
limpo
2023-11-03 10:58:55 +08:00
@tinyzilan123 还是你逗 😂,取名真的很困难,这不是为了沾 Java 的光😂
Maboroshii
2023-11-03 11:02:25 +08:00
是写一个小功能至少要写三个类的啰嗦写法吗(没写过 java 不太清楚,但是我同事 java 转 go 就是这么啰嗦。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/988146

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX