news 2026/7/6 14:33:15

10 个示例快速入门 Go-Spring|v1.3.0 正式发布

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
10 个示例快速入门 Go-Spring|v1.3.0 正式发布

如今,只需简简单单的一行gs.Run(),便可以优雅地启动整个 Go-Spring 应用,实在令人畅快!

从此,我们只需要依照既定规范,对配置、日志与启动进行扩展,便能够从容应对各种日常开发场景,
既能保持代码简洁高效,也为系统留下足够的可扩展空间。


接下来,本文将通过 10 个示例,带你一步一步、由浅入深地快速入门 Go-Spring。
每个示例都能独立运行,完整代码在 https://github.com/lvan100/go-spring-first 这里。

1. 启动一个最小 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-spring@v1.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||msg=ready to serve requests

ready to serve requests表示应用已经启动,并且成功监听:9090
使用下面的命令访问根路径:

curl http://127.0.0.1:9090/

会得到:

404 page not found

这里 404 是预期结果。它说明 HTTP Server 已经启动了,只是还没有 handler 能处理这个路径。
按下Ctrl+C可以停止程序,接着 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!

这里的HelloGo-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.greeting=Hello from ./conf/app.properties demo.audience=config 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.audience

GS_DEMO_AUDIENCE="env var" go run . curl http://127.0.0.1:9090/hello

执行上面的命令,会看到响应从配置文件里的值变成了环境变量里的值:

Hello from ./conf/app.properties, env var!

咱们还可以使用命令行参数覆盖配置,写法是-Dkey=value

go run . -Ddemo.audience="cmd 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,而是写greetingaudience
因为注册构造函数时会指定整体前缀${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的参数声明了它需要*GreetingService
Go-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 { cfg RedisConfig } func NewRedisClient(cfg RedisConfig) (*RedisClient, error) { log.Printf("create redis client addr=%s", cfg.Addr) return &RedisClient{cfg: cfg}, nil } func CloseRedis(*RedisClient) error { return nil } func (c *RedisClient) Ping(context.Context) error { log.Printf("redis ping addr=%s", c.cfg.Addr) return nil }

然后让 service 依赖*RedisClient,并在处理请求时调用它:

type GreetingService struct { redis *RedisClient Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"` } func NewGreetingService(redis *RedisClient) *GreetingService { return &GreetingService{redis: redis} } func (s *GreetingService) Message(ctx context.Context, audience string) string { _ = s.redis.Ping(ctx) return fmt.Sprintf("%s, %s!", s.Greeting, audience) }

最后咱们需要增加 Redis client 的注册代码:

func init() { gs.Provide(NewRedisClient, gs.TagArg("${spring.go-redis}")).Destroy(CloseRedis) gs.Provide(NewGreetingService) gs.Provide(NewController) gs.Provide(NewHTTPMux) }

注册 Redis client 时:

  • gs.TagArg("${spring.go-redis}")表示构造函数参数RedisConfigspring.go-redis前缀读取配置;
  • Destroy(CloseRedis)表示容器关闭时调用销毁函数。

完整代码在 examples/07-redis-single-client/main.go。

咱们还需要在配置文件中增加一个配置项,用于指定 Redis 地址:

spring.go-redis.addr=127.0.0.1:6379

使用下面的命令运行示例:

cd examples/07-redis-single-client go run .

会看到控制台上打印了创建客户端的日志:

create redis client addr=127.0.0.1:6379

访问/hello路由:

curl http://127.0.0.1:9090/hello

可以看到预期中的响应:

Hello with Redis, single client!

另外,咱们还能在控制台上看到请求过程中打印出的redis ping日志。
虽然这一章的 Redis Client 只是模拟对象,但它的注册方式和真实客户端没有区别:
配置绑定、依赖注入、资源销毁都交给容器。

8. 条件注册和多实例客户端

上一章咱们注册的只有一个 Redis client,所以 service 直接依赖*RedisClient就够了。
但真实应用里更常见的是同一种客户端有多个实例,例如默认 Redis、cache Redis、queue Redis。
如果咱们继续手写多个NewRedisClient,那么注册很快就会变乱,所以这一章咱们引入条件注册、命名 bean 和配置分组。

首先在默认客户端注册的时候增加两个声明:

gs.Provide(NewRedisClient, gs.TagArg("${spring.go-redis}")). Condition(gs.OnProperty("spring.go-redis.addr")). Destroy(CloseRedis). Name("__default__")
  • Condition(gs.OnProperty("spring.go-redis.addr"))表示只有配置里存在spring.go-redis.addr时才创建默认客户端。
  • Name("__default__")表示给这个 bean 一个名字,后续同类型实例变多时,注入方就可以明确选择它。

然后注册其他 Redis 实例,不过咱们不需要一条条手写注册,而是交给gs.Group
它可以根据配置批量创建多个同类型的实例:

gs.Group("${spring.go-redis.instances}", NewRedisClient, CloseRedis)

咱们需要在配置文件中新增一个spring.go-redis.instances配置项,它是一个 map,
键是实例名称,值是实例的配置。

spring.go-redis.addr=127.0.0.1:6379 spring.go-redis.instances.cache.addr=127.0.0.1:6380 spring.go-redis.instances.queue.addr=127.0.0.1:6381

现在需要对 service 做一些调整,因为现在同类型*RedisClient有多个注册实例。
咱们可以通过它字段上的autowire指定注入名为__default__的实例:

type GreetingService struct { Client *RedisClient `autowire:"__default__?"` Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"` }

完整代码在 examples/08-conditional-multi-redis/main.go。

使用下面的命令运行示例:

cd examples/08-conditional-multi-redis go run .

然后会看到启动时控制台上打印了__default__创建的日志,
但是并没有cachequeue创建的日志。
这是因为 Go-Spring 是按需实例化的,用不到的实例不会被创建。

访问/hello路由:

curl http://127.0.0.1:9090/hello

可以看到预期中的响应:

Hello with conditional Redis, conditional clients!

咱们可以修改 service,让它注入cache实例:

type GreetingService struct { Client *RedisClient `autowire:"cache?"` Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"` }

然后会看到启动时只有cache实例被创建。
同样的方式,咱们也可以注入queue实例。

这一章咱们解决的是“同类型多个实例如何管理”的问题。
Condition可以控制 bean 是否创建,
Name可以给 bean 命名,
autowire可以让依赖方选择具体实例,
Group可以把一组配置批量转换成一组客户端。

9. 接入结构化日志

到目前为止,示例已经展示了 HTTP、配置、依赖注入和客户端注册,但日志还只是普通文本。
真实服务需要更容易检索和关联的日志:业务日志要能标识来源,请求日志要能记录方法、路径和耗时,
同一次请求中的日志最好带上同一个 request id,等等。
所以这一章咱们引入 Go-Spring 的日志系统。

首先注册两个日志标签,一个用于业务日志,一个用于 HTTP 访问日志:

var ( tagBizGreeting = log.RegisterBizTag("greeting", "serve") tagHTTPRequest = log.RegisterRPCTag("http", "request") )

service 中不再使用标准库打印日志,而是使用 Go-Spring 的日志系统记录结构化字段:

func (s *GreetingService) Summary(ctx context.Context) string { log.Info(ctx, tagBizGreeting, log.String("greeting", s.Greeting), log.Msg("building greeting"), ) return s.Greeting + ", structured logs!" }

为 HTTP 入口新增一个中间件requestID,它可以从请求头读取或生成 request id。
对于 request id 这类信息,咱们希望它们能被自动记录到日志中,而不是每次打印日志时手动添加。
所以,咱们把 request id 放进 context 中,方便日志系统自动提取。

type requestIDKey struct{} func NewHTTPMux(c *Controller) *gs.HttpServeMux { mux := http.NewServeMux() mux.HandleFunc("/hello", c.Hello) return &gs.HttpServeMux{Handler: requestID(logging(mux))} } func requestID(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { id := r.Header.Get("X-Request-ID") if id == "" { id = fmt.Sprintf("%d", time.Now().UnixNano()) } w.Header().Set("X-Request-ID", id) ctx := context.WithValue(r.Context(), requestIDKey{}, id) next.ServeHTTP(w, r.WithContext(ctx)) }) }

咱们需要给日志系统设置一个上下文提取回调log.FieldsFromContext
这样它就可以从 context 中自动提取 request id,然后和其他字段一起被记录下来。

log.FieldsFromContext = func(ctx context.Context) []log.Field { id, ok := ctx.Value(requestIDKey{}).(string) if !ok || id == "" { return nil } return []log.Field{log.String("request_id", id)} }

最后咱们在配置文件中添加日志系统的配置,将日志以 JSON 格式输出到控制台。

logging.logger.root.type=ConsoleLogger logging.logger.root.level=INFO logging.logger.root.layout.type=JSONLayout logging.logger.root.layout.fileLineMaxLength=30

使用下面的命令运行示例:

cd examples/09-logging go run .

带上请求 ID 访问/hello路由:

curl -H "X-Request-ID: demo-1" http://127.0.0.1:9090/hello

可以看到预期中的响应:

Hello with logging, structured logs!

同时可以看到控制台会输出 JSON 日志,包含 tag、request_id、HTTP 方法、路径、耗时和业务字段。

{"level":"info","time":"2026-05-03T08:57:21.525","fileLine":"...mples/09-logging/main.go:29","tag":"_biz_greeting_serve","request_id":"demo-1","greeting":"Hello with logging","msg":"building greeting"} {"level":"info","time":"2026-05-03T08:57:21.526","fileLine":"...mples/09-logging/main.go:80","tag":"_rpc_http_request","request_id":"demo-1","method":"GET","path":"/hello","elapsed":"783.125µs","msg":"http request completed"}

这一章的重点不是“打印更多内容”,而是让日志变成结构化事件:
标签说明事件类型,字段承载可检索数据,context 把一次请求中的公共字段串起来。

10. 让组件可以脱离真实服务测试

经过前面的步骤,应用已经具备了一个 Web 服务常见的核心结构:
HTTP 入口、controller/service 分层、配置绑定、外部客户端和结构化日志。
还剩最后一个问题:测试

如果 service 直接依赖具体 Redis client,测试时就会很难替换;
如果测试必须启动真实的 HTTP Server,也会让反馈变慢。
这一章咱们把依赖改成接口,并使用 Go-Spring 的测试容器验证装配关系。

第一处变化是定义接口,让 service 依赖行为而不是具体实现:

type RedisPinger interface { Ping(context.Context) error } type GreetingService struct { redis RedisPinger Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"` } func NewGreetingService(redis RedisPinger) *GreetingService { return &GreetingService{redis: redis} }

生产环境咱们仍然使用RedisClient,不过这次注册时还需要把它导出为RedisPinger

gs.Provide(NewRedisClient, gs.TagArg("${spring.go-redis}")). Condition(gs.OnProperty("spring.go-redis.addr")). Destroy(CloseRedis). Export(gs.As[RedisPinger]())

这样一来,咱们就可以在测试代码里用一个很小的 fakeRedis 来替代真实 Redis:

type fakeRedis struct { err error calls int } func (f *fakeRedis) Ping(context.Context) error { f.calls++ return f.err }

有了这个 fakeRedis,service 就可以直接测试了:

func TestGreetingServiceWithFakeRedis(t *testing.T) { redis := &fakeRedis{} service := &GreetingService{redis: redis, Greeting: "Hi"} got := service.Message(context.Background(), "tester") if got != "Hi, tester!" { t.Fatalf("unexpected greeting: %q", got) } if redis.calls != 1 { t.Fatalf("expected one redis ping, got %d", redis.calls) } }

对于 controller,咱们也可以不启动真实的 HTTP Server,而是使用httptest来测试 handler:

func TestControllerWithFakeRedis(t *testing.T) { service := &GreetingService{redis: &fakeRedis{}, Greeting: "Hi"} controller := &Controller{service: service, Audience: "controller"} req := httptest.NewRequest(http.MethodGet, "/hello", nil) rec := httptest.NewRecorder() controller.Hello(rec, req) if rec.Code != http.StatusOK { t.Fatalf("unexpected status: %d", rec.Code) } if strings.TrimSpace(rec.Body.String()) != "Hi, controller!" { t.Fatalf("unexpected body: %q", rec.Body.String()) } }

上面都是非常纯粹的 Go 原生单元测试,并没有依赖 Go-Spring 容器。
如果咱们还想验证 Go-Spring 容器里的装配关系,可以按照下面的步骤进行。

  • 首先使用gs.Web(false)关闭真实的 HTTP Server,
  • 然后使用app.Provide(&fakeRedis{}).Export(...)把 fakeRedis 注册成接口实现,
  • 最后可以在gs.RunTest()中注入要检查的对象。

代码如下:

func TestIoCContainerWithFakeRedis(t *testing.T) { gs.Web(false).Configure(func(app gs.App) { app.Property("spring.app.config.dir", "./testdata/empty-conf") // The built-in Redis client is not enabled app.Provide(&fakeRedis{}).Export(gs.As[RedisPinger]()) }).RunTest(t, func(ts *struct { Service *GreetingService `autowire:""` Controller *Controller `autowire:""` }) { if ts.Service == nil { t.Fatal("service was not injected") } if ts.Controller == nil { t.Fatal("controller was not injected") } got := ts.Service.Message(context.Background(), "ioc") if got != "Hello, ioc!" { t.Fatalf("unexpected ioc greeting: %q", got) } }) }

gs.RunTest()在运行的时候会启动完整的 Go-Spring 容器,对 init 注册的对象进行装配。
它接受一个回调函数,回调函数的参数是一个结构体,用来注入要检查的对象,
可以使用autowirevalue标签来注入对象或者配置。

完整代码在 examples/10-unit-tests/main.go,
测试代码在 examples/10-unit-tests/main_test.go。

使用下面的命令运行测试:

cd examples/10-unit-tests go test

可以看到,所有测试都通过了。

这一章把前面所有的能力都落到可测试性上。
接口让外部依赖可以被 fakeRedis 替换,Export(gs.As[...])让生产实现按接口进入容器,
gs.Web(false)gs.RunTest()让容器装配本身也能被测试。

至此,一个 Go-Spring 应用从最小启动、HTTP 路由、配置绑定、容器装配、外部客户端、条件多实例、
结构化日志到测试的完整路径就串起来了。


从上面十个示例可以看到,Go-Spring 的核心价值并不是要替代 Go 生态中已有的标准库和工具,

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 14:32:18

RFID技术赋能电力应急物资智慧管理

地震、冰灾、台风等自然灾害发生时&#xff0c;电力抢险就是与时间赛跑。但传统应急物资仓库往往存在储备不清、位置不明、出库缓慢、调度混乱等问题。在分秒必争的应急时刻&#xff0c;如果找物资就要花上一两个小时&#xff0c;代价是不可承受的。行业破局RFID技术凭借其快速…

作者头像 李华
网站建设 2026/7/6 14:25:34

Claude Code 子智能体实战:让 AI 自己调 AI 来写代码

上周我在调一个挺复杂的 CI/CD 流水线&#xff0c;任务涉及三个环节&#xff1a;编译、静态分析、部署。按以前的做法&#xff0c;我得把整个流程写在一个 Prompt 里&#xff0c;结果 Claude 经常在中间步骤遗忘上下文&#xff0c;改一次 Prompt 要等好几分钟才知道对不对。但我…

作者头像 李华
网站建设 2026/7/6 14:23:56

Linux基础2+基础目录与文件管理

绝对路径与相对路径绝对路径&#xff08;例如cd /etc/grub/site-font&#xff09;写清楚完整的地址&#xff0c;可以从A里面的a里面的1直接去B里面的b里面的1,就是cd /A/a/1相对路径只能在当下目录找东西&#xff0c;比如现在在a&#xff0c;写cd A去不了A&#xff0c;写cd 1可…

作者头像 李华
网站建设 2026/7/6 14:23:25

基于波导匀光整形的照明光学系统仿真

一、波导&#xff08;积分管&#xff09;介绍 照明系统的一大作用就是将光非常均匀的投影到成像系统或者微型芯片上&#xff0c;例如数字投影系统&#xff0c;但是将光束变得均匀靠寻常透镜实现难度比较大&#xff0c;况且芯片经常遇到的是方形的&#xff0c;光源发光一般是圆…

作者头像 李华
网站建设 2026/7/6 14:21:46

编译原理:文法和语言

📌目录 ⚖️ 文法和语言:形式语言理论的基石 🎯 一、基本概念 (一)字母表与字符串 (二)语言 📦 二、文法的形式化定义 (一)文法的定义 (二)产生式 (三)文法示例 🌐 三、推导与归约 (一)推导 (二)归约 (三)句型与句子 📊 四、语法树与二义性 (一)语…

作者头像 李华
网站建设 2026/7/6 14:15:58

Scikit-learn 1.5.0 波士顿房价预测:7种回归模型R2对比与过拟合分析

Scikit-learn 1.5.0 波士顿房价预测&#xff1a;7种回归模型深度评测与实战指南房价预测一直是机器学习领域最经典的回归问题之一。波士顿房价数据集作为学术界和工业界广泛使用的基准数据集&#xff0c;为我们提供了研究不同回归算法性能的绝佳机会。本文将使用Scikit-learn 1…

作者头像 李华