Go-Spring is a high-performance framework for modern Go application development, inspired by the Spring / Spring Boot ecosystem in the Java community. Its design philosophy deeply integrates the characteristics of the Go language, retaining mature development paradigms from the Spring world, such as Dependency Injection (DI), auto-configuration, and lifecycle management, while avoiding the complexity and performance overhead that traditional frameworks might bring. Go-Spring allows developers to enjoy higher levels of abstraction and automation while maintaining Go's native style and execution efficiency.
Whether you are developing a monolithic application or building a microservices-based distributed system, Go-Spring provides a unified and flexible development experience. It simplifies the project setup process in an "out-of-the-box" manner, reduces the need for boilerplate code, and does not impose an intrusive framework structure, allowing developers to focus more on business logic implementation. Go-Spring is committed to improving development efficiency, maintainability, and system consistency, making it a milestone framework in the Go language ecosystem.
Go-Spring offers a rich set of practical features to help developers efficiently build modern Go applications:
-
β‘ Extreme Startup Performance
- Bean registration based on Go's
init()
mechanism, with no runtime scanning, ensuring rapid startup; - Injection relies only on reflection during the initialization phase, with zero reflection at runtime, maximizing performance.
- Bean registration based on Go's
-
π§© Out-of-the-Box, Non-Intrusive Design
- Supports struct tag injection and chained configuration, making it easy to use without mastering complex concepts;
- Does not strongly depend on interfaces or inheritance structures, keeping business logic in native Go style.
-
π Hot Configuration Updates, Real-Time Application
- Supports loading configurations from multiple formats and sources, with environment isolation and dynamic refresh capabilities;
- Configuration changes can be applied immediately, facilitating gray releases and online parameter tuning.
-
βοΈ Flexible Dependency Injection Mechanism
- Supports constructor injection, struct field injection, and constructor parameter injection in various ways;
- Injection behavior can be flexibly adjusted based on configuration items or runtime environments.
-
π Multi-Model Service Startup Support
- Built-in HTTP Server launcher for quick deployment of web services;
- Supports three running models:
Runner
,Job
, andServer
, adapting to different service forms; - Comprehensive lifecycle hooks support graceful startup and shutdown.
-
π§ͺ Built-In Testing Capabilities
- Seamlessly integrates with
go test
, supports Bean Mock and dependency injection, making it easy to write unit tests.
- Seamlessly integrates with
Go-Spring uses Go Modules for dependency management, making installation straightforward:
go get github.com/go-spring/spring-core
Go-Spring emphasizes "out-of-the-box" usage. Below are two examples to quickly experience its powerful capabilities.
More examples can be found at: gs/examples
func main() {
http.HandleFunc("/echo", func (w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world!"))
})
gs.Run()
}
Access method:
curl http://127.0.0.1:9090/echo
# Output: hello world!
β
No complex configuration required; the Go standard library http
can be used directly;
β
gs.Run()
manages the lifecycle, supporting graceful exit, signal listening, and other capabilities.
func init() {
gs.Object(&Service{})
gs.Provide(func (s *Service) *http.ServeMux {
http.HandleFunc("/echo", s.Echo)
http.HandleFunc("/refresh", s.Refresh)
return http.DefaultServeMux
})
sysconf.Set("start-time", time.Now().Format(timeLayout))
sysconf.Set("refresh-time", time.Now().Format(timeLayout))
}
const timeLayout = "2006-01-02 15:04:05.999 -0700 MST"
type Service struct {
StartTime time.Time `value:"${start-time}"`
RefreshTime gs.Dync[time.Time] `value:"${refresh-time}"`
}
func (s *Service) Echo(w http.ResponseWriter, r *http.Request) {
str := fmt.Sprintf("start-time: %s refresh-time: %s",
s.StartTime.Format(timeLayout),
s.RefreshTime.Value().Format(timeLayout))
w.Write([]byte(str))
}
func (s *Service) Refresh(w http.ResponseWriter, r *http.Request) {
sysconf.Set("refresh-time", time.Now().Format(timeLayout))
gs.RefreshProperties()
w.Write([]byte("OK!"))
}
Access method:
curl http://127.0.0.1:9090/echo # View current time
curl http://127.0.0.1:9090/refresh # Trigger hot refresh
β
value
tag automatically binds configuration;
β
gs.Dync[T]
implements field hot updates;
β
gs.Object
and gs.Provide()
register Beans.
Go-Spring provides a flexible and powerful configuration loading mechanism, supporting configuration items from multiple sources, easily meeting the needs of multi-environment and multi-deployment scenarios. Whether for local development, containerized deployment, or cloud-native architectures, Go-Spring can provide consistent and flexible configuration support.
To address the complex requirements of diverse configuration sources and coverage relationships, Go-Spring has built a hierarchical configuration loading system. The system automatically merges configuration items from different sources at startup and resolves and overwrites them according to priority rules.
-
Command Line Arguments
Use the-Dkey=value
format to inject, with the highest priority, suitable for quickly overriding runtime configurations. -
Environment Variables
Directly read from the operating system environment variables, convenient for injecting configurations in containers or CI/CD pipelines. -
Remote Files
Supports pulling configurations from a configuration center, with scheduled pull and hot update capabilities, suitable for centralized configuration management. -
Local Files
Supports common formats such as.yaml
,.properties
, and.toml
, suitable for most development and deployment scenarios. -
In-Memory Configuration (
sysconf
)
Suitable for testing scenarios or runtime temporary configuration injection, offering high flexibility. -
Struct Default Values
Set default values through struct tags, serving as the final fallback mechanism in the configuration system.
Example: Property Binding
type AppConfig struct {
Name string `value:"${app.name}"`
Version string `value:"${app.version}"`
}
In Go-Spring, Beans are the core building units of an application, similar to the component concept in other dependency injection frameworks. The entire system is organized around the registration, initialization, dependency injection, and lifecycle management of Beans. Go-Spring does not rely on runtime reflection but achieves type-safe and high-performance Bean management through compile-time metadata generation and explicit calls. This design is particularly suitable for building high-performance, maintainable large-scale systems.
The framework adopts a combination of "explicit registration + tag declaration + conditional assembly," giving developers clear control over Bean registration and dependency relationships. Since it does not rely on runtime container scanning and has no magic configurations, this approach ensures a good development experience while further enhancing debugging and operational controllability, achieving the goal of zero intrusion and (runtime) zero reflection.
Go-Spring provides multiple ways to register Beans:
gs.Object(obj)
- Registers an existing object as a Beangs.Provide(ctor, args...)
- Uses a constructor to generate and register a Beangs.Register(bd)
- Registers a complete Bean definition (suitable for low-level encapsulation or advanced usage)gs.GroupRegister(fn)
- Batch registers multiple Beans (commonly used for module initialization and other scenarios)
Example:
gs.Object(&Service{}) // Register a struct instance
gs.Provide(NewService) // Register using a constructor
gs.Provide(NewRepo, gs.ValueArg("db")) // Constructor with parameters
gs.Register(gs.NewBean(NewService)) // Complete definition registration
// Batch register multiple Beans
gs.GroupRegister(func (p conf.Properties) []*gs.BeanDefinition {
return []*gs.BeanDefinition{
gs.NewBean(NewUserService),
gs.NewBean(NewOrderService),
}
})
Go-Spring offers multiple flexible dependency injection methods.
Inject configuration items or Beans into struct fields through tags, suitable for most scenarios.
type App struct {
Logger *log.Logger `autowire:""`
Filters []*Filter `autowire:"access,*?"`
StartTime time.Time `value:"${start-time}"`
}
value:"${...}"
indicates binding configuration values;autowire:""
indicates automatic injection by type and name;autowire:"access,*?"
indicates injecting multiple Beans by type and name.
Complete automatic injection through function parameters; Go-Spring automatically infers and matches dependent Beans.
func NewService(logger *log.Logger) *Service {
return &Service{Logger: logger}
}
gs.Provide(NewService)
Explicitly define injection behavior through parameter wrappers, more suitable for complex construction logic:
gs.Provide(NewService,
TagArg("${log.level}"), // Inject from configuration
ValueArg("value"), // Direct value injection
BindArg(parseFunc), // Option function injection
)
Available parameter types:
TagArg(tag)
: Extract values from configurationValueArg(value)
: Use fixed valuesIndexArg(i, arg)
: Inject by parameter positionBindArg(fn, args...)
: Inject through option functions
Developers can explicitly declare initialization, destruction, dependencies, conditional registration, and other behaviors for each Bean.
gs.Provide(NewService).
Name("myService").
Init(func (s *Service) { ... }).
Destroy(func (s *Service) { ... }).
Condition(OnProperty("feature.enabled")).
DependsOn(gs.BeanSelectorFor[*Repo]()).
Export(gs.As[ServiceInterface]()).
AsRunner()
Configuration item descriptions:
Name(string)
: Specifies the Bean nameInit(fn)
: Initialization function (supports method name strings)Destroy(fn)
: Destruction function (supports method name strings)DependsOn(...)
: Specifies dependencies on other BeansCondition(...)
: Conditional registration controlExport(...)
: Exports the Bean as an interface, supporting multiple interface exports
Inspired by Spring's @Conditional
concept, Go-Spring implements a flexible and powerful conditional injection system.
It dynamically decides whether to register a Bean based on configuration, environment, context, and other conditions,
achieving "on-demand assembly." This is particularly crucial in multi-environment deployment, plugin architectures,
feature toggles, and gray release scenarios.
OnProperty("key")
: Activates when the specified configuration key existsOnMissingProperty("key")
: Activates when the specified configuration key does not existOnBean[Type]("name")
: Activates when a Bean of the specified type/name existsOnMissingBean[Type]("name")
: Activates when a Bean of the specified type/name does not existOnSingleBean[Type]("name")
: Activates when a Bean of the specified type/name is the only instanceOnFunc(func(ctx CondContext) bool)
: Uses custom condition logic to determine activation
Example:
gs.Provide(NewService).
Condition(OnProperty("service.enabled"))
The NewService
will only be registered if service.enabled=true
exists in the configuration file.
Go-Spring supports combining multiple conditions to build more complex judgment logic:
Not(...)
- Negates a conditionAnd(...)
- All conditions must be satisfiedOr(...)
- Any condition being satisfied is sufficientNone(...)
- All conditions must not be satisfied
Example:
gs.Provide(NewService).
Condition(
And(
OnProperty("feature.enabled"),
Not(OnBean[*DeprecatedService]()),
),
)
This Bean will be enabled when feature.enabled
is turned on and *DeprecatedService
is not registered.
Go-Spring supports a lightweight hot configuration update mechanism. Through the generic type gs.Dync[T]
and
gs.RefreshProperties()
,
applications can perceive configuration changes in real-time during runtime without restarting. This is particularly
crucial for gray releases, dynamic parameter tuning, and configuration center integration in microservices
architectures.
- Use
gs.Dync[T]
to declare dynamic fields
Wrap fields with the generic type gs.Dync[T]
to listen for configuration changes and automatically update:
type Config struct {
Version gs.Dync[string] `value:"${app.version}"`
}
Use
.Value()
to get the current value; the framework automatically updates this value when the configuration changes.
- Call
gs.RefreshProperties()
to trigger a refresh
After the configuration changes, call this method to immediately update all dynamic fields:
gs.RefreshProperties()
const versionKey = "app.version"
type App struct {
Version gs.Dync[string] `value:"${app.version:=v0.0.1}"`
}
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Version:", a.Version.Value())
}
func RefreshVersion(w http.ResponseWriter, r *http.Request) {
sysconf.Set(versionKey, "v2.0.1")
gs.RefreshProperties()
fmt.Fprintln(w, "Version updated!")
}
func main() {
gs.Object(&App{})
gs.Provide(func (app *App) *http.ServeMux {
http.Handle("/", app)
http.HandleFunc("/refresh", RefreshVersion)
return http.DefaultServeMux
})
gs.Run()
}
After running the program, accessing /
will output the current version. After accessing /refresh
, accessing /
again will show the updated version number.
Go-Spring provides a generic Server
interface for registering various service components (such as HTTP, gRPC,
WebSocket, etc.). All registered Servers
are automatically integrated into the application's lifecycle management, supporting concurrent startup, unified
shutdown, and other capabilities, helping developers build cleanly structured and consistently managed systems.
type Server interface {
ListenAndServe(sig ReadySignal) error
Shutdown(ctx context.Context) error
}
ListenAndServe(sig ReadySignal)
: Starts the service and provides services externally after receiving thesig
signal.Shutdown(ctx)
: Gracefully shuts down the service and releases resources.
type ReadySignal interface {
TriggerAndWait() <-chan struct{}
}
You can wait in ListenAndServe
for the main process to trigger the startup completion signal before officially
providing services externally.
func init() {
gs.Object(NewServer()).AsServer()
}
type MyServer struct {
svr *http.Server
}
func NewServer() *MyServer {
return &MyServer{
svr: &http.Server{Addr: ":8080"},
}
}
func (s *MyServer) ListenAndServe(sig gs.ReadySignal) error {
ln, err := net.Listen("tcp", s.svr.Addr)
if err != nil {
return err
}
<-sig.TriggerAndWait() // Wait for the startup signal
return s.svr.Serve(ln)
}
func (s *MyServer) Shutdown(ctx context.Context) error {
return s.svr.Shutdown(ctx)
}
type GRPCServer struct {
svr *grpc.Server
}
// ...
func (s *GRPCServer) ListenAndServe(sig gs.ReadySignal) error {
lis, err := net.Listen("tcp", ":9595")
if err != nil {
return err
}
<-sig.TriggerAndWait()
return s.svr.Serve(lis)
}
func (s *GRPCServer) Shutdown(ctx context.Context) error {
s.svr.GracefulStop()
return nil
}
All services registered through .AsServer()
will start concurrently when gs.Run()
is called and listen for exit
signals uniformly:
gs.Object(&HTTPServer{}).AsServer()
gs.Object(&GRPCServer{}).AsServer()
Go-Spring abstracts the application runtime cycle into three roles: Runner
, Job
, and Server
, with the following
meanings:
- Runner: One-time tasks executed immediately after application startup (e.g., initialization)
- Job: Background tasks that run continuously during application runtime (e.g., daemon threads, polling)
- Server: Long-term service processes that provide external services (e.g., HTTP/gRPC)
These roles can be registered through .AsRunner() / .AsJob() / .AsServer()
.
Example: Runner
type Bootstrap struct{}
func (b *Bootstrap) Run() error {
fmt.Println("Bootstrap init...")
return nil
}
func init() {
gs.Object(&Bootstrap{}).AsRunner()
}
- If a Runner returns an error during execution, the application startup process will be terminated.
Example: Job
type Job struct{}
func (j *Job) Run(ctx context.Context) error {
for {
select {
case <-ctx.Done():
fmt.Println("job exit")
return nil
default:
if gs.Exiting() {
return nil
}
time.Sleep(300 * time.Millisecond)
fmt.Println("job tick")
}
}
}
func init() {
gs.Object(&Job{}).AsJob()
}
- Jobs start after
gs.Run()
and continue until the exit signal arrives; - Supports graceful shutdown, promptly responding to
ctx.Done()
orgs.Exiting()
states.
Go-Spring provides a unit testing framework that seamlessly integrates with the standard go test
, making dependency
injection and mock testing simple and efficient.
Use gstest.MockFor[T]().With(obj)
to easily replace any bean at runtime:
gstest.MockFor[*book_dao.BookDao]().With(&book_dao.BookDao{
Store: map[string]book_dao.Book{
"978-0132350884": {
Title: "Clean Code",
Author: "Robert C. Martin",
ISBN: "978-0132350884",
Publisher: "Prentice Hall",
},
},
})
There are two ways to obtain the object under test:
Directly Get Instance:
o := gstest.Get[*BookDao](t)
assert.NotNil(t, o)
Structured Injection:
s := gstest.Wire(t, new(struct {
SvrAddr string `value:"${server.addr}"`
Service *BookService `autowire:""`
BookDao *book_dao.BookDao `autowire:""`
}))
assert.That(t, s.SvrAddr).Equal("0.0.0.0:9090")
Go-Spring differentiates itself with these key features:
Feature | Go-Spring | Wire | fx | dig |
---|---|---|---|---|
Runtime IoC Container | β | β | β | β |
Compile-time Validation | Partial | β | β | β |
Conditional Beans | β | β | β | β |
Dynamic Configuration | β | β | β | β |
Lifecycle Management | β | β | β | β |
Property Binding | β | β | β | β |
Zero-struct Modification | β | β | β | β |
- ...
Using Go-Spring and want to be featured here? Welcome to submit a PR!
We welcome contributions! Please see CONTRIBUTING.md to get started.
Thanks to JetBrains for providing the IntelliJ IDEA product, which offers a convenient and fast code editing and testing environment.
The Go-Spring is released under version 2.0 of the Apache License.