The project has been officially released, welcome to use!
Go-Spring is a high-performance framework for modern Go application development, inspired by Spring / Spring Boot from the Java ecosystem.
Its design philosophy deeply integrates native Go language features, inheriting mature development paradigms from the Spring ecosystem β Dependency Injection (DI), auto-configuration, and lifecycle management β while avoiding the complexity and performance overhead that traditional frameworks may incur.
Go-Spring allows developers to enjoy the convenience of high-level abstraction and automated development while maintaining Go's native style and execution efficiency.
Whether you're building monolithic applications or constructing distributed microservices systems, Go-Spring provides a unified and flexible development experience.
The framework simplifies project initialization in an "out-of-the-box" way, reduces boilerplate code, and does not enforce an intrusive architecture, allowing developers to focus on implementing business logic.
Go-Spring is committed to improving development efficiency, enhancing maintainability, and ensuring system consistency, making it a milestone framework in the Go ecosystem.
Go-Spring combines mature design ideas of dependency injection and auto-configuration, adheres to Go's philosophy of "simplicity is beauty", and provides rich practical features to help developers efficiently build modern Go applications:
-
β‘ Extreme startup performance, zero reflection at runtime
- Pre-registers beans based on Go's native
init()mechanism, no runtime scanning, startup takes only milliseconds; - Reflection is only used during initialization phase to complete dependency injection, after initialization zero reflection throughout runtime, performance comparable to handwritten code.
- Pre-registers beans based on Go's native
-
π§© Non-intrusive IoC container
- No forced interface dependencies or inheritance structure, business logic maintains native Go style, truly non-intrusive;
- Supports standalone dependency injection usage, can also be used for full-stack framework development, flexible and unbound, fully compatible with Go standard library;
- Provides complete bean lifecycle management,
natively supports
InitandDestroyhooks.
-
π Flexible and diverse Bean dependency injection
- Supports multiple injection methods: struct field injection, constructor injection, constructor parameter injection;
- Supports multiple matching strategies by type, name, and tags, covering various scenario requirements.
-
π·οΈ Convenient Value configuration binding
- Configuration values are directly bound to struct fields, no manual parsing required;
- Supports default value syntax
${key:=default}, elegant fallback; - Built-in field validation, automatically checks configuration correctness.
-
π― Powerful conditional injection system
- Supports dynamically deciding whether to register a bean based on configuration, environment, context and other conditions;
- Provides multiple commonly used condition types, supports logical combinations (AND/OR/NOT);
- Lays a solid foundation for modular auto-wiring.
-
βοΈ Layered configuration system
- Supports multi-source (command line, environment variables, configuration files, memory) and multi-format (YAML, TOML, Properties) configuration loading;
- Clear configuration priority layering, automatic overriding, natively supports multi-environment isolation;
- Supports configuration import, can integrate remote configuration centers to meet cloud-native deployment requirements.
-
π Hot configuration reload, real-time effective
- Original
gs.Dync[T]generic natively supports hot reloading, configuration changes don't require application restart; - Fully compatible with Value binding syntax, consistent usage, easy to get started;
- Configuration automatically synchronizes to fields, enabling gray release and online parameter tuning in one step.
- Original
-
ποΈ Modular auto-wiring
- Modular auto-wiring based on conditional injection;
- Modular design, assembled on demand, truly out-of-the-box;
- The ecosystem provides rich Starter modules for quick integration of various functions.
-
π Clear application runtime model
- Abstracts two runtime models:
Runner(one-time tasks) andServer(long-running services); - Built-in HTTP Server launcher, supports concurrent startup of multiple services;
- Complete lifecycle hooks, supports graceful startup/shutdown and signal handling.
- Abstracts two runtime models:
-
π§ͺ Native
go testintegration for testinggs.RunTest()starts the container with one click, automatically completes dependency injection;- Automatic graceful shutdown after tests, no extra scaffolding code required.
-
πͺ΅ Out-of-the-box logging system in the ecosystem
- Go-Spring ecosystem provides natively integrated structured logging module;
- Unified logging interface, supports multiple outputs, adaptable to various logging implementations.
Go-Spring uses Go Modules for dependency management, installation is straightforward:
go get github.com/go-spring/spring-coreGo-Spring prides itself on being "out-of-the-box". Below are two examples to quickly experience the framework's powerful capabilities:
- Example 1 shows how Go-Spring perfectly integrates with the standard library without changing your coding habits
- Example 2 shows the core framework features such as dependency injection, configuration binding, dynamic refresh, etc.
This example demonstrates Go-Spring's perfect compatibility
with the standard library net/http.
You can use standard library writing directly,
and the framework only handles lifecycle management:
package main
import (
"net/http"
"github.com/go-spring/spring-core/gs"
)
func main() {
// Define routes entirely using Go standard library http.Handler
// Go-Spring won't force you to replace it with framework-specific routing syntax
http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world!"))
})
// Start the application with just one line, the framework automatically takes over:
// - Signal handling (graceful exit on Ctrl+C)
// - Lifecycle management
// - Automatic waiting for all services to exit
gs.Run()
}Access the service:
curl http://127.0.0.1:9090/echo
# Output: hello world!This minimal example already reflects Go-Spring's design philosophy:
- β
Non-intrusive compatibility: Directly use Go standard library
http, no code rewriting needed - β
Zero configuration startup: No complicated configuration files, just
gs.Run()to run - β Lifecycle enhancement: The framework automatically handles signal capture and graceful exit, eliminating template code handwriting
- β Gradual integration: You can use only lifecycle management, or gradually introduce DI and configuration capabilities
This example demonstrates how multiple core features of Go-Spring work together, showcasing dependency injection, configuration binding, dynamic configuration hot reloading, custom configuration at startup and other capabilities:
package main
import (
"fmt"
"net/http"
"os"
"time"
"github.com/go-spring/spring-core/gs"
)
const timeLayout = "2006-01-02 15:04:05.999 -0700 MST"
// Register Bean during init() phase, based on Go native mechanism, no runtime scanning needed
func init() {
gs.Provide(&Service{})
// Parameter s *Service will be automatically injected by the framework
gs.Provide(func(s *Service) *gs.HttpServeMux {
http.HandleFunc("/echo", s.Echo)
http.HandleFunc("/refresh", s.Refresh)
return &gs.HttpServeMux{Handler: http.DefaultServeMux}
})
}
type Service struct {
// Auto-inject configuration refresher by type
AppConfig *gs.PropertiesRefresher `autowire:""`
// Bind configuration value to field through value tag
StartTime time.Time `value:"${start-time}"`
// Use gs.Dync[T] generic to support hot reloading, automatically syncs after configuration changes
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) {
// Simulate environment variable change, trigger configuration refresh.
// According to priority rules, environment variables have higher priority
// than in-memory configuration
os.Setenv("GS_REFRESH-TIME", time.Now().Format(timeLayout))
// Call refresh interface, all fields wrapped with Dync will be automatically updated
s.AppConfig.RefreshProperties()
w.Write([]byte("OK!"))
}
func main() {
// Set configuration through code during application startup phase,
// this belongs to the in-memory configuration level
gs.Configure(func(app gs.App) {
app.Property("start-time", time.Now().Format(timeLayout))
app.Property("refresh-time", time.Now().Format(timeLayout))
}).Run()
}Access the service:
curl http://127.0.0.1:9090/echo # Check current time (start time and refresh time)
curl http://127.0.0.1:9090/refresh # Trigger hot refresh, refresh time will updateThis example covers many core features of Go-Spring:
- β
Bean registration and dependency injection: Register beans via
gs.Provide(), framework automatically completes dependency injection - β Custom configuration at startup: Supports dynamic configuration through code during application startup, flexibly adapts to different scenarios
- β
Automatic configuration binding:
valuetag directly binds configuration to struct fields, no manual parsing required - β Layered configuration system: Follows priority rules: environment variables > in-memory configuration > default values, naturally supports multiple environments
- β
Dynamic configuration hot reloading: Natively supported via
gs.Dync[T]generic, configuration changes take effect in real-time without application restart - β
Configuration refresh mechanism: Provides
PropertiesRefresherto support manual triggering of configuration reload, can be used with configuration centers
In Go-Spring, Beans are the core building blocks of your application, similar to the concept of "components" in other dependency injection frameworks. The entire system is organized around Bean registration, initialization, dependency injection, and lifecycle management.
Go-Spring's design philosophy is "Ready at compile time, minimal at runtime":
- No reliance on runtime scanning, all beans complete registration metadata collection
during the
init()phase - Reflection is only used during initialization phase to complete dependency injection, after initialization zero reflection throughout runtime
- Type safety is guaranteed by the Go compiler, runtime performance comparable to handwritten code
This design fundamentally avoids the performance overhead and debugging complexity caused by runtime reflection in traditional IoC frameworks, making it particularly suitable for building high-performance, maintainable large-scale systems.
The framework adopts a combination approach of "explicit registration + tag declaration + conditional assembly":
- Explicit registration: All beans must be explicitly registered, no implicit scanning, dependencies are clear at a glance
- Tag declaration: Concise declaration of injection rules through tags, no redundant configuration
- Conditional assembly: Supports dynamic registration decisions based on environment, naturally adapts to modular design
Since it doesn't rely on runtime container scanning and there's no "magic configuration", this approach improves debugging and operational controllability while maintaining a good development experience, truly achieving the goal of zero intrusion, (runtime) zero reflection.
Go-Spring provides two ways to register beans:
gs.Provide(objOrCtor, args...)- Register global Beans ininit()functionsapp.Provide(objOrCtor, args...)- Register Beans during the application configuration phase
Example:
// Register global Bean in init()
func init() {
gs.Provide(&Service{}) // Register struct instance
gs.Provide(NewService) // Register using constructor
gs.Provide(NewRepo, gs.ValueArg("db")) // Constructor with parameters
}
// Register Bean in application configuration
gs.Configure(func(app gs.App) {
app.Provide(&MyService{})
app.Root(&Bootstrap{}) // Mark as root Bean, triggers dependency injection
})π‘ On-demand instantiation and
RootGo-Spring defaults to an on-demand instantiation strategy β only beans that are depended on or marked asRootwill be instantiated. Beans marked viaapp.Root()serve as entry points to the application, and the framework automatically completes their dependency injection and instantiation.
Go-Spring provides multiple flexible dependency injection methods.
Inject configuration items or beans into struct fields through tags, suitable for most scenarios and is the most commonly used injection method.
type App struct {
Logger *Logger `autowire:""` // Auto-inject Bean by type
Filters []*Filter `autowire:"access,*?" // Inject multiple Beans, allowed to not exist
StartTime time.Time `value:"${start-time}" // Bind configuration value
}Syntax explanation:
value:"${key}"orvalue:"${key:=default}": Binds configuration value to field, supports default valuesautowire:"": Automatic injection by type, matches directly when type is uniqueautowire:"?": Inject by type, allows non-existence, field is nil if not existsautowire:"name?": Match by type and name, allows non-existence, field is nil if not existsautowire:"a,*?": First match name a, then inject remaining beans of the same type, injection order matches registration orderautowire:"a,b,c": Exact match multiple beans by specified names, order strictly matches declaration orderautowire:"a,*?,b": Exact match multiple specified beans by name, keep remaining other beans, overall ordered
Automatic injection through function parameters, Go-Spring automatically infers and matches dependent beans.
func NewService(logger *Logger) *Service {
return &Service{Logger: logger}
}
gs.Provide(NewService)You can explicitly specify the injection behavior for each parameter through parameter wrappers, more suitable for complex construction logic.
gs.Provide(NewService,
gs.TagArg("${log.level}"), // Inject value from configuration
gs.ValueArg(8080), // Inject fixed value directly
gs.BindArg(connectDB), // Inject after processing through function
)Available parameter types:
| Parameter Wrapper | Purpose | Usage Scenario |
|---|---|---|
gs.TagArg("${key}") |
Extract value from configuration and inject | Need configuration value directly as constructor parameter |
gs.ValueArg(val) |
Inject fixed value | Know the parameter value definitely, no need to get from container |
gs.IndexArg(i, arg) |
Specify injection by parameter position | Need to skip certain parameters, or customize injection for specific parameters |
gs.BindArg(fn, args...) |
Inject after processing via function | Need conversion or custom processing for injected values |
While this approach may seem slightly verbose, it gives you complete control over the injection process, which is very useful in complex scenarios.
Go-Spring provides rich APIs for configuring bean metadata, lifecycle hooks, and dependencies. Through method chaining, you can fully define all behaviors of a bean.
gs.Provide(NewService).
Name("myService"). // Specify Bean name
Init(func(s *Service) { ... }). // Initialization function
Destroy(func(s *Service) { ... }). // Destruction function
Condition(gs.OnProperty("feature.enabled")). // Conditional registration
DependsOn(gs.BeanIDFor[*Repo]()). // Declare explicit dependency
Export(gs.As[ServiceInterface]()). // Export as interface
Export(gs.As[gs.Runner]()) // Supports multiple interface exportsComplete configuration option description:
| Option | Purpose | Description |
|---|---|---|
Name(string) |
Specify Bean name | Used to distinguish when multiple beans of the same type exist, used with autowire:"name" |
Init(fn) |
Initialization function | Called after bean dependency injection is complete, also supports InitMethod("Init") specification by method name |
Destroy(fn) |
Destruction function | Called when application shuts down, also supports DestroyMethod("Close") specification by method name |
DependsOn(...) |
Declare dependency | Specify other beans that this bean depends on, ensuring correct initialization order |
Condition(...) |
Conditional registration | Only registers the current bean when condition is met, skips registration otherwise |
Export(as) |
Interface export | Register the bean as a specific interface to the container, convenient for injection by interface, supports multiple calls to export multiple interfaces |
Go-Spring supports capabilities similar to Spring Boot's @Configuration β
you can mark a bean as a configuration class,
and the framework will automatically scan the configuration class's methods
and automatically register method return values as sub-beans.
This approach is very suitable for organizing configuration modularly.
// Define configuration class
type DataSourceConfig struct {}
// Method return values are automatically registered as Beans
func (c *DataSourceConfig) PrimaryDB() *sql.DB {
// Write your database connection creation logic here
return &sql.DB{ /* ... */ }
}
// Multiple methods can define multiple related Beans
func (c *DataSourceConfig) ReplicaDB() *sql.DB {
return &sql.DB{ /* ... */ }
}
func init() {
// Mark as configuration class via .Configuration(),
// framework automatically scans and registers all sub-beans
gs.Provide(&DataSourceConfig{}).Configuration()
}You can precisely control which methods need to be scanned and registered using regular expressions:
func init() {
// Only include methods matching New.* pattern
gs.Provide(&Config{}).Configuration(gs.Configuration{
Includes: []string{"New.*"}, // Include patterns
Excludes: []string{"Test.*"}, // Exclude patterns
})
}- If you don't specify
Includes, it defaults to scanning all public methods - Regex syntax follows Go standard
regexppackage specification, please avoid incomplete regular expressions like* - Scanned methods must return Bean instances, supports two signatures:
(T)or(T, error)
Go-Spring provides a layered-designed, flexible and powerful configuration management system that supports loading configuration from multiple sources, natively meeting enterprise requirements such as multi-environment isolation and dynamic updates. Whether for local development, containerized deployment, or cloud-native architecture, Go-Spring provides a consistent and concise configuration experience.
The framework automatically merges configuration items from different sources at startup and automatically overrides according to priority rules, so you don't need to manually handle configuration merging logic.
Go-Spring supports three mainstream configuration formats out of the box:
YAML (.yaml/.yml, recommended), Properties (.properties), and TOML (.toml).
The framework automatically recognizes the format based on the file extension.
The most convenient way to configure in Go-Spring
is to bind configuration directly to struct fields via the value tag,
no manual parsing required:
type ServerConfig struct {
Port int `value:"${server.port:=8080}"` // With default value
Host string `value:"${server.host:=localhost}"` // With default value
Enabled bool `value:"${server.enabled:=true}"` // Boolean type
}Syntax explanation:
${key}: Binds the value of configuration keykeyto the field${key:=default}: Usesdefaultas the default value if the configuration key doesn't exist- Supports almost all Go primitive types:
int/int64/uint/float64/bool/string, etc., also supports custom types liketime.Duration
Go-Spring adopts a clear priority layering design, where higher priority configurations automatically override lower priority configurations with the same name. Priorities are listed from highest to lowest below:
| Priority | Configuration Source | Description | Usage Scenario |
|---|---|---|---|
| 1 β¬οΈ | Command Line Arguments | -Dkey=value |
Temporary override configuration, quick debugging verification |
| 2 | Environment Variables | System environment variables | Containerized deployment, twelve-factor apps |
| 3 | profile configuration | app-{profile}.ext |
Multi-environment isolation (dev/test/prod) |
| 4 | app base configuration | app.ext |
Default base configuration |
| 5 | In-memory configuration | Programmatic setting via app.Property() |
Unit testing, dynamic override |
| 6 β¬οΈ | Tag default values | ${key:=default} |
Final fallback, default values |
π‘ Core Priority Rule Later-loaded configurations have higher priority, configurations closer to runtime take precedence, which matches intuition.
π‘ Configuration Import Rule Both base configuration and profile configuration support importing external configurations via
spring.app.imports, Later imported configurations have higher priority than the file's original configuration, overriding in import order.
Injected using -Dkey=value format, highest priority,
suitable for quickly overriding runtime configuration:
go run main.go -Dserver.port=9090 -Dapp.env=productionDirectly read from OS environment variables, best practice for containerized deployment:
export SERVER_PORT=9090
export APP_ENV=production
export SPRING_PROFILES_ACTIVE=devπ‘ Go-Spring automatically converts underscores in environment variables to dots, for example
SERVER_PORTmaps toserver.port.
Achieves environment isolation by activating different profiles,
file naming format is app-{profile}.{ext}:
# Activate dev environment
export SPRING_PROFILES_ACTIVE=devThe framework automatically loads app-dev.yaml (or other formats),
which has higher priority than base configuration.
Configurations imported in profile configuration also follow the later-import-first rule.
By default loads conf/app with extension (e.g., conf/app.yaml),
suitable for storing general base configuration:
./conf/app.yaml
./conf/app.properties
Base configuration supports importing external configurations via spring.app.imports.
Both base configuration and profile configuration support importing external configurations
via spring.app.imports, making it easy to split and reuse:
# app.yaml
spring:
app:
imports:
- "database.yaml" # Split database configuration
- "redis.yaml" # Split Redis configuration
- "nacos://server.json" # Import from remote configuration center (requires extension)Importing executes in the order declared, later imported configurations override keys with the same name from earlier imports and original configuration. This mechanism is very suitable for configuration splitting and integrating remote configuration centers (Nacos, etcd, etc.).
Programmatic configuration setting via code during application startup, commonly used for testing or dynamic scenarios:
gs.Configure(func(app gs.App) {
app.Property("app.name", "test-app")
app.Property("feature.enabled", true)
})Embedded default values through tags, serve as the final fallback for the configuration system:
type Config struct {
Port int `value:"${server.port:=8080}"`
Env string `value:"${app.env:=development}"`
}Drawing inspiration from Spring's @Conditional concept,
Go-Spring implements a flexible and powerful conditional injection system.
It dynamically decides whether to register beans based on configuration,
environment, context and other conditions, achieving "assembly on demand".
This is particularly crucial in scenarios such as multi-environment deployment,
plugin architecture, feature toggles, and gray release.
gs.OnProperty("key"): Activates when the specified configuration key existsgs.OnBean[Type]("name"): Activates when a bean of the specified type/name existsgs.OnMissingBean[Type]("name"): Activates when a bean of the specified type/name does not existgs.OnSingleBean[Type]("name"): Activates when the specified type/name bean is the only instancegs.OnFunc(func(ctx gs.ConditionContext) (bool, error)): Uses custom conditional logic to determine activation
Example:
gs.Provide(NewService).
Condition(gs.OnProperty("service.enabled"))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:
gs.Not(...)- Negates a conditiongs.And(...)- All conditions must be satisfiedgs.Or(...)- Any condition being satisfied is sufficientgs.None(...)- All conditions must not be satisfied
Example:
gs.Provide(NewService).
Condition(
gs.And(
gs.OnProperty("feature.enabled"),
gs.Not(gs.OnBean[*DeprecatedService]()),
),
)This bean will be enabled when feature.enabled is turned on
and *DeprecatedService is not registered.
Drawing inspiration from Spring Boot's Starter concept, Go-Spring provides a Module mechanism to implement auto-configuration and modular assembly. Through Module, you can organize related beans together to create "out-of-the-box" functional modules.
A Module is Go-Spring's conditional configuration module mechanism that can dynamically decide whether to register a set of related beans based on configuration properties. This is ideal for:
- π§© Developing starters for various functions (such as Redis, MySQL, gRPC, etc.)
- ποΈ Organizing code by functional modules, achieving loosely coupled architecture
- β‘ Automatically enabling/disabling features based on configuration, truly assembled on demand
The core interface is very concise:
gs.Module(condition gs.PropertyCondition, fn func(r gs.BeanProvider, p flatten.Storage) error)condition: Property condition, only when the condition is met will the beans inside the module be registered (usually created withgs.OnProperty("key"))fn: Module initialization function, register all beans of this module in batch within the functionr: Bean registrar, usage is exactly the same as globalgs.Provide()p: Configuration storage, you can read configuration from it for dynamic binding
Suppose you want to develop a Redis Starter, you can organize your code like this:
package redis
import (
"github.com/go-spring/spring-core/gs"
"github.com/go-spring/stdlib/flatten"
)
// Cache interface
type Cache interface {
Get(key string) (string, error)
Set(key string, value string) error
}
func init() {
// Automatically enable the Redis module when redis.host configuration is detected
// If the condition is not met, beans inside the module won't be registered,
// doesn't affect application startup
gs.Module(gs.OnProperty("redis.host"),
func(r gs.BeanProvider, p flatten.Storage) error {
// 1. Register Redis Client, specify name, initialization and destruction methods
r.Provide(NewRedisClient).
Name("redisClient").
InitMethod("Connect"). // Call Connect after dependency injection completes
DestroyMethod("Close"). // Call Close when application shuts down to release resources
// 2. Register Redis-based Cache implementation and export as Cache interface
// This allows other components to inject by interface, decoupled from specific implementation
r.Provide(NewRedisCache).
Export(gs.As[Cache]())
// You can continue to register other related beans...
return nil
})
}It's very simple for users to use, just add to the configuration file:
redis:
host: localhost
port: 6379
password: xxx
db: 0Go-Spring will automatically detect the configuration, and when the condition is met, automatically execute the module to register all related beans. Users don't need to write any manual registration code, truly out-of-the-box!
Go-Spring also provides gs.Group convenient syntax for handling a common scenario:
batch creating multiple beans from a map in configuration.
Each map entry is automatically converted to a named bean,
with the map key as the bean name. Usage example:
// Batch create multiple HTTP clients from configuration
gs.Group(
"${http.clients}", // Path to map-type configuration in configuration
func(cfg HTTPClientConfig) (*HTTPClient, error) {
return NewHTTPClient(cfg) // Create a client instance for each configuration entry
},
func(c *HTTPClient) error {
return c.Close() // Optional: destruction function for resource cleanup
},
)Corresponding YAML configuration:
http:
clients:
serviceA: # map key "serviceA" becomes the bean name
baseURL: "http://a.example.com"
timeout: 30s
serviceB: # map key "serviceB" becomes the bean name
baseURL: "http://b.example.com"
timeout: 60sThis approach is very suitable for scenarios that require batch creation of beans based on configuration, such as multiple data sources, multi-tenancy, dynamic plugins, etc.
Go-Spring natively supports a lightweight configuration hot update mechanism.
Through the generic type gs.Dync[T] and RefreshProperties(),
applications can perceive configuration changes in real-time at runtime
without restarting the application.
This feature is very useful in scenarios such as gray release,
dynamic parameter tuning, and configuration center integration
in microservices architecture.
Divided into two steps: declare dynamic field and trigger refresh.
Wrap fields with the generic type gs.Dync[T],
and the framework will automatically listen for configuration changes
and update in real-time:
type Config struct {
Version gs.Dync[string] `value:"${app.version}"` // Declare as dynamic configuration
}To use, get the latest current value through the .Value() method:
version := config.Version.Value() // Always gets the latest valueThe framework will automatically update the internal value when the configuration changes, no manual handling required from you.
After external configuration changes, you need to inject *gs.PropertiesRefresher
and call its method to trigger the refresh:
func RefreshHandler(w http.ResponseWriter, r *http.Request, refresher *gs.PropertiesRefresher) {
// Simulate configuration change (in real scenarios it's usually pushed by configuration center)
os.Setenv("APP_VERSION", "v2.0.1")
// Trigger refresh, all gs.Dync[T] fields will update automatically
_ = refresher.RefreshProperties()
fmt.Fprintln(w, "Version updated!")
}Go-Spring abstracts components in the application runtime phase
into two core roles: Runner and Server, with clear division of responsibilities:
| Role | Execution Method | Typical Scenarios |
|---|---|---|
| Runner | One-time execution | Database initialization, cache warming, data migration and other startup tasks |
| Server | Long-running | HTTP services, gRPC services, WebSocket services, etc. |
All roles are registered via .Export(gs.As[Interface]()).
Design Note: Early versions included a
Jobtype for background scheduled tasks, but to simplify the model and reduce cognitive burden, it has been removed in the latest version. For background tasks that need to run continuously, it's recommended to implement directly using theServerinterface and handle with a loop in theRunmethod.
package main
import (
"context"
"fmt"
"github.com/go-spring/spring-core/gs"
)
func init() {
// Register Bootstrap and export as Runner interface
gs.Provide(&Bootstrap{}).Export(gs.As[gs.Runner]())
}
type Bootstrap struct{}
func (b *Bootstrap) Run(ctx context.Context) error {
fmt.Println("Bootstrap: Initialization completed...")
return nil // If returns error, application startup will be terminated
}
func main() {
gs.Run()
}Go-Spring provides a generic Server interface that allows you
to conveniently integrate various service components.
All registered Servers automatically integrate into the application lifecycle,
and the framework handles general logic such as concurrent startup,
graceful shutdown, and signal handling.
Server interface definition:
type Server interface {
Run(ctx context.Context, sig ReadySignal) error
Stop() error
}Run(ctx context.Context, sig ReadySignal): Start the service, wait for the startup signal before officially providing services externallyStop() error: Gracefully shuts down the service, releases resources
ReadySignal interface:
type ReadySignal interface {
TriggerAndWait() <-chan struct{}
}The role of ReadySignal is to wait for all Servers to complete listening binding
before uniformly providing services externally, avoiding errors caused
by accepting requests before startup completes.
package main
import (
"context"
"net"
"net/http"
"github.com/go-spring/spring-core/gs"
)
func init() {
gs.Provide(NewServer).Export(gs.As[gs.Server]())
}
type MyServer struct {
svr *http.Server
}
// NewServer creates HTTP service instance
func NewServer() *MyServer {
return &MyServer{
svr: &http.Server{Addr: ":8080"},
}
}
func (s *MyServer) Run(ctx context.Context, sig gs.ReadySignal) error {
// Complete port listening binding first
ln, err := net.Listen("tcp", s.svr.Addr)
if err != nil {
return err // Binding fails, return directly, terminate startup
}
// Wait for all servers to complete startup, then start accepting connections
<-sig.TriggerAndWait()
// Officially start serving
return s.svr.Serve(ln)
}
func (s *MyServer) Stop() error {
// Gracefully shutdown HTTP service
return s.svr.Shutdown(context.Background())
}package main
import (
"context"
"net"
"github.com/go-spring/spring-core/gs"
"google.golang.org/grpc"
)
type GRPCServer struct {
svr *grpc.Server
}
func (s *GRPCServer) Run(ctx context.Context, sig gs.ReadySignal) error {
lis, err := net.Listen("tcp", ":9595")
if err != nil {
return err
}
<-sig.TriggerAndWait() // Wait for all services to complete startup
return s.svr.Serve(lis)
}
func (s *GRPCServer) Stop() error {
s.svr.GracefulStop() // Graceful stop
return nil
}All services registered via .Export(gs.As[gs.Server]()) will be started concurrently
when gs.Run() is called, and exit signals will be handled uniformly:
func init() {
// HTTP and gRPC services run concurrently
gs.Provide(&HTTPServer{}).Export(gs.As[gs.Server]())
gs.Provide(&GRPCServer{}).Export(gs.As[gs.Server]())
}After receiving an exit signal (such as Ctrl+C), the framework uniformly calls
the Stop() method of all servers to achieve graceful shutdown.
Thanks to Go-Spring's non-intrusive design, you can completely write unit tests in native Go way, and there's no mandatory requirement to use special testing capabilities from the framework.
For simple unit tests, just manually instantiate the test object and pass in dependencies manually:
func TestMyService(t *testing.T) {
// Manually create dependencies (can use Mock)
mockRepo := NewMockRepo()
// Manually instantiate the service under test
service := NewMyService(mockRepo)
// Test directly, no need to start container
result := service.DoSomething()
assert.Equal(t, "ok", result)
}When you need to write integration tests that require starting the complete container
and automatically completing dependency injection, Go-Spring provides gs.RunTest()
with native go test integration, which is very convenient:
package main
import (
"testing"
"github.com/go-spring/spring-core/gs"
"github.com/stretchr/testify/assert"
)
func TestExample(t *testing.T) {
// gs.RunTest automatically creates container, completes dependency injection,
// automatically closes after test
gs.RunTest(t, func(ts *struct {
DB *MyDB `autowire:""`
Cache *Cache `autowire:""`
}) {
// All dependencies are already automatically injected, ready to use directly
result := ts.DB.Query("SELECT ...")
assert.NotNil(t, result)
})
}- β
Fully native compatible: Seamlessly integrated with standard
go test, no special test runner required - β Automatic dependency injection: Declare required beans in the test parameter struct, framework injects automatically
- β Automatic resource cleanup: Automatically calls destruction methods after tests, graceful shutdown
Below is a feature comparison between Go-Spring and other mainstream Go dependency injection frameworks:
| Feature Point | Go-Spring | Wire | fx | dig |
|---|---|---|---|---|
| Runtime IoC Container | β | β | β | β |
| No runtime scanning (pre-registration based on init()) | β | β | β | β |
| Zero reflection runtime (no reflection after initialization) | β | β | β | β |
| Compile-time type checking | Partial | β | β | β |
| Conditional Beans support | β | β | β | β |
| Modular auto-wiring (Starter mechanism) | β | β | β | β |
| Dynamic configuration hot reloading | β | β | β | β |
| Lifecycle management | β | β | β | β |
| Configuration property auto-binding | β | β | β | β |
| Non-intrusive design (no modification to original struct) | β | β | β | β |
Go-Spring does not intend to replace any existing Go framework, but acts as a "glue" to help you integrate the entire Go ecosystem.
Go-Spring deeply respects Go's native ecosystem, the framework is fully compatible with the standard library and various third-party frameworks:
- β Can be used with any web framework like Gin/Echo/Chi, the framework doesn't force you to replace your routing syntax
- β Can be used with gRPC/protobuf ecosystem, auto-wire services
- β Can be used with sql/database or ORM frameworks, configuration-driven multiple data sources
- β
Fully compatible with Go standard library
net/http,context, etc., no intrusive modifications
| Component | What Go-Spring does | You can choose |
|---|---|---|
| Dependency Injection | β Full responsibility | - |
| Configuration Management | β Full responsibility (multi-source, hot reloading) | - |
| Web Routing | Optional integration | Gin, Echo, Chi, standard library net/http |
| ORM/Database | Optional integration | GORM, XORM, sqlx, standard library database/sql |
| Logging | Provides unified interface | Zap, Logrus, slog, etc. |
| Service Discovery/Registration | Integratable via Starter | etcd, Consul, Nacos |
In one sentence: Go-Spring helps you manage dependencies and configuration well, leaving the rest to the tools you're familiar with.
Want to get started quickly? Check out these resources:
- π Complete Documentation: go-spring/go-spring
- π‘ Example Projects: go-spring/examples
- π¦ Ecosystem Starters: The Go-Spring organization maintains many out-of-the-box modules
Many companies are using Go-Spring to build microservices applications in production:
- ...
If your company or project is also using Go-Spring, feel free to submit a PR to showcase your project here!
- π Bug Reports: GitHub Issues
- π‘ Feature Suggestions: Welcome to submit an Issue to join the discussion
- β Star Support: If you like this project, feel free to give a star to encourage us!
Go-Spring is an open source community-driven project, we welcome all forms of contributions:
- Fix documentation errors
- Fix bugs
- Submit feature suggestions
- Contribute new features
- Share your usage experience
Please check CONTRIBUTING.md for how to participate.
Welcome to join the QQ group for discussion:
Follow the WeChat official account for the latest updates:
Thanks to JetBrains for the IntelliJ IDEA open source license, which greatly facilitates project development.
Apache License 2.0, see LICENSE for details.
.jpeg)
