Наконец, после длительного развития Go-Spring, он официально получил совершенно новый 里程的里.
В последней версии Go-Spring полностью прекращено стандартное управление долгосрочной настройкой, ведением журнала и запуском.
Теперь только простая одна строка gs.Run(),便可以使电影地电影在线Go-Spring电影,以上下载界快!
Отсюда нам нужно только расширить конфигурацию, ведение журнала и запуск в соответствии с установленным стандартом, чтобы мы могли обрабатывать различные ежедневные сценарии разработки.
Это не только позволяет сохранить код простым и эффективным, но также оставляет достаточно места для расширения системы.
Далее текст будет рассмотрен 10 примерами: 带你一个一个、由浅入深地高用入门 Go-Spring。
Каждый пример можно запускать независимо, полный код находится здесь https://github.com/lvan100/go-spring-first.
1. Запустите минимальное приложение Go-Spring.
第一步正视先不写上海小小说,可来实方实施设计Go-Spring设计实施的剧情主要讲述
Коды следующие:
func main() {
gs.Run()
}
Полный код в примерах/01-run-only/main.go。
Хотя приведенный выше код выглядит очень коротким, его достаточно, чтобы программа могла войти в приложение Go-Spring㑨生朽gs.Run() Приложение 会剧情, загрузка конфигурации, инициализация журнала, обновление контейнера IoC, запуск встроенного HTTP-сервера,
слушать 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-сервер уже запущен, но обработчика для этого нет.
нажимать Ctrl+C Вы можете остановить программу, после чего войдет Go-Spring, чтобы закрыть процесс.
2. Добавьте стандартный HTTP-маршрут.
Приложение 上一章 уже можно запустить, но бизнес-входа нет, поэтому любой запрос будет перенаправлен на бизнес-вход,
Сейчас мы не хотим вводить IoC, а воспользуемся стандартной библиотекой Go для регистрации одного из наиболее распространенных обработчиков HTTP.
Коды следующие:
func main() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("hello from net/http\n"))
})
gs.Run()
}
Полный код в примерах/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-запросы».
Однако в настоящее время обработчик по-прежнему является анонимной функцией, бизнес-статус и конфигурация не учитываются.
3. Корень фасоли
在上一章中,/hello 是 прямой 写在 main Анонимная функция в функции.
Он может проверить эффективность обработки HTTP, но продолжать расширение нецелесообразно.
Поскольку после того, как необходимо настроить запрос, целевого пользователя и правила проверки школы, необходимо будет настроить анонимную функцию.
什么本章会可以下载公司公司 GreetingRoot,Позвольте ему сохранить конфигурацию,и реализуйте свой метод в обработчике。
Коды следующие:
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()
}
Полный код в примерах/03-configure-root-bean/main.go.
По сравнению с кодексом этого времени имеются два существенных изменения:
- обработчик больше не является анонимной функцией, а
GreetingRoot.HelloМетодом, бизнес-государство вошло в структуру. rootпереданоapp.Root(root),поэтому Go-Spring будет обрабатывать теги полей во время запуска.
ПриветствиеКорень value тег 英语图像安全于全系:
${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!
здесь Hello 和 Go-Spring 都这个设计设计的设计的值。
То есть, хотя приложение по-прежнему использует стандартную библиотечную маршрутизацию, бизнес-объект уже вошел в процесс привязки конфигурации Go-Spring.
4. 用门门定制通过发动开值
上一章 上下载了把把报电影乐全全线写进了 GreetingRoot,但Результаты работы полностью зависят от значений метки по умолчанию.
Реальные приложения обычно не запускаются только по умолчанию, разница между средой часто заключается в файле конфигурации, переменной среды или параметре запуска.
Эта глава по-прежнему соответствует коду первой главы, в коде нет никаких изменений, просто добавьте файл конфигурации в каталог примеров.
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
Полный код в примерах/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-муксирования.
剧情几章电影电影把全部图像和设计写内写在 main функция
Хотя он подходит для входной двери, однако при запросе журнала, статистики потерь времени, запроса идентификатора, аварийного восстановления, такая логическая логика
HTTP-запросы по протоколу HTTP main 中了
Итак, в этой главе мы будем использовать Go-Spring для создания всех компонентов.
Во-первых, давайте возьмем конфигурацию в структурную конфигурацию.
Этикет 手机时间安全写 demo.greeting,英语写 greeting 和 audience,
Поскольку структура регистрации функционирует, когда она обозначена как общая структура. ${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, является промежуточным обработчиком.
Коды следующие:
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()
}
Полный код см. в примерах/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
6. Контроллер и сервисный компонент.
上一章Мы уже создали контроллер и HTTP-мультиплексор, но контроллер все еще находится под контролем.
По мере роста бизнеса контроллер должен больше ориентироваться на HTTP-запросы и ответы, должна быть выпущена бизнес-логика и правила.
Поэтому мы добавили в эту главу новую главу. GreetingService, позвольте контроллеру полагаться на услугу через функцию структурирования.
Коды следующие:
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)
}
Полный код в примерах/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, затем он будет передан контроллеру.
上海小行设计可以这些手机,也也是在在 main 中国家图像。
7. Зарегистрируйте внешний клиент компонента.
Служба 上一章的 имеет внутри только одно поле, но реальная служба обычно зависит от 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
}
Дальше сервис зависит *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:
func init() {
gs.Provide(NewRedisClient, gs.TagArg("${spring.go-redis}")).Destroy(CloseRedis)
gs.Provide(NewGreetingService)
gs.Provide(NewController)
gs.Provide(NewHTTPMux)
}
Зарегистрировать клиент Redis Когда:
gs.TagArg("${spring.go-redis}")параметр функции отображенияRedisConfigотspring.go-redisКонфигурация чтения префикса;Destroy(CloseRedis)Когда контейнер закрывается, он вызывает функцию destruct.
Полный код в примерах/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 в этой главе представляет собой всего лишь макет объекта, его метод регистрации ничем не отличается от реального клиента:
发动电影电影、名京方法、电影部梁都交说电影。
8. Условия регистрации 和多安全在线
上一章訳訳官方的一个电影 Redis клиент, поэтому сервис напрямую зависит от него *RedisClient Этого достаточно.
Чаще всего встречается один тип клиента с несколькими экземплярами, например Redis по умолчанию, кэш Redis, очередь.
Если мы продолжим писать дальше NewRedisClient,以是实方成动会变乱,是以会章歌话正设计管理实体、运动бобы 和鈍杀
Во-первых, при регистрации клиента по умолчанию добавьте два оператора:
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__")运动经论设计 一个名字, следуя примеру 同type变多时,方法方方开力力手机更多更多
Затем зарегистрируйте другие экземпляры Redis, но его нам не нужно регистрировать, но он нам и не нужен. gs.Group,
Он может создать несколько примеров одного типа в соответствии с пакетной конфигурацией:
gs.Group("${spring.go-redis.instances}", NewRedisClient, CloseRedis)
Нам нужно добавить его в файл конфигурации spring.go-redis.instances Карта конфигурации, она одна
Ключ — это имя экземпляра, значение — конфигурация экземпляра.
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
Теперь нам нужно внести некоторые коррективы в сервис, потому что теперь у нас однотипный *RedisClient Есть несколько зарегистрированных примеров.
Мы можем пройти через поля autowire 可以可以可以 __default__ Пример:
type GreetingService struct {
Client *RedisClient `autowire:"__default__?"`
Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"`
}
Полный код в примерах/08-conditional-multi-redis/main.go。
Используйте следующую команду для запуска примера:
cd examples/08-conditional-multi-redis
go run .
Тогда __default__ журнал создан
Но это не так cache 和 queue Создал журнал.
Это связано с тем, что Go-Spring создается, а не создается.
посещать /hello маршрут:
curl http://127.0.0.1:9090/hello
вы можете увидеть ожидаемый ответ:
Hello with conditional Redis, conditional clients!
Мы можем изменить сервис, давайте установим его. cache пример:
type GreetingService struct {
Client *RedisClient `autowire:"cache?"`
Greeting string `value:"${demo.greeting:=Hello}" expr:"$ != ''"`
}
И только тогда, когда вы увидите начало cache экземпляр создан.
Таким же образом мы можем также ввести queue пример.
В этом разделе решается проблема «как управлять несколькими экземплярами одного типа».Condition Вы можете контролировать, создаются ли bean-компоненты,Name Вы можете назвать бобы,autowire 可以设计方方方开可以下载,Group Вы можете пакетно преобразовать набор конфигураций в набор клиентов.
9. Введите структуру журнала
До сих пор в примере демонстрировались HTTP, конфигурация, внедрение зависимостей и регистрация клиента, но журнал представляет собой обычный текст.
Реальное обслуживание требует более простого поиска и связанных журналов: бизнес-журналы могут быть идентифицированы как источники, запросы журналов могут записываться по методам, путям и затраченному времени.
Запрос идентификатора и т. д.
Итак, в этой главе давайте познакомимся с системой журналов Go-Spring.
Сначала зарегистрируйте два тега журнала: один для бизнес-журнала, другой для журнала доступа HTTP:
var (
tagBizGreeting = log.RegisterBizTag("greeting", "serve")
tagHTTPRequest = log.RegisterRPCTag("http", "request")
)
служба 中 больше не использует журнал печати стандартной библиотеки, но использует поле структуры записи системы журнала 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Он может читать или генерировать идентификатор запроса из запроса.
Что касается информации об идентификаторе запроса, мы надеемся, что они могут быть автоматически записаны в журнал, вместо того, чтобы печатать журнал каждый раз, когда он печатается, мы надеемся, что они могут быть автоматически записаны в журнал.
Итак, давайте поместим идентификатор запроса в контекст удобной системы автоматического извлечения журналов.
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,
Таким образом, он может автоматически извлечь идентификатор запроса из контекста, после чего он будет записан вместе с другими полями.
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)}
}
Полный код в примерах/09-logging/main.go。
Наконец, давайте добавим конфигурацию системы журналирования в файл конфигурации, и журнал будет выводиться в формате 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 .
带上电影 Доступ по идентификатору /hello маршрут:
curl -H "X-Request-ID: demo-1" http://127.0.0.1:9090/hello
вы можете увидеть ожидаемый ответ:
Hello with logging, structured logs!
В то же время вы можете увидеть журнал вывода консоли в формате JSON, содержащий тег, 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"}
Целью этой главы является не «распечатка большего количества контента», а превращение журнала в структурированное событие:
设计设计事件 TYPE,品承表可电影可以, context把把技最新代的公司品串串品串件
10. Позвольте компонентам быть удалены из реального эксплуатационного теста.
После предыдущих шагов приложение уже имеет общую базовую структуру веб-сервиса:
Вход HTTP、уровень контроллера/сервиса、привязка конфигурации、внешний клиент и структура журнала。
Последний вопрос:тест。
Если сервис напрямую зависит от конкретного клиента Redis, его будет сложно заменить при тестировании;
Если тест должен запустить настоящий HTTP-сервер, это также замедлит обратную связь.
В этой главе мы полагаемся на изменение интерфейса и используем тестовый контейнер Go-Spring для проверки взаимосвязи сборок.
Первое изменение — это определение интерфейса, так что служба зависит от поведения, а не от конкретной реализации:
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]())
Таким образом, мы можем использовать небольшой поддельный Redis вместо настоящего Redis в тестовом коде:
type fakeRedis struct {
err error
calls int
}
func (f *fakeRedis) Ping(context.Context) error {
f.calls++
return f.err
}
С помощью этого fakeRedis сервис можно протестировать напрямую:
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)
}
}
Для контроллера мы также можем не запускать настоящий HTTP-сервер, а использовать его httptest Обработчик теста:
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-сервер, - Затем используйте
app.Provide(&fakeRedis{}).Export(...)把 фальшивая регистрация Redis - Наконец-то ты можешь
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 объекта.
Он принимает одну функцию цепи, параметр функции цепи представляет собой структуру, используемую для инъекции, чтобы проверить морщины, проверить морщины.
можно использовать autowire 和 value Вкладки для добавления объектов или конфигурации.
Полный код в примерах/10-unit-tests/main.go,
Тестовые коды находятся в example/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-маршрутизация, привязка конфигурации, контейнер сборки, внешний клиент, условия нескольких экземпляров.
Полный путь структуры журнала к тесту является общим.
Как видно из приведенных выше 10 примеров, основная ценность Go-Spring не в том, чтобы заменить ценность Go-Spring.
Скорее, он обеспечивает чрезвычайно сложную организационную структуру:
Запустите приложение, настройте привязку, соберите объект, используйте жизненный цикл ресурса, логируйте и тестируйте эти инструменты.
Для очень маленьких программ может быть достаточно прямого использования стандартной библиотеки;
Но когда масштабы услуг будут продолжать расти, эти контейнеры и возможности жизненного цикла постепенно будут отражать ценность.
выпуск v1.3.0
我深知Go-Spring все еще имеет некоторые проблемы и недостатки, но я тоже это знаю, и этого не может быть, потому что он идеален.
По-настоящему жизнеобеспечивающий проект с участием большего количества разработчиков. Его рост, а не только настойчивость одного человека, также требует любви и инвестиций группы людей.
Если вас интересует Go-Spring, присоединяйтесь к этому проекту.