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

188 天前
 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的辛勤付出。

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