From 17fea1bd5cc051b074eb3d235dd0a1c08ba6c69d Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 28 Dec 2024 19:11:55 +0800 Subject: [PATCH 01/75] init version --- .gitignore | 3 +- LICENSE | 1 + README.md | 707 ---------- conf/README.md | 0 conf/bind.go | 37 +- conf/bind_test.go | 2 +- conf/conf.go | 2 +- conf/conf_test.go | 2 +- conf/internal/path.go | 2 +- conf/internal/path_test.go | 2 +- conf/internal/storage.go | 2 +- conf/internal/storage_test.go | 2 +- conf/prop/prop.go | 2 +- conf/prop/prop_test.go | 2 +- conf/testdata/config/application.properties | 2 +- conf/testdata/config/application.yaml | 2 +- conf/toml/toml.go | 2 +- conf/toml/toml_test.go | 2 +- conf/yaml/yaml.go | 2 +- conf/yaml/yaml_test.go | 2 +- database/database.go | 22 - dync/bool.go | 2 +- dync/duration.go | 2 +- dync/dync.go | 2 +- dync/dync_test.go | 2 +- dync/event.go | 2 +- dync/float32.go | 2 +- dync/float64.go | 2 +- dync/int32.go | 2 +- dync/int64.go | 2 +- dync/ref.go | 2 +- dync/string.go | 2 +- dync/time.go | 2 +- dync/uint32.go | 2 +- dync/uint64.go | 2 +- expr/expr.go | 4 +- expr/expr_test.go | 2 +- go.mod | 11 +- go.sum | 40 +- grpc/grpc.go | 33 - gs/README.md | 1 - gs/app.go | 179 +-- gs/app_args.go | 2 +- gs/app_args_test.go | 2 +- gs/app_bootstrap.go | 2 +- gs/app_configuration.go | 2 +- gs/app_resource.go | 2 +- gs/app_test.go | 10 +- gs/arg/arg.go | 2 +- gs/arg/arg_test.go | 2 +- gs/boot.go | 147 +- gs/boot_web.go | 73 - gs/cond/cond.go | 2 +- gs/cond/cond_test.go | 2 +- gs/gs.go | 7 +- gs/gs_bean.go | 2 +- gs/gs_bean_test.go | 22 +- gs/gs_context.go | 2 +- gs/gs_dynamic_test.go | 2 +- gs/gs_test.go | 48 +- gs/gs_version.go | 2 +- gs/internal/triple_sort.go | 2 +- gs/testdata/config/application-test.yaml | 2 +- gs/testdata/config/application.properties | 12 +- gs/testdata/config/application.yaml | 2 +- gs/testdata/config/extension.properties | 2 +- gs/testdata/config/logger.xml | 2 +- gs/testdata/pkg/bar/pkg.go | 2 +- gs/testdata/pkg/foo/pkg.go | 2 +- mongo/mongo.go | 23 - mq/consumer.go | 83 -- mq/message.go | 84 -- mq/producer.go | 26 - redis/README.md | 11 - redis/case_base.go | 30 - redis/case_base_test.go | 192 --- redis/case_bitmap.go | 268 ---- redis/case_bitmap_test.go | 43 - redis/case_hash.go | 518 ------- redis/case_hash_test.go | 75 - redis/case_key.go | 672 --------- redis/case_key_test.go | 83 -- redis/case_list.go | 823 ----------- redis/case_list_test.go | 87 -- redis/case_set.go | 860 ------------ redis/case_set_test.go | 83 -- redis/case_string.go | 777 ----------- redis/case_string_test.go | 99 -- redis/case_zset.go | 1375 ------------------- redis/case_zset_test.go | 127 -- redis/ops_bitmap.go | 99 -- redis/ops_hash.go | 138 -- redis/ops_key.go | 163 --- redis/ops_list.go | 199 --- redis/ops_server.go | 29 - redis/ops_set.go | 189 --- redis/ops_string.go | 187 --- redis/ops_zset.go | 328 ----- redis/redis.go | 290 ---- redis/redis_error.go | 33 - redis/redis_mock.go | 50 - redis/redis_mock_test.go | 40 - validate/validate.go | 67 - web/README.md | 259 ---- web/basic-auth.go | 99 -- web/basic-auth_test.go | 39 - web/binding.go | 166 --- web/binding_form.go | 87 -- web/binding_form_test.go | 69 - web/binding_json.go | 32 - web/binding_json_test.go | 72 - web/binding_test.go | 70 - web/binding_xml.go | 32 - web/binding_xml_test.go | 72 - web/constants.go | 99 -- web/context-base.go | 464 ------- web/context-base_test.go | 17 - web/context.go | 319 ----- web/filter.go | 160 --- web/filter_test.go | 152 -- web/gzip.go | 129 -- web/gzip_test.go | 33 - web/i18n/i18n.go | 432 ------ web/i18n/i18n_test.go | 94 -- web/i18n/testdata/en/file.properties | 1 - web/i18n/testdata/zh.properties | 1 - web/method-override.go | 81 -- web/method-override_test.go | 35 - web/middleware.go | 73 - web/prefilter.go | 32 - web/redirect.go | 113 -- web/redirect_test.go | 36 - web/request-id.go | 49 - web/request-id_test.go | 37 - web/rewrite.go | 23 - web/rewrite_test.go | 17 - web/router.go | 323 ----- web/router_test.go | 122 -- web/rpc-result.go | 112 -- web/rpc-result_test.go | 72 - web/rpc.go | 104 -- web/server.go | 355 ----- web/server_test.go | 40 - web/swagger.go | 34 - web/url.go | 184 --- web/url_test.go | 135 -- 146 files changed, 136 insertions(+), 14347 deletions(-) delete mode 100644 conf/README.md delete mode 100644 database/database.go delete mode 100644 grpc/grpc.go delete mode 100644 gs/README.md delete mode 100644 gs/boot_web.go delete mode 100644 mongo/mongo.go delete mode 100644 mq/consumer.go delete mode 100644 mq/message.go delete mode 100644 mq/producer.go delete mode 100644 redis/README.md delete mode 100644 redis/case_base.go delete mode 100644 redis/case_base_test.go delete mode 100644 redis/case_bitmap.go delete mode 100644 redis/case_bitmap_test.go delete mode 100644 redis/case_hash.go delete mode 100644 redis/case_hash_test.go delete mode 100644 redis/case_key.go delete mode 100644 redis/case_key_test.go delete mode 100644 redis/case_list.go delete mode 100644 redis/case_list_test.go delete mode 100644 redis/case_set.go delete mode 100644 redis/case_set_test.go delete mode 100644 redis/case_string.go delete mode 100644 redis/case_string_test.go delete mode 100644 redis/case_zset.go delete mode 100644 redis/case_zset_test.go delete mode 100644 redis/ops_bitmap.go delete mode 100644 redis/ops_hash.go delete mode 100644 redis/ops_key.go delete mode 100644 redis/ops_list.go delete mode 100644 redis/ops_server.go delete mode 100644 redis/ops_set.go delete mode 100644 redis/ops_string.go delete mode 100644 redis/ops_zset.go delete mode 100644 redis/redis.go delete mode 100644 redis/redis_error.go delete mode 100644 redis/redis_mock.go delete mode 100644 redis/redis_mock_test.go delete mode 100644 validate/validate.go delete mode 100644 web/README.md delete mode 100644 web/basic-auth.go delete mode 100644 web/basic-auth_test.go delete mode 100644 web/binding.go delete mode 100644 web/binding_form.go delete mode 100644 web/binding_form_test.go delete mode 100644 web/binding_json.go delete mode 100644 web/binding_json_test.go delete mode 100644 web/binding_test.go delete mode 100644 web/binding_xml.go delete mode 100644 web/binding_xml_test.go delete mode 100644 web/constants.go delete mode 100644 web/context-base.go delete mode 100644 web/context-base_test.go delete mode 100644 web/context.go delete mode 100644 web/filter.go delete mode 100644 web/filter_test.go delete mode 100644 web/gzip.go delete mode 100644 web/gzip_test.go delete mode 100644 web/i18n/i18n.go delete mode 100644 web/i18n/i18n_test.go delete mode 100644 web/i18n/testdata/en/file.properties delete mode 100644 web/i18n/testdata/zh.properties delete mode 100644 web/method-override.go delete mode 100644 web/method-override_test.go delete mode 100644 web/middleware.go delete mode 100644 web/prefilter.go delete mode 100644 web/redirect.go delete mode 100644 web/redirect_test.go delete mode 100644 web/request-id.go delete mode 100644 web/request-id_test.go delete mode 100644 web/rewrite.go delete mode 100644 web/rewrite_test.go delete mode 100644 web/router.go delete mode 100644 web/router_test.go delete mode 100644 web/rpc-result.go delete mode 100644 web/rpc-result_test.go delete mode 100644 web/rpc.go delete mode 100644 web/server.go delete mode 100644 web/server_test.go delete mode 100644 web/swagger.go delete mode 100644 web/url.go delete mode 100644 web/url_test.go diff --git a/.gitignore b/.gitignore index 47f36f65..2137f544 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -vendor \ No newline at end of file +vendor/ +.idea/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9e..57bc88a1 100644 --- a/LICENSE +++ b/LICENSE @@ -199,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/README.md b/README.md index 0267e6d3..852d2932 100644 --- a/README.md +++ b/README.md @@ -1,708 +1 @@ # spring-core - -[仅发布] 该项目仅为最终发布,开发请关注 [go-spring](https://github.com/go-spring/go-spring) 项目。 - -实现了一个功能完善的运行时 IoC 容器。 - -- [SpringContext](#springcontext) - - [属性操作](#属性操作) - - [LoadProperties](#loadproperties) - - [ReadProperties](#readproperties) - - [GetProperty](#getproperty) - - [GetBoolProperty](#getboolproperty) - - [GetIntProperty](#getintproperty) - - [GetUintProperty](#getuintproperty) - - [GetFloatProperty](#getfloatproperty) - - [GetStringProperty](#getstringproperty) - - [GetDurationProperty](#getdurationproperty) - - [GetTimeProperty](#gettimeproperty) - - [GetDefaultProperty](#getdefaultproperty) - - [SetProperty](#setproperty) - - [GetPrefixProperties](#getprefixproperties) - - [GetGroupedProperties](#getgroupedproperties) - - [GetProperties](#getproperties) - - [BindProperty](#bindproperty) - - [BindPropertyIf](#bindpropertyif) - - [容器配置](#容器配置) - - [Context](#context) - - [GetProfile](#getprofile) - - [SetProfile](#setprofile) - - [Bean 注册](#bean-注册) - - [RegisterBean](#registerbean) - - [RegisterNameBean](#registernamebean) - - [RegisterBeanFn](#registerbeanfn) - - [RegisterNameBeanFn](#registernamebeanfn) - - [RegisterMethodBean](#registermethodbean) - - [RegisterNameMethodBean](#registernamemethodbean) - - [RegisterMethodBeanFn](#registermethodbeanfn) - - [RegisterNameMethodBeanFn](#registernamemethodbeanfn) - - [RegisterBeanDefinition](#registerbeandefinition) - - [依赖注入](#依赖注入) - - [AutoWireBeans](#autowirebeans) - - [WireBean](#wirebean) - - [获取 Bean](#获取-bean) - - [GetBean](#getbean) - - [FindBean](#findbean) - - [CollectBeans](#collectbeans) - - [GetBeanDefinitions](#getbeandefinitions) - - [任务配置](#任务配置) - - [Run](#run) - - [RunNow](#runnow) - - [Config](#config) - - [ConfigWithName](#configwithname) - - [容器销毁](#容器销毁) - - [Close](#close) - - [其他功能](#其他功能) - - [SafeGoroutine](#safegoroutine) -- [Condition](#condition) -- [Bean](#bean) - - [IsRefType](#isreftype) - - [IsValueType](#isvaluetype) - - [TypeName](#typename) - - [BeanSelector](#beanselector) - - [beanStatus](#beanstatus) - - [BeanDefinition](#beandefinition) - - [Bean](#bean-1) - - [Type](#type) - - [Value](#value) - - [TypeName](#typename-1) - - [Name](#name) - - [BeanId](#beanid) - - [FileLine](#fileline) - - [WithName](#withname) - - [Or](#or) - - [And](#and) - - [ConditionOn](#conditionon) - - [ConditionNot](#conditionnot) - - [ConditionOnProperty](#conditiononproperty) - - [ConditionOnMissingProperty](#conditiononmissingproperty) - - [ConditionOnPropertyValue](#conditiononpropertyvalue) - - [ConditionOnOptionalPropertyValue](#conditiononoptionalpropertyvalue) - - [ConditionOnBean](#conditiononbean) - - [ConditionOnMissingBean](#conditiononmissingbean) - - [ConditionOnExpression](#conditiononexpression) - - [ConditionOnMatches](#conditiononmatches) - - [ConditionOnProfile](#conditiononprofile) - - [Options](#options) - - [DependsOn](#dependson) - - [Primary](#primary) - - [Init](#init) - - [Destroy](#destroy) - - [Export](#export) - - [Bean 创建函数](#bean-创建函数) - - [ObjectBean](#objectbean) - - [ConstructorBean](#constructorbean) - - [MethodBean](#methodbean) - -## SpringContext - -SpringContext 定义了 IoC 容器接口。它的工作过程可以分为三个大的阶段:注册 Bean 列表、加载属性配置文件、自动绑定。其中自动绑定又分为两个小阶段:解析(决议)和绑定。 - -一条需要谨记的注册规则是: `AutoWireBeans` 调用后就不能再注册新的 Bean 了,这样做是因为实现起来更简单而且性能更高。 - -### 属性操作 - -#### LoadProperties - -加载属性配置文件,支持 properties、yaml 和 toml 三种文件格式。 - -``` -func LoadProperties(filename string) -``` - -#### ReadProperties - -读取属性配置文件,支持 properties、yaml 和 toml 三种文件格式。 - -``` -func ReadProperties(reader io.Reader, configType string) -``` - -#### GetProperty - -返回 keys 中第一个存在的属性值,属性名称统一转成小写。 - -``` -func GetProperty(keys ...string) interface{} -``` - -#### GetBoolProperty - -返回 keys 中第一个存在的布尔型属性值,属性名称统一转成小写。 - -``` -func GetBoolProperty(keys ...string) bool -``` - -#### GetIntProperty - -返回 keys 中第一个存在的有符号整型属性值,属性名称统一转成小写。 - -``` -func GetIntProperty(keys ...string) int64 -``` - -#### GetUintProperty - -返回 keys 中第一个存在的无符号整型属性值,属性名称统一转成小写。 - -``` -func GetUintProperty(keys ...string) uint64 -``` - -#### GetFloatProperty - -返回 keys 中第一个存在的浮点型属性值,属性名称统一转成小写。 - -``` -func GetFloatProperty(keys ...string) float64 -``` - -#### GetStringProperty - -返回 keys 中第一个存在的字符串型属性值,属性名称统一转成小写。 - -``` -func GetStringProperty(keys ...string) string -``` - -#### GetDurationProperty - -返回 keys 中第一个存在的 Duration 类型属性值,属性名称统一转成小写。 - -``` -func GetDurationProperty(keys ...string) time.Duration -``` - -#### GetTimeProperty - -返回 keys 中第一个存在的 Time 类型的属性值,属性名称统一转成小写。 - -``` -func GetTimeProperty(keys ...string) time.Time -``` - -#### GetDefaultProperty - -返回属性值,如果没有找到则使用指定的默认值,属性名称统一转成小写。 - -``` -func GetDefaultProperty(key string, def interface{}) (interface{}, bool) -``` - -#### SetProperty - -设置属性值,属性名称统一转成小写。 - -``` -func SetProperty(key string, value interface{}) -``` - -#### GetPrefixProperties - -返回指定前缀的属性值集合,属性名称统一转成小写。 - -``` -func GetPrefixProperties(prefix string) map[string]interface{} -``` - -#### GetGroupedProperties - -返回指定前缀的属性值集合并进行分组,属性名称统一转成小写。 - -``` -func GetGroupedProperties(prefix string) map[string]map[string]interface{} -``` - -#### GetProperties - -返回所有的属性值,属性名称统一转成小写。 - -``` -func GetProperties() map[string]interface{} -``` - -#### BindProperty - -根据类型获取属性值,属性名称统一转成小写。 - -``` -func BindProperty(key string, i interface{}) -``` - -#### BindPropertyIf - -根据类型获取属性值,属性名称统一转成小写。 - -``` -func BindPropertyIf(key string, i interface{}, allAccess bool) -``` - -### 容器配置 - -#### Context - -返回上下文接口。 - -``` -func Context() context.Context -``` - -#### GetProfile - -返回运行环境。 - -``` -func GetProfile() string -``` - -#### SetProfile - -设置运行环境。 - -``` -func SetProfile(profile string) -``` - -### Bean 注册 - -#### RegisterBean - -注册单例 Bean,不指定名称,重复注册会 panic。 - -``` -func RegisterBean(bean interface{}) *BeanDefinition -``` - -#### RegisterNameBean - -注册单例 Bean,需指定名称,重复注册会 panic。 - -``` -func RegisterNameBean(name string, bean interface{}) *BeanDefinition -``` - -#### RegisterBeanFn - -注册单例构造函数 Bean,不指定名称,重复注册会 panic。 - -``` -func RegisterBeanFn(fn interface{}, tags ...string) *BeanDefinition -``` - -#### RegisterNameBeanFn - -注册单例构造函数 Bean,需指定名称,重复注册会 panic。 - -``` -func RegisterNameBeanFn(name string, fn interface{}, tags ...string) *BeanDefinition -``` - -#### RegisterMethodBean - -注册成员方法单例 Bean,不指定名称,重复注册会 panic。必须给定方法名而不能通过遍历方法列表比较方法类型的方式获得函数名,因为不同方法的类型可能相同。而且 interface 的方法类型不带 receiver 而成员方法的类型带有 receiver,两者类型也不好匹配。 - -``` -func RegisterMethodBean(selector BeanSelector, method string, tags ...string) *BeanDefinition -``` - -#### RegisterNameMethodBean - -RegisterNameMethodBean 注册成员方法单例 Bean,需指定名称,重复注册会 panic。必须给定方法名而不能通过遍历方法列表比较方法类型的方式获得函数名,因为不同方法的类型可能相同。而且 interface 的方法类型不带 receiver 而成员方法的类型带有 receiver,两者类型也不好匹配。 - -``` -func RegisterNameMethodBean(name string, selector BeanSelector, method string, tags ...string) *BeanDefinition -``` - -#### RegisterMethodBeanFn - -注册成员方法单例 Bean,不指定名称,重复注册会 panic。method 形如 ServerInterface.Consumer (接口) 或 (*Server).Consumer (类型)。 - -``` -func RegisterMethodBeanFn(method interface{}, tags ...string) *BeanDefinition -``` - -#### RegisterNameMethodBeanFn - -注册成员方法单例 Bean,需指定名称,重复注册会 panic。method 形如 ServerInterface.Consumer (接口) 或 (*Server).Consumer (类型)。 - -``` -func RegisterNameMethodBeanFn(name string, method interface{}, tags ...string) *BeanDefinition -``` - -#### RegisterBeanDefinition - -注册 BeanDefinition 对象。 - -``` -func RegisterBeanDefinition(bd *BeanDefinition) -``` - -### 依赖注入 - -#### AutoWireBeans - -对所有 Bean 进行依赖注入和属性绑定。 - -``` -func AutoWireBeans() -``` - -#### WireBean - -对外部的 Bean 进行依赖注入和属性绑定。 - -``` -func WireBean(i interface{}) -``` - -### 获取 Bean - -#### GetBean - -获取单例 Bean,若多于 1 个则 panic;找到返回 true 否则返回 false。它和 FindBean 的区别是它在调用后能够保证返回的 Bean 已经完成了注入和绑定过程。 - -``` -func GetBean(i interface{}, selector ...BeanSelector) bool -``` - -#### FindBean - -查询单例 Bean,若多于 1 个则 panic;找到返回 true 否则返回 false。它和 GetBean 的区别是它在调用后不能保证返回的 Bean 已经完成了注入和绑定过程。 - -``` -func FindBean(selector BeanSelector) (*BeanDefinition, bool) -``` - -#### CollectBeans - -收集数组或指针定义的所有符合条件的 Bean,收集到返回 true,否则返回 false。该函数有两种模式:自动模式和指定模式。自动模式是指 selectors 参数为空,这时候不仅会收集符合条件的单例 Bean,还会收集符合条件的数组 Bean (是指数组的元素符合条件,然后把数组元素拆开一个个放到收集结果里面)。指定模式是指 selectors 参数不为空,这时候只会收集单例 Bean,而且要求这些单例 Bean 不仅需要满足收集条件,而且必须满足 selector 条件。另外,自动模式下不对收集结果进行排序,指定模式下根据selectors 列表的顺序对收集结果进行排序。 - -``` -func CollectBeans(i interface{}, selectors ...BeanSelector) bool -``` - -#### GetBeanDefinitions - -获取所有 Bean 的定义,不能保证解析和注入,请谨慎使用该函数! - -``` -func GetBeanDefinitions() []*BeanDefinition -``` - -### 任务配置 - -#### Run - -根据条件判断是否立即执行一个一次性的任务。 - -``` -func Run(fn interface{}, tags ...string) *Runner -``` - -#### RunNow - -立即执行一个一次性的任务。 - -``` -func RunNow(fn interface{}, tags ...string) error -``` - -#### Config - -注册一个配置函数。 - -``` -func Config(fn interface{}, tags ...string) *Configer -``` - -#### ConfigWithName - -注册一个配置函数,名称的作用是对 Config 进行排重和排顺序。 - -``` -func ConfigWithName(name string, fn interface{}, tags ...string) *Configer -``` - -### 容器销毁 - -#### Close - -关闭容器上下文,用于通知 Bean 销毁等。该函数可以确保 Bean 的销毁顺序和注入顺序相反。 - -``` -func Close(beforeDestroy ...func()) -``` - -### 其他功能 - -#### SafeGoroutine - -安全地启动一个 goroutine - -``` -func SafeGoroutine(fn GoFunc) -``` - -## Condition - -定义一个判断条件。 - -`NewFunctionCondition` 基于 Matches 方法的 Condition 实现。 -`NewNotCondition` 对 Condition 取反的 Condition 实现。 -`NewPropertyCondition` 基于属性值存在的 Condition 实现。 -`NewMissingPropertyCondition` 基于属性值不存在的 Condition 实现。 -`NewPropertyValueCondition` 基于属性值匹配的 Condition 实现。 -`NewBeanCondition` 基于 Bean 存在的 Condition 实现。 -`NewMissingBeanCondition` 基于 Bean 不能存在的 Condition 实现。 -`NewExpressionCondition` 基于表达式的 Condition 实现。 -`NewProfileCondition` 基于运行环境匹配的 Condition 实现。 -`NewConditions` 基于条件组的 Condition 实现。 -`NewConditional` Condition 计算式。 - -- `Or` c=a||b -- `And` c=a&&b -- `OnCondition` 设置一个 Condition。 -- `OnConditionNot` 设置一个取反的 Condition。 -- `ConditionOnProperty` 返回设置了 propertyCondition 的 Conditional 对象。 -- `ConditionOnMissingProperty` 返回设置了 missingPropertyCondition 的 Conditional 对象。 -- `ConditionOnPropertyValue` 返回设置了 propertyValueCondition 的 Conditional 对象。 -- `ConditionOnOptionalPropertyValue` 返回属性值不存在时默认条件成立的 Conditional 对象。 -- `OnOptionalPropertyValue` 设置一个 propertyValueCondition,当属性值不存在时默认条件成立。 -- `ConditionOnBean` 返回设置了 beanCondition 的 Conditional 对象。 -- `ConditionOnMissingBean` 返回设置了 missingBeanCondition 的 Conditional 对象。 -- `ConditionOnExpression` 返回设置了 expressionCondition 的 Conditional 对象。 -- `ConditionOnMatches` 返回设置了 functionCondition 的 Conditional 对象。 -- `ConditionOnProfile` 返回设置了 profileCondition 的 Conditional 对象。 - -## Bean - -#### IsRefType - -返回是否是引用类型。 - - func IsRefType(k reflect.Kind) bool - -#### IsValueType - -返回是否是值类型。 - - func IsValueType(k reflect.Kind) bool - -#### TypeName - -返回原始类型的全限定名,Go 语言允许不同的路径下存在相同的包,因此有全限定名的需求,形如 "github.com/go-spring/spring-core/SpringCore.BeanDefinition"。 - - func TypeName(typOrPtr TypeOrPtr) string - -#### BeanSelector - -Bean 选择器,可以是 BeanId 字符串,可以是 reflect.Type 对象或者形如 (*error)(nil) 的对象指针,还可以是 *BeanDefinition 对象。 - - type BeanSelector interface{} - -#### beanStatus - -Bean 的状态值。 - - beanStatus_Default = beanStatus(0) // 默认状态 - beanStatus_Resolving = beanStatus(1) // 正在决议 - beanStatus_Resolved = beanStatus(2) // 已决议 - beanStatus_Wiring = beanStatus(3) // 正在注入 - beanStatus_Wired = beanStatus(4) // 注入完成 - beanStatus_Deleted = beanStatus(5) // 已删除 - -### BeanDefinition - -用于存储 Bean 的各种元数据。 - -#### Bean - -返回 Bean 的源。 - - func (d *BeanDefinition) Bean() interface{} - -#### Type - -返回 Bean 的类型。 - - func (d *BeanDefinition) Type() reflect.Type - -#### Value - -返回 Bean 的值。 - - func (d *BeanDefinition) Value() reflect.Value - -#### TypeName - -返回 Bean 的原始类型的全限定名。 - - func (d *BeanDefinition) TypeName() string - -#### Name - -返回 Bean 的名称。 - - func (d *BeanDefinition) Name() string - -#### BeanId - -返回 Bean 的唯一 ID。 - - func (d *BeanDefinition) BeanId() string - -#### FileLine - -返回 Bean 的注册点。 - - func (d *BeanDefinition) FileLine() string - -#### WithName - -设置 Bean 的名称。 - - func (d *BeanDefinition) WithName(name string) *BeanDefinition - -#### Or - -c=a||b。 - - func (d *BeanDefinition) Or() *BeanDefinition - -#### And - -c=a&&b。 - - func (d *BeanDefinition) And() *BeanDefinition - -#### ConditionOn - -为 Bean 设置一个 Condition。 - - func (d *BeanDefinition) ConditionOn(cond Condition) *BeanDefinition - -#### ConditionNot - -为 Bean 设置一个取反的 Condition。 - - func (d *BeanDefinition) ConditionNot(cond Condition) *BeanDefinition - -#### ConditionOnProperty - -为 Bean 设置一个 PropertyCondition。 - - func (d *BeanDefinition) ConditionOnProperty(name string) *BeanDefinition - -#### ConditionOnMissingProperty - -为 Bean 设置一个 MissingPropertyCondition。 - - func (d *BeanDefinition) ConditionOnMissingProperty(name string) *BeanDefinition - -#### ConditionOnPropertyValue - -为 Bean 设置一个 PropertyValueCondition。 - - func (d *BeanDefinition) ConditionOnPropertyValue(name string, havingValue interface{}, options ...PropertyValueConditionOption) *BeanDefinition - -#### ConditionOnOptionalPropertyValue - -为 Bean 设置一个 PropertyValueCondition,当属性值不存在时默认条件成立。 - - func (d *BeanDefinition) ConditionOnOptionalPropertyValue(name string, havingValue interface{}) *BeanDefinition - -#### ConditionOnBean - -为 Bean 设置一个 BeanCondition。 - - func (d *BeanDefinition) ConditionOnBean(selector BeanSelector) *BeanDefinition - -#### ConditionOnMissingBean - -为 Bean 设置一个 MissingBeanCondition。 - - func (d *BeanDefinition) ConditionOnMissingBean(selector BeanSelector) *BeanDefinition - -#### ConditionOnExpression - -为 Bean 设置一个 ExpressionCondition。 - - func (d *BeanDefinition) ConditionOnExpression(expression string) *BeanDefinition - -#### ConditionOnMatches - -为 Bean 设置一个 FunctionCondition。 - - func (d *BeanDefinition) ConditionOnMatches(fn ConditionFunc) *BeanDefinition - -#### ConditionOnProfile - -为 Bean 设置一个 ProfileCondition。 - - func (d *BeanDefinition) ConditionOnProfile(profile string) *BeanDefinition - -#### Options - -设置 Option 模式函数的 Option 参数绑定。 - - func (d *BeanDefinition) Options(options ...*optionArg) *BeanDefinition - - // NewOptionArg optionArg 的构造函数,tags 是 Option 函数的一般参数绑定 - func NewOptionArg(fn interface{}, tags ...string) *optionArg {} - -#### DependsOn - -设置 Bean 的间接依赖项。 - - func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition - -#### Primary - -设置 Bean 为主版本。 - - func (d *BeanDefinition) Primary(primary bool) *BeanDefinition - -#### Init - -设置 Bean 的初始化函数,tags 是初始化函数的一般参数绑定。 - - func (d *BeanDefinition) Init(fn interface{}, tags ...string) *BeanDefinition - -#### Destroy - -设置 Bean 的销毁函数,tags 是销毁函数的一般参数绑定。 - - func (d *BeanDefinition) Destroy(fn interface{}, tags ...string) *BeanDefinition - -#### Export - -显式指定 Bean 的导出接口。 - - func (d *BeanDefinition) Export(exports ...TypeOrPtr) *BeanDefinition - -### Bean 创建函数 - -#### ObjectBean - -将 Bean 转换为 BeanDefinition 对象 - -``` -func ObjectBean(i interface{}) *BeanDefinition -``` - -#### ConstructorBean - -将构造函数转换为 BeanDefinition 对象 - -``` -func ConstructorBean(fn interface{}, tags ...string) *BeanDefinition -``` - -#### MethodBean - -将成员方法转换为 BeanDefinition 对象 - -``` -func MethodBean(selector BeanSelector, method string, tags ...string) *BeanDefinition -``` diff --git a/conf/README.md b/conf/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/conf/bind.go b/conf/bind.go index 4fed2c9e..70f3396c 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import ( "github.com/go-spring/spring-base/code" "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/validate" ) var ( @@ -149,9 +148,9 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var u uint64 if u, err = strconv.ParseUint(val, 0, 0); err == nil { - if err = validate.Field(u, param.Validate); err != nil { - return err - } + // if err = validate.Field(u, param.Validate); err != nil { + // return err + // } v.SetUint(u) return nil } @@ -159,9 +158,9 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 if i, err = strconv.ParseInt(val, 0, 0); err == nil { - if err = validate.Field(i, param.Validate); err != nil { - return err - } + // if err = validate.Field(i, param.Validate); err != nil { + // return err + // } v.SetInt(i) return nil } @@ -169,9 +168,9 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, case reflect.Float32, reflect.Float64: var f float64 if f, err = strconv.ParseFloat(val, 64); err == nil { - if err = validate.Field(f, param.Validate); err != nil { - return err - } + // if err = validate.Field(f, param.Validate); err != nil { + // return err + // } v.SetFloat(f) return nil } @@ -179,17 +178,17 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, case reflect.Bool: var b bool if b, err = strconv.ParseBool(val); err == nil { - if err = validate.Field(b, param.Validate); err != nil { - return err - } + // if err = validate.Field(b, param.Validate); err != nil { + // return err + // } v.SetBool(b) return nil } return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) case reflect.String: - if err = validate.Field(val, param.Validate); err != nil { - return err - } + // if err = validate.Field(val, param.Validate); err != nil { + // return err + // } v.SetString(val) return nil } @@ -345,8 +344,8 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, } if tag, ok := ft.Tag.Lookup("value"); ok { - validateTag, _ := ft.Tag.Lookup(validate.TagName()) - if err := subParam.BindTag(tag, validateTag); err != nil { + // validateTag, _ := ft.Tag.Lookup(validate.TagName()) + if err := subParam.BindTag(tag, ""); err != nil { return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) } if filter != nil { diff --git a/conf/bind_test.go b/conf/bind_test.go index 0fc7733b..bb4bd70f 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/conf.go b/conf/conf.go index 67e7c0c9..c68e5550 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/conf_test.go b/conf/conf_test.go index 98954405..938c055f 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/internal/path.go b/conf/internal/path.go index 7deaf97f..2896b0e6 100644 --- a/conf/internal/path.go +++ b/conf/internal/path.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/internal/path_test.go b/conf/internal/path_test.go index 861fee63..3f2d1839 100644 --- a/conf/internal/path_test.go +++ b/conf/internal/path_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/internal/storage.go b/conf/internal/storage.go index 33913cd4..2a8e466b 100644 --- a/conf/internal/storage.go +++ b/conf/internal/storage.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/internal/storage_test.go b/conf/internal/storage_test.go index 91ae0784..80f13d1b 100644 --- a/conf/internal/storage_test.go +++ b/conf/internal/storage_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/prop/prop.go b/conf/prop/prop.go index 097fb384..b536a10e 100644 --- a/conf/prop/prop.go +++ b/conf/prop/prop.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/prop/prop_test.go b/conf/prop/prop_test.go index eb5d3009..e8567341 100644 --- a/conf/prop/prop_test.go +++ b/conf/prop/prop_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/testdata/config/application.properties b/conf/testdata/config/application.properties index 7709df18..7981b2a9 100644 --- a/conf/testdata/config/application.properties +++ b/conf/testdata/config/application.properties @@ -3,4 +3,4 @@ properties.list[1]=2 properties.obj.list[0].name=tom properties.obj.list[0].age=4 properties.obj.list[1].name=jerry -properties.obj.list[1].age=2 \ No newline at end of file +properties.obj.list[1].age=2 diff --git a/conf/testdata/config/application.yaml b/conf/testdata/config/application.yaml index 0be7da6c..cb271886 100644 --- a/conf/testdata/config/application.yaml +++ b/conf/testdata/config/application.yaml @@ -86,4 +86,4 @@ prefix_map: password: 123456 url: 1.1.1.1 port: 3306 - db: db2 \ No newline at end of file + db: db2 diff --git a/conf/toml/toml.go b/conf/toml/toml.go index 72bf6617..dcc382cc 100644 --- a/conf/toml/toml.go +++ b/conf/toml/toml.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/toml/toml_test.go b/conf/toml/toml_test.go index 95f85c8a..3dff59d2 100644 --- a/conf/toml/toml_test.go +++ b/conf/toml/toml_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/yaml/yaml.go b/conf/yaml/yaml.go index e75bb03c..29712618 100644 --- a/conf/yaml/yaml.go +++ b/conf/yaml/yaml.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/conf/yaml/yaml_test.go b/conf/yaml/yaml_test.go index 2beb1a88..b6d376d3 100644 --- a/conf/yaml/yaml_test.go +++ b/conf/yaml/yaml_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/database/database.go b/database/database.go deleted file mode 100644 index 2b7584ef..00000000 --- a/database/database.go +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package database - -// Config is the configuration of database client. -type Config struct { - URL string `value:"${url}"` -} diff --git a/dync/bool.go b/dync/bool.go index f90e5c0b..2045c64a 100644 --- a/dync/bool.go +++ b/dync/bool.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/duration.go b/dync/duration.go index 9b29be3d..516c7501 100644 --- a/dync/duration.go +++ b/dync/duration.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/dync.go b/dync/dync.go index 0005b421..fc8c60a0 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/dync_test.go b/dync/dync_test.go index 68a290bc..0c1616a4 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/event.go b/dync/event.go index 0facd5e6..5b0d8033 100644 --- a/dync/event.go +++ b/dync/event.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/float32.go b/dync/float32.go index 12fef4b3..77ce195a 100644 --- a/dync/float32.go +++ b/dync/float32.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/float64.go b/dync/float64.go index bdeafe71..ec22a0f3 100644 --- a/dync/float64.go +++ b/dync/float64.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/int32.go b/dync/int32.go index 6431e600..795488c8 100644 --- a/dync/int32.go +++ b/dync/int32.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/int64.go b/dync/int64.go index e1055251..d6480031 100644 --- a/dync/int64.go +++ b/dync/int64.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/ref.go b/dync/ref.go index 8bfa79e8..0c11a9dd 100644 --- a/dync/ref.go +++ b/dync/ref.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/string.go b/dync/string.go index 9f43b88f..d22c9f21 100644 --- a/dync/string.go +++ b/dync/string.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/time.go b/dync/time.go index 31788c32..db4d6603 100644 --- a/dync/time.go +++ b/dync/time.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/uint32.go b/dync/uint32.go index 43da1eec..6cdb87fc 100644 --- a/dync/uint32.go +++ b/dync/uint32.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/dync/uint64.go b/dync/uint64.go index a37cb2a8..bfc438a6 100644 --- a/dync/uint64.go +++ b/dync/uint64.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/expr/expr.go b/expr/expr.go index afcf1d29..6ec95768 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package expr import ( - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" "github.com/go-spring/spring-base/code" "github.com/go-spring/spring-base/util" ) diff --git a/expr/expr_test.go b/expr/expr_test.go index f50ce730..55bd4a71 100644 --- a/expr/expr_test.go +++ b/expr/expr_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/go.mod b/go.mod index 10207d2b..ceb46c06 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,12 @@ module github.com/go-spring/spring-core -go 1.14 +go 1.22 require ( - github.com/antonmedv/expr v1.9.0 + github.com/expr-lang/expr v1.16.9 github.com/go-spring/spring-base v1.1.3 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.3.0 - github.com/magiconair/properties v1.8.5 - github.com/pelletier/go-toml v1.9.4 + github.com/magiconair/properties v1.8.9 + github.com/pelletier/go-toml v1.9.5 gopkg.in/yaml.v2 v2.4.0 ) - -//replace github.com/go-spring/spring-base => ../spring-base diff --git a/go.sum b/go.sum index a92a1887..3e698184 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,13 @@ -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= -github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= -github.com/go-spring/spring-base v1.1.3-0.20221009074117-5fc71d4a6063 h1:TaWsPu5T5ZSNpURPiIApXDZuYKzVNAfb+Vnp6jL0e3g= -github.com/go-spring/spring-base v1.1.3-0.20221009074117-5fc71d4a6063/go.mod h1:tdngm+6agA34HQ5YADitIGaQ04e1pmxuR5cd6Eaobmw= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/go-spring/spring-base v1.1.3 h1:oyPwSend8UFIYSk8X6x4PaRu3BrbLWK7rYc+htnqLWA= +github.com/go-spring/spring-base v1.1.3/go.mod h1:tdngm+6agA34HQ5YADitIGaQ04e1pmxuR5cd6Eaobmw= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -38,14 +19,11 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -53,7 +31,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/grpc/grpc.go b/grpc/grpc.go deleted file mode 100644 index 66c909cb..00000000 --- a/grpc/grpc.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package grpc - -// ServerConfig is the configuration of the grpc server. -type ServerConfig struct { - Port int `value:"${port:=9090}"` -} - -// EndpointConfig is the configuration of the grpc client. -type EndpointConfig struct { - Address string `value:"${address:=localhost:9090}"` -} - -// A Server is a service provider and its register function. -type Server struct { - Register interface{} // register function - Service interface{} // service provider -} diff --git a/gs/README.md b/gs/README.md deleted file mode 100644 index 4d4e3e8c..00000000 --- a/gs/README.md +++ /dev/null @@ -1 +0,0 @@ -# gs diff --git a/gs/app.go b/gs/app.go index a548579e..b0241e82 100644 --- a/gs/app.go +++ b/gs/app.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import ( "context" "fmt" "io/ioutil" - "net/http" "os" "os/signal" "path/filepath" @@ -32,10 +31,7 @@ import ( "github.com/go-spring/spring-base/log" "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/grpc" "github.com/go-spring/spring-core/gs/arg" - "github.com/go-spring/spring-core/mq" - "github.com/go-spring/spring-core/web" ) // SpringBannerVisible 是否显示 banner。 @@ -53,10 +49,7 @@ type AppEvent interface { } type tempApp struct { - router web.Router - consumers *Consumers - grpcServers *GrpcServers - banner string + banner string } // App 应用 @@ -74,45 +67,11 @@ type App struct { Runners []AppRunner `autowire:"${command-line-runner.collection:=*?}"` } -type Consumers struct { - consumers []mq.Consumer -} - -func (c *Consumers) Add(consumer mq.Consumer) { - c.consumers = append(c.consumers, consumer) -} - -func (c *Consumers) ForEach(fn func(mq.Consumer)) { - for _, consumer := range c.consumers { - fn(consumer) - } -} - -type GrpcServers struct { - servers map[string]*grpc.Server -} - -func (s *GrpcServers) Add(serviceName string, server *grpc.Server) { - s.servers[serviceName] = server -} - -func (s *GrpcServers) ForEach(fn func(string, *grpc.Server)) { - for serviceName, server := range s.servers { - fn(serviceName, server) - } -} - // NewApp application 的构造函数 func NewApp() *App { return &App{ - c: New().(*container), - tempApp: &tempApp{ - router: web.NewRouter(), - consumers: new(Consumers), - grpcServers: &GrpcServers{ - servers: map[string]*grpc.Server{}, - }, - }, + c: New().(*container), + tempApp: &tempApp{}, exitChan: make(chan struct{}), } } @@ -142,9 +101,6 @@ func (app *App) Run() error { } app.Object(app) - app.Object(app.consumers) - app.Object(app.grpcServers) - app.Object(app.router).Export((*web.Router)(nil)) app.logger = log.GetLogger(util.TypeName(app)) // 响应控制台的 Ctrl+C 及 kill 命令。 @@ -393,130 +349,3 @@ func (app *App) Object(i interface{}) *BeanDefinition { func (app *App) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { return app.c.Accept(NewBean(ctor, args...)) } - -// HttpGet 注册 GET 方法处理函数。 -func (app *App) HttpGet(path string, h http.HandlerFunc) *web.Mapper { - return app.router.HttpGet(path, h) -} - -// HandleGet 注册 GET 方法处理函数。 -func (app *App) HandleGet(path string, h web.Handler) *web.Mapper { - return app.router.HandleGet(path, h) -} - -// GetMapping 注册 GET 方法处理函数。 -func (app *App) GetMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.router.GetMapping(path, fn) -} - -// GetBinding 注册 GET 方法处理函数。 -func (app *App) GetBinding(path string, fn interface{}) *web.Mapper { - return app.router.GetBinding(path, fn) -} - -// HttpPost 注册 POST 方法处理函数。 -func (app *App) HttpPost(path string, h http.HandlerFunc) *web.Mapper { - return app.router.HttpPost(path, h) -} - -// HandlePost 注册 POST 方法处理函数。 -func (app *App) HandlePost(path string, h web.Handler) *web.Mapper { - return app.router.HandlePost(path, h) -} - -// PostMapping 注册 POST 方法处理函数。 -func (app *App) PostMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.router.PostMapping(path, fn) -} - -// PostBinding 注册 POST 方法处理函数。 -func (app *App) PostBinding(path string, fn interface{}) *web.Mapper { - return app.router.PostBinding(path, fn) -} - -// HttpPut 注册 PUT 方法处理函数。 -func (app *App) HttpPut(path string, h http.HandlerFunc) *web.Mapper { - return app.router.HttpPut(path, h) -} - -// HandlePut 注册 PUT 方法处理函数。 -func (app *App) HandlePut(path string, h web.Handler) *web.Mapper { - return app.router.HandlePut(path, h) -} - -// PutMapping 注册 PUT 方法处理函数。 -func (app *App) PutMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.router.PutMapping(path, fn) -} - -// PutBinding 注册 PUT 方法处理函数。 -func (app *App) PutBinding(path string, fn interface{}) *web.Mapper { - return app.router.PutBinding(path, fn) -} - -// HttpDelete 注册 DELETE 方法处理函数。 -func (app *App) HttpDelete(path string, h http.HandlerFunc) *web.Mapper { - return app.router.HttpDelete(path, h) -} - -// HandleDelete 注册 DELETE 方法处理函数。 -func (app *App) HandleDelete(path string, h web.Handler) *web.Mapper { - return app.router.HandleDelete(path, h) -} - -// DeleteMapping 注册 DELETE 方法处理函数。 -func (app *App) DeleteMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.router.DeleteMapping(path, fn) -} - -// DeleteBinding 注册 DELETE 方法处理函数。 -func (app *App) DeleteBinding(path string, fn interface{}) *web.Mapper { - return app.router.DeleteBinding(path, fn) -} - -// HandleRequest 注册任意 HTTP 方法处理函数。 -func (app *App) HandleRequest(method uint32, path string, h web.Handler) *web.Mapper { - return app.router.HandleRequest(method, path, h) -} - -// RequestMapping 注册任意 HTTP 方法处理函数。 -func (app *App) RequestMapping(method uint32, path string, fn web.HandlerFunc) *web.Mapper { - return app.router.RequestMapping(method, path, fn) -} - -// RequestBinding 注册任意 HTTP 方法处理函数。 -func (app *App) RequestBinding(method uint32, path string, fn interface{}) *web.Mapper { - return app.router.RequestBinding(method, path, fn) -} - -// File 定义单个文件资源 -func (app *App) File(path string, file string) *web.Mapper { - return app.router.File(path, file) -} - -// Static 定义一组文件资源 -func (app *App) Static(prefix string, dir string) *web.Mapper { - return app.router.Static(prefix, dir) -} - -// StaticFS 定义一组文件资源 -func (app *App) StaticFS(prefix string, fs http.FileSystem) *web.Mapper { - return app.router.StaticFS(prefix, fs) -} - -// Consume 注册 MQ 消费者。 -func (app *App) Consume(fn interface{}, topics ...string) { - app.consumers.Add(mq.Bind(fn, topics...)) -} - -// GrpcServer 注册 gRPC 服务提供者,fn 是 gRPC 自动生成的服务注册函数, -// serviceName 是服务名称,必须对应 *_grpc.pg.go 文件里面 grpc.ServerDesc -// 的 ServiceName 字段,server 是服务提供者对象。 -func (app *App) GrpcServer(serviceName string, server *grpc.Server) { - app.grpcServers.Add(serviceName, server) -} - -// GrpcClient 注册 gRPC 服务客户端,fn 是 gRPC 自动生成的客户端构造函数。 -func (app *App) GrpcClient(fn interface{}, endpoint string) *BeanDefinition { - return app.c.Accept(NewBean(fn, endpoint)) -} diff --git a/gs/app_args.go b/gs/app_args.go index fd4447da..61c0852c 100644 --- a/gs/app_args.go +++ b/gs/app_args.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/app_args_test.go b/gs/app_args_test.go index a0437b82..44a5a391 100644 --- a/gs/app_args_test.go +++ b/gs/app_args_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/app_bootstrap.go b/gs/app_bootstrap.go index a2467113..9af2c6a4 100644 --- a/gs/app_bootstrap.go +++ b/gs/app_bootstrap.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/app_configuration.go b/gs/app_configuration.go index e50ae8f8..8ec57429 100644 --- a/gs/app_configuration.go +++ b/gs/app_configuration.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/app_resource.go b/gs/app_resource.go index 9d049687..6d24ff2a 100644 --- a/gs/app_resource.go +++ b/gs/app_resource.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/app_test.go b/gs/app_test.go index 7420e932..943596b0 100644 --- a/gs/app_test.go +++ b/gs/app_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,11 +72,11 @@ func TestConfig(t *testing.T) { gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") app := startApplication("testdata/config/", func(ctx gs.Context) { assert.Equal(t, ctx.Prop("spring.profiles.active"), "dev") - //keys := ctx.Properties().Keys() - //sort.Strings(keys) - //for _, k := range keys { + // keys := ctx.Properties().Keys() + // sort.Strings(keys) + // for _, k := range keys { // fmt.Println(k, "=", ctx.Prop(k)) - //} + // } }) defer app.ShutDown("run test end") }) diff --git a/gs/arg/arg.go b/gs/arg/arg.go index 65165e3e..f2d26b63 100644 --- a/gs/arg/arg.go +++ b/gs/arg/arg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/arg/arg_test.go b/gs/arg/arg_test.go index 8461b116..d8487da0 100644 --- a/gs/arg/arg_test.go +++ b/gs/arg/arg_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/boot.go b/gs/boot.go index 18eb37b5..ef760150 100644 --- a/gs/boot.go +++ b/gs/boot.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,11 @@ package gs import ( - "net/http" "os" "reflect" "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/grpc" "github.com/go-spring/spring-core/gs/arg" - "github.com/go-spring/spring-core/web" ) var app = NewApp() @@ -35,24 +32,9 @@ func Setenv(key string, value string) { util.Panic(err).When(err != nil) } -type startup struct { - web bool -} - -func Web(enable bool) *startup { - return &startup{web: enable} -} - -func (s *startup) Run() error { - if s.web { - Object(new(WebStarter)).Export((*AppEvent)(nil)) - } - return app.Run() -} - // Run 启动程序。 func Run() error { - return Web(true).Run() + return app.Run() } // ShutDown 停止程序。 @@ -94,128 +76,3 @@ func Object(i interface{}) *BeanDefinition { func Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { return app.c.Accept(NewBean(ctor, args...)) } - -// HttpGet 参考 App.HttpGet 的解释。 -func HttpGet(path string, h http.HandlerFunc) *web.Mapper { - return app.HttpGet(path, h) -} - -// HandleGet 参考 App.HandleGet 的解释。 -func HandleGet(path string, h web.Handler) *web.Mapper { - return app.HandleGet(path, h) -} - -// GetMapping 参考 App.GetMapping 的解释。 -func GetMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.GetMapping(path, fn) -} - -// GetBinding 参考 App.GetBinding 的解释。 -func GetBinding(path string, fn interface{}) *web.Mapper { - return app.GetBinding(path, fn) -} - -// HttpPost 参考 App.HttpPost 的解释。 -func HttpPost(path string, h http.HandlerFunc) *web.Mapper { - return app.HttpPost(path, h) -} - -// HandlePost 参考 App.HandlePost 的解释。 -func HandlePost(path string, h web.Handler) *web.Mapper { - return app.HandlePost(path, h) -} - -// PostMapping 参考 App.PostMapping 的解释。 -func PostMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.PostMapping(path, fn) -} - -// PostBinding 参考 App.PostBinding 的解释。 -func PostBinding(path string, fn interface{}) *web.Mapper { - return app.PostBinding(path, fn) -} - -// HttpPut 参考 App.HttpPut 的解释。 -func HttpPut(path string, h http.HandlerFunc) *web.Mapper { - return app.HttpPut(path, h) -} - -// HandlePut 参考 App.HandlePut 的解释。 -func HandlePut(path string, h web.Handler) *web.Mapper { - return app.HandlePut(path, h) -} - -// PutMapping 参考 App.PutMapping 的解释。 -func PutMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.PutMapping(path, fn) -} - -// PutBinding 参考 App.PutBinding 的解释。 -func PutBinding(path string, fn interface{}) *web.Mapper { - return app.PutBinding(path, fn) -} - -// HttpDelete 参考 App.HttpDelete 的解释。 -func HttpDelete(path string, h http.HandlerFunc) *web.Mapper { - return app.HttpDelete(path, h) -} - -// HandleDelete 参考 App.HandleDelete 的解释。 -func HandleDelete(path string, h web.Handler) *web.Mapper { - return app.HandleDelete(path, h) -} - -// DeleteMapping 参考 App.DeleteMapping 的解释。 -func DeleteMapping(path string, fn web.HandlerFunc) *web.Mapper { - return app.DeleteMapping(path, fn) -} - -// DeleteBinding 参考 App.DeleteBinding 的解释。 -func DeleteBinding(path string, fn interface{}) *web.Mapper { - return app.DeleteBinding(path, fn) -} - -// HandleRequest 参考 App.HandleRequest 的解释。 -func HandleRequest(method uint32, path string, h web.Handler) *web.Mapper { - return app.HandleRequest(method, path, h) -} - -// RequestMapping 参考 App.RequestMapping 的解释。 -func RequestMapping(method uint32, path string, fn web.HandlerFunc) *web.Mapper { - return app.RequestMapping(method, path, fn) -} - -// RequestBinding 参考 App.RequestBinding 的解释。 -func RequestBinding(method uint32, path string, fn interface{}) *web.Mapper { - return app.RequestBinding(method, path, fn) -} - -// File 定义单个文件资源 -func File(path string, file string) *web.Mapper { - return app.File(path, file) -} - -// Static 定义一组文件资源 -func Static(prefix string, dir string) *web.Mapper { - return app.Static(prefix, dir) -} - -// StaticFS 定义一组文件资源 -func StaticFS(prefix string, fs http.FileSystem) *web.Mapper { - return app.StaticFS(prefix, fs) -} - -// Consume 参考 App.Consume 的解释。 -func Consume(fn interface{}, topics ...string) { - app.Consume(fn, topics...) -} - -// GrpcServer 参考 App.GrpcServer 的解释。 -func GrpcServer(serviceName string, server *grpc.Server) { - app.GrpcServer(serviceName, server) -} - -// GrpcClient 参考 App.GrpcClient 的解释。 -func GrpcClient(fn interface{}, endpoint string) *BeanDefinition { - return app.c.Accept(NewBean(fn, endpoint)) -} diff --git a/gs/boot_web.go b/gs/boot_web.go deleted file mode 100644 index 1b8f5682..00000000 --- a/gs/boot_web.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs - -import ( - "context" - "net/http" - "strings" - - "github.com/go-spring/spring-core/web" -) - -// WebStarter Web 服务器启动器 -type WebStarter struct { - Containers []web.Server `autowire:""` - Filters []web.Filter `autowire:"${web.server.filters:=*?}"` - Router web.Router `autowire:""` -} - -// OnAppStart 应用程序启动事件。 -func (starter *WebStarter) OnAppStart(ctx Context) { - for _, c := range starter.Containers { - c.AddFilter(starter.Filters...) - } - for _, m := range starter.Router.Mappers() { - for _, c := range starter.getContainers(m) { - c.AddMapper(m) - } - } - starter.startContainers(ctx) -} - -func (starter *WebStarter) getContainers(m *web.Mapper) []web.Server { - var ret []web.Server - for _, c := range starter.Containers { - if strings.HasPrefix(m.Path(), c.Config().Prefix) { - ret = append(ret, c) - } - } - return ret -} - -func (starter *WebStarter) startContainers(ctx Context) { - for i := range starter.Containers { - c := starter.Containers[i] - ctx.Go(func(_ context.Context) { - if err := c.Start(); err != nil && err != http.ErrServerClosed { - ShutDown(err.Error()) - } - }) - } -} - -// OnAppStop 应用程序结束事件。 -func (starter *WebStarter) OnAppStop(ctx context.Context) { - for _, c := range starter.Containers { - _ = c.Stop(ctx) - } -} diff --git a/gs/cond/cond.go b/gs/cond/cond.go index d19148bd..e560ca23 100755 --- a/gs/cond/cond.go +++ b/gs/cond/cond.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/cond/cond_test.go b/gs/cond/cond_test.go index b20fbed6..c1742d5a 100644 --- a/gs/cond/cond_test.go +++ b/gs/cond/cond_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/gs.go b/gs/gs.go index 80693f02..8e23c0e6 100755 --- a/gs/gs.go +++ b/gs/gs.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,6 @@ import ( "github.com/go-spring/spring-core/gs/arg" "github.com/go-spring/spring-core/gs/cond" "github.com/go-spring/spring-core/gs/internal" - "github.com/go-spring/spring-core/validate" ) type refreshState int @@ -826,8 +825,8 @@ func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar } if tag, ok = ft.Tag.Lookup("value"); ok { - validateTag, _ := ft.Tag.Lookup(validate.TagName()) - if err := subParam.BindTag(tag, validateTag); err != nil { + // validateTag, _ := ft.Tag.Lookup(validate.TagName()) + if err := subParam.BindTag(tag, ""); err != nil { return err } if ft.Anonymous { diff --git a/gs/gs_bean.go b/gs/gs_bean.go index 6d241188..5134f389 100755 --- a/gs/gs_bean.go +++ b/gs/gs_bean.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/gs_bean_test.go b/gs/gs_bean_test.go index b54bd13c..c235ecd9 100644 --- a/gs/gs_bean_test.go +++ b/gs/gs_bean_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { return gs.NewBean(objOrCtor, ctorArgs...) } -//func TestParseSingletonTag(t *testing.T) { +// func TestParseSingletonTag(t *testing.T) { // // data := map[string]SingletonTag{ // "?": {"", "", true}, @@ -54,9 +54,9 @@ func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { // tag := parseSingletonTag(k) // util.Equal(t, tag, v) // } -//} +// } // -//func TestParseBeanTag(t *testing.T) { +// func TestParseBeanTag(t *testing.T) { // // data := map[string]collectionTag{ // "?": {[]SingletonTag{}, true}, @@ -66,7 +66,7 @@ func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { // tag := ParseCollectionTag(k) // util.Equal(t, tag, v) // } -//} +// } func TestIsFuncBeanType(t *testing.T) { @@ -222,13 +222,13 @@ func TestConstructorBean(t *testing.T) { bd = newBean(NewPtrStudent) assert.Equal(t, bd.Type().String(), "*gs_test.Student") - //mapFn := func() map[int]string { return make(map[int]string) } - //bd = newBean(mapFn) - //assert.Equal(t, bd.Type().String(), "*map[int]string") + // mapFn := func() map[int]string { return make(map[int]string) } + // bd = newBean(mapFn) + // assert.Equal(t, bd.Type().String(), "*map[int]string") - //sliceFn := func() []int { return make([]int, 1) } - //bd = newBean(sliceFn) - //assert.Equal(t, bd.Type().String(), "*[]int") + // sliceFn := func() []int { return make([]int, 1) } + // bd = newBean(sliceFn) + // assert.Equal(t, bd.Type().String(), "*[]int") funcFn := func() func(int) { return nil } bd = newBean(funcFn) diff --git a/gs/gs_context.go b/gs/gs_context.go index 88217eff..42608709 100644 --- a/gs/gs_context.go +++ b/gs/gs_context.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/gs_dynamic_test.go b/gs/gs_dynamic_test.go index 1258614d..9f48fcad 100644 --- a/gs/gs_dynamic_test.go +++ b/gs/gs_dynamic_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/gs_test.go b/gs/gs_test.go index 1ab1297b..068a0b9f 100755 --- a/gs/gs_test.go +++ b/gs/gs_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { func TestApplicationContext(t *testing.T) { - ///////////////////////////////////////// + // /////////////////////////////////////// // 自定义数据类型 t.Run("pkg1.SamePkg", func(t *testing.T) { @@ -531,7 +531,7 @@ func TestApplicationContext_Get(t *testing.T) { }) } -//func TestApplicationContext_FindByName(t *testing.T) { +// func TestApplicationContext_FindByName(t *testing.T) { // // c := runTest(c,func(p gs.Context) {}) // c.Object(&BeanZero{5}) @@ -573,7 +573,7 @@ func TestApplicationContext_Get(t *testing.T) { // // b, _ = p.Find((*Grouper)(nil)) // assert.Equal(t, len(b), 0) -//} +// } type Teacher interface { Course() string @@ -2317,7 +2317,7 @@ func TestDefaultSpringContext(t *testing.T) { } // TODO 现在的方式父 Bean 不存在子 Bean 创建的时候会报错 -//func TestDefaultSpringContext_ParentNotRegister(t *testing.T) { +// func TestDefaultSpringContext_ParentNotRegister(t *testing.T) { // // c := gs.New() // parent := c.Provide(NewServerInterface).On(cond.OnProperty("server.is.nil")) @@ -2332,7 +2332,7 @@ func TestDefaultSpringContext(t *testing.T) { // var c *Consumer // ok = p.Get(&c) // util.Equal(t, ok, false) -//} +// } func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { c := gs.New() @@ -2377,7 +2377,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { } } -//func TestFunctionCondition(t *testing.T) { +// func TestFunctionCondition(t *testing.T) { // c := gs.New() // // fn := func(c cond.Context) bool { return true } @@ -2387,9 +2387,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // fn = func(c cond.Context) bool { return false } // c2 := cond.OnMatches(fn) // assert.False(t, c2.Matches(c)) -//} +// } // -//func TestPropertyCondition(t *testing.T) { +// func TestPropertyCondition(t *testing.T) { // // c := gs.New() // c.Property("int", 3) @@ -2406,9 +2406,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c4 := cond.OnProperty("parent123") // assert.False(t, c4.Matches(c)) -//} +// } // -//func TestMissingPropertyCondition(t *testing.T) { +// func TestMissingPropertyCondition(t *testing.T) { // // c := gs.New() // c.Property("int", 3) @@ -2425,9 +2425,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c4 := cond.OnMissingProperty("parent123") // assert.True(t, c4.Matches(c)) -//} +// } // -//func TestPropertyValueCondition(t *testing.T) { +// func TestPropertyValueCondition(t *testing.T) { // // c := gs.New() // c.Property("str", "this is a str") @@ -2447,9 +2447,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c5 := cond.OnPropertyValue("str", "\"$\"==\"this is a str\"") // assert.True(t, c5.Matches(c)) -//} +// } // -//func TestBeanCondition(t *testing.T) { +// func TestBeanCondition(t *testing.T) { // // c := gs.New() // c.Object(&BeanZero{5}) @@ -2461,9 +2461,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c2 := cond.OnBean("Null") // assert.False(t, c2.Matches(c)) -//} +// } // -//func TestMissingBeanCondition(t *testing.T) { +// func TestMissingBeanCondition(t *testing.T) { // // c := gs.New() // c.Object(&BeanZero{5}) @@ -2475,13 +2475,13 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c2 := cond.OnMissingBean("Null") // assert.True(t, c2.Matches(c)) -//} +// } // -//func TestExpressionCondition(t *testing.T) { +// func TestExpressionCondition(t *testing.T) { // -//} +// } // -//func TestConditional(t *testing.T) { +// func TestConditional(t *testing.T) { // // c := gs.New() // c.Property("bool", false) @@ -2532,9 +2532,9 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // OnPropertyValue("bool", false). // OnPropertyValue("bool", false) // assert.True(t, c9.Matches(c)) -//} +// } // -//func TestNotCondition(t *testing.T) { +// func TestNotCondition(t *testing.T) { // // c := gs.New() // c.Property(environ.SpringProfilesActive, "test") @@ -2555,7 +2555,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // And(). // On(cond.Not(profileCond)) // assert.False(t, c2.Matches(c)) -//} +// } func TestApplicationContext_Invoke(t *testing.T) { diff --git a/gs/gs_version.go b/gs/gs_version.go index 36555b72..3252a874 100644 --- a/gs/gs_version.go +++ b/gs/gs_version.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/internal/triple_sort.go b/gs/internal/triple_sort.go index 5a78dd5b..7cbb548c 100644 --- a/gs/internal/triple_sort.go +++ b/gs/internal/triple_sort.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/testdata/config/application-test.yaml b/gs/testdata/config/application-test.yaml index 9ec43093..b09c545e 100644 --- a/gs/testdata/config/application-test.yaml +++ b/gs/testdata/config/application-test.yaml @@ -1 +1 @@ -spring.application.name: test.yaml \ No newline at end of file +spring.application.name: test.yaml diff --git a/gs/testdata/config/application.properties b/gs/testdata/config/application.properties index 9bd50e83..4dc590df 100644 --- a/gs/testdata/config/application.properties +++ b/gs/testdata/config/application.properties @@ -1,11 +1 @@ -spring.application.name=test - -# 请使用 yaml 文件定义 -#properties.list[0]=1 -#properties.list[1]=2 - -# 请使用 yaml 文件定义 -#properties.obj.list[0].name=tom -#properties.obj.list[0].age=4 -#properties.obj.list[1].name=jerry -#properties.obj.list[1].age=2 \ No newline at end of file +spring.application.name=test \ No newline at end of file diff --git a/gs/testdata/config/application.yaml b/gs/testdata/config/application.yaml index c352bcb0..048a5e32 100644 --- a/gs/testdata/config/application.yaml +++ b/gs/testdata/config/application.yaml @@ -83,4 +83,4 @@ prefix_map: password: 123456 url: 1.1.1.1 port: 3306 - db: db2 \ No newline at end of file + db: db2 diff --git a/gs/testdata/config/extension.properties b/gs/testdata/config/extension.properties index 97b2e656..0330c028 100644 --- a/gs/testdata/config/extension.properties +++ b/gs/testdata/config/extension.properties @@ -1 +1 @@ -extension.app.name=extension_app \ No newline at end of file +extension.app.name=extension_app diff --git a/gs/testdata/config/logger.xml b/gs/testdata/config/logger.xml index 239886e3..12e80c0d 100644 --- a/gs/testdata/config/logger.xml +++ b/gs/testdata/config/logger.xml @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/gs/testdata/pkg/bar/pkg.go b/gs/testdata/pkg/bar/pkg.go index 0a06fc50..4136bfbe 100644 --- a/gs/testdata/pkg/bar/pkg.go +++ b/gs/testdata/pkg/bar/pkg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gs/testdata/pkg/foo/pkg.go b/gs/testdata/pkg/foo/pkg.go index 6155d4a1..ab2b9fa5 100644 --- a/gs/testdata/pkg/foo/pkg.go +++ b/gs/testdata/pkg/foo/pkg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/mongo/mongo.go b/mongo/mongo.go deleted file mode 100644 index 22db3b0c..00000000 --- a/mongo/mongo.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mongo - -// Config is the configuration of mongodb client. -type Config struct { - URL string `value:"${url:=mongodb://localhost}"` - Ping bool `value:"${ping:=true}"` -} diff --git a/mq/consumer.go b/mq/consumer.go deleted file mode 100644 index 0ce040d7..00000000 --- a/mq/consumer.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mq - -import ( - "context" - "encoding/json" - "errors" - "reflect" - - "github.com/go-spring/spring-base/util" -) - -// Consumer 消息消费者。 -type Consumer interface { - Topics() []string - Consume(ctx context.Context, msg Message) error -} - -// consumer Bind 方式的消息消费者。 -type consumer struct { - - // 消息主题列表。 - topics []string - - fn interface{} - t reflect.Type - v reflect.Value - e reflect.Type -} - -func (c *consumer) Topics() []string { - return c.topics -} - -func (c *consumer) Consume(ctx context.Context, msg Message) error { - e := reflect.New(c.e.Elem()) - err := json.Unmarshal(msg.Body(), e.Interface()) - if err != nil { - return err - } - out := c.v.Call([]reflect.Value{reflect.ValueOf(ctx), e}) - if err = out[0].Interface().(error); err != nil { - return err - } - return nil -} - -func validBindFn(t reflect.Type) bool { - return util.IsFuncType(t) && - util.ReturnOnlyError(t) && - t.NumIn() == 2 && - util.IsContextType(t.In(0)) && - util.IsStructPtr(t.In(1)) -} - -// Bind 创建 Bind 方式的消费者。 -func Bind(fn interface{}, topics ...string) *consumer { - if t := reflect.TypeOf(fn); validBindFn(t) { - return &consumer{ - topics: topics, - fn: fn, - t: t, - v: reflect.ValueOf(fn), - e: t.In(1), - } - } - panic(errors.New("fn should be func(ctx,*struct)error")) -} diff --git a/mq/message.go b/mq/message.go deleted file mode 100644 index 413d89d3..00000000 --- a/mq/message.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Package mq 提供了标准的消息队列接口,可以灵活适配各种 MQ 实现。 -package mq - -type Message interface { - Topic() string - ID() string - Body() []byte - Extra() map[string]string -} - -type message struct { - topic string // 消息主题 - id string // Key - body []byte // Value - extra map[string]string // 额外信息 -} - -// NewMessage 创建新的消息对象。 -func NewMessage() *message { - return &message{} -} - -// Topic 返回消息的主题。 -func (msg *message) Topic() string { - return msg.topic -} - -// WithTopic 设置消息的主题。 -func (msg *message) WithTopic(topic string) *message { - msg.topic = topic - return msg -} - -// ID 返回消息的序号。 -func (msg *message) ID() string { - return msg.id -} - -// WithID 设置消息的序号。 -func (msg *message) WithID(id string) *message { - msg.id = id - return msg -} - -// Body 返回消息的内容。 -func (msg *message) Body() []byte { - return msg.body -} - -// WithBody 设置消息的内容。 -func (msg *message) WithBody(body []byte) *message { - msg.body = body - return msg -} - -// Extra 返回消息的额外信息。 -func (msg *message) Extra() map[string]string { - return msg.extra -} - -// WithExtra 为消息添加额外的信息。 -func (msg *message) WithExtra(key, value string) *message { - if msg.extra == nil { - msg.extra = make(map[string]string) - } - msg.extra[key] = value - return msg -} diff --git a/mq/producer.go b/mq/producer.go deleted file mode 100644 index 9d6dc020..00000000 --- a/mq/producer.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package mq - -import "context" - -// Producer 消息生产者。 -type Producer interface { - - // SendMessage 传入的消息类型不同用途也不同,需要分别判断。 - SendMessage(ctx context.Context, msg Message) error -} diff --git a/redis/README.md b/redis/README.md deleted file mode 100644 index 775b02fe..00000000 --- a/redis/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# redis - -- [Installation](#installation) -- [Client](#client) -- [Commands](#commands) -- [redis.ErrNil](#redis.errnil) -- [Reply](#reply) -- [Record](#record) -- [Replay](#replay) - -## Installation diff --git a/redis/case_base.go b/redis/case_base.go deleted file mode 100644 index 35b37993..00000000 --- a/redis/case_base.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" -) - -type Case struct { - Func func(t *testing.T, ctx context.Context, c *Client) - Skip bool - Data string -} - -type Cases struct{} diff --git a/redis/case_base_test.go b/redis/case_base_test.go deleted file mode 100644 index 69390cb1..00000000 --- a/redis/case_base_test.go +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "bytes" - "context" - "errors" - "fmt" - "os/exec" - "strconv" - "testing" - "unicode/utf8" - - "github.com/go-spring/spring-base/cast" - "github.com/go-spring/spring-core/redis" -) - -func runCase(t *testing.T, c *redis.Case) { - ctx := context.Background() - client := redis.NewClient(&driver{}) - client.FlushAll(ctx) - c.Func(t, ctx, client) -} - -type driver struct{} - -func (p *driver) Exec(ctx context.Context, args []interface{}) (interface{}, error) { - str := encodeTTY(args) - c := exec.Command("/bin/bash", "-c", fmt.Sprintf("redis-cli --csv --quoted-input %s", str)) - output, err := c.CombinedOutput() - if err != nil { - fmt.Println(string(output[:len(output)-1])) - return nil, err - } - csv, err := decodeCSV(string(output[:len(output)-1])) - if err != nil { - return nil, err - } - if len(csv) == 1 { - if csv[0] == "NULL" { - return nil, redis.ErrNil() - } - } else if len(csv) > 1 { - if csv[0] == "ERROR" { - return nil, errors.New(csv[1]) - } - } - return &redis.Result{Data: csv}, nil -} - -func encodeTTY(data []interface{}) string { - var buf bytes.Buffer - for i, arg := range data { - switch s := arg.(type) { - case string: - if c := ttyQuoteCount(s); c > 0 { - buf.WriteByte('\'') - buf.WriteString(strconv.Quote(s)) - buf.WriteByte('\'') - } else { - buf.WriteString(s) - } - default: - buf.WriteString(cast.ToString(arg)) - } - if i < len(data)-1 { - buf.WriteByte(' ') - } - } - return buf.String() -} - -func ttyQuoteCount(s string) int { - if len(s) == 0 { - return 1 - } - ok := (s[0] >= 'a' && s[0] <= 'z') || (s[0] >= 'A' && s[0] <= 'Z') || s[0] == '_' - if !ok { - return 1 - } - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if b <= 0x20 || b >= 0x7E { - // ASCII printable characters (character code 32-127) - return 1 - } - i++ - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - return 2 - } - i += size - } - return 0 -} - -func decodeCSV(data string) ([]string, error) { - var ( - ret []string - buf bytes.Buffer - ) - for i := 0; ; { - if i >= len(data) { - return ret, nil - } - buf.Reset() - var ( - done bool - inQuote bool - inSingleQuote bool - ) - for ; !done; i++ { - if i >= len(data) && (inQuote || inSingleQuote) { - return nil, errors.New("invalid syntax") - } - if c := data[i]; inQuote { - if c == '\\' && i < len(data)-3 && data[i+1] == 'x' && cast.IsHexDigit(data[i+2]) && cast.IsHexDigit(data[i+3]) { - b1 := cast.HexDigitToInt(data[i+2]) * 16 - b2 := cast.HexDigitToInt(data[i+3]) - b := byte(b1 + b2) - buf.WriteByte(b) - i += 3 - } else if c == '\\' && i < len(data)-1 { - i++ - switch c = data[i]; c { - case 'n': - c = '\n' - case 'r': - c = '\r' - case 't': - c = '\t' - case 'b': - c = '\b' - case 'a': - c = '\a' - } - buf.WriteByte(c) - } else if c == '"' { - done = true - } else { - buf.WriteByte(c) - } - } else if inSingleQuote { - if c == '\\' && i < len(data)-1 && data[i+1] == '\'' { - i++ - buf.WriteByte('\'') - } else if c == '\'' { - done = true - } else { - buf.WriteByte(c) - } - } else { - switch c { - case ',': - if inQuote { - return nil, errors.New("invalid syntax") - } - done = true - case '"': - inQuote = true - case '\'': - inSingleQuote = true - default: - buf.WriteByte(c) - } - if i == len(data)-1 { - done = true - } - } - } - if buf.Len() > 0 { - ret = append(ret, buf.String()) - } - } -} diff --git a/redis/case_bitmap.go b/redis/case_bitmap.go deleted file mode 100644 index 56d7d287..00000000 --- a/redis/case_bitmap.go +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) BitCount() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "foobar") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.BitCount(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(26)) - - r3, err := c.BitCount(ctx, "mykey", 0, 0) - assert.Nil(t, err) - assert.Equal(t, r3, int64(4)) - - r4, err := c.BitCount(ctx, "mykey", 1, 1) - assert.Nil(t, err) - assert.Equal(t, r4, int64(6)) - }, - Data: `{ - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [ - { - "Protocol": "REDIS", - "Request": "SET mykey foobar", - "Response": "\"OK\"" - }, - { - "Protocol": "REDIS", - "Request": "BITCOUNT mykey", - "Response": "\"26\"" - }, - { - "Protocol": "REDIS", - "Request": "BITCOUNT mykey 0 0", - "Response": "\"4\"" - }, - { - "Protocol": "REDIS", - "Request": "BITCOUNT mykey 1 1", - "Response": "\"6\"" - } - ] - }`, - } -} - -func (c *Cases) BitOpAnd() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "foobar") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Set(ctx, "key2", "abcdef") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.BitOpAnd(ctx, "dest", "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r3, int64(6)) - - r4, err := c.Get(ctx, "dest") - assert.Nil(t, err) - assert.Equal(t, r4, "`bc`ab") - }, - Data: `{ - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [ - { - "Protocol": "REDIS", - "Request": "SET key1 foobar", - "Response": "\"OK\"" - }, - { - "Protocol": "REDIS", - "Request": "SET key2 abcdef", - "Response": "\"OK\"" - }, - { - "Protocol": "REDIS", - "Request": "BITOP AND dest key1 key2", - "Response": "\"6\"" - }, - { - "Protocol": "REDIS", - "Request": "GET dest", - "Response": "\"` + "`bc`ab" + `\"" - } - ] - }`, - } -} - -func (c *Cases) BitPos() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "\xff\xf0\x00") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.BitPos(ctx, "mykey", 0) - assert.Nil(t, err) - assert.Equal(t, r2, int64(12)) - - r3, err := c.Set(ctx, "mykey", "\x00\xff\xf0") - assert.Nil(t, err) - assert.True(t, IsOK(r3)) - - r4, err := c.BitPos(ctx, "mykey", 1, 0) - assert.Nil(t, err) - assert.Equal(t, r4, int64(8)) - - r5, err := c.BitPos(ctx, "mykey", 1, 2) - assert.Nil(t, err) - assert.Equal(t, r5, int64(16)) - - r6, err := c.Set(ctx, "mykey", "\x00\x00\x00") - assert.Nil(t, err) - assert.True(t, IsOK(r6)) - - r7, err := c.BitPos(ctx, "mykey", 1) - assert.Nil(t, err) - assert.Equal(t, r7, int64(-1)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey \"\\xff\\xf0\\x00\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "BITPOS mykey 0", - "Response": "\"12\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey \"\\x00\\xff\\xf0\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "BITPOS mykey 1 0", - "Response": "\"8\"" - }, { - "Protocol": "REDIS", - "Request": "BITPOS mykey 1 2", - "Response": "\"16\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey \u0000\u0000\u0000", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "BITPOS mykey 1", - "Response": "\"-1\"" - }] - }`, - } -} - -func (c *Cases) GetBit() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SetBit(ctx, "mykey", 7, 1) - assert.Nil(t, err) - assert.Equal(t, r1, int64(0)) - - r2, err := c.GetBit(ctx, "mykey", 0) - assert.Nil(t, err) - assert.Equal(t, r2, int64(0)) - - r3, err := c.GetBit(ctx, "mykey", 7) - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.GetBit(ctx, "mykey", 100) - assert.Nil(t, err) - assert.Equal(t, r4, int64(0)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SETBIT mykey 7 1", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "GETBIT mykey 0", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "GETBIT mykey 7", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "GETBIT mykey 100", - "Response": "\"0\"" - }] - }`, - } -} - -func (c *Cases) SetBit() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SetBit(ctx, "mykey", 7, 1) - assert.Nil(t, err) - assert.Equal(t, r1, int64(0)) - - r2, err := c.SetBit(ctx, "mykey", 7, 0) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "\u0000") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SETBIT mykey 7 1", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SETBIT mykey 7 0", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"\\x00\"" - }] - }`, - } -} diff --git a/redis/case_bitmap_test.go b/redis/case_bitmap_test.go deleted file mode 100644 index 0134da32..00000000 --- a/redis/case_bitmap_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestBitCount(t *testing.T) { - runCase(t, new(redis.Cases).BitCount()) -} - -func TestBitOpAnd(t *testing.T) { - runCase(t, new(redis.Cases).BitOpAnd()) -} - -func TestBitPos(t *testing.T) { - runCase(t, new(redis.Cases).BitPos()) -} - -func TestGetBit(t *testing.T) { - runCase(t, new(redis.Cases).GetBit()) -} - -func TestSetBit(t *testing.T) { - runCase(t, new(redis.Cases).SetBit()) -} diff --git a/redis/case_hash.go b/redis/case_hash.go deleted file mode 100644 index 855be30b..00000000 --- a/redis/case_hash.go +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) HDel() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "foo") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HDel(ctx, "myhash", "field1") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HDel(ctx, "myhash", "field2") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 foo", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HDEL myhash field1", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HDEL myhash field2", - "Response": "\"0\"" - }] - }`, - } -} - -func (c *Cases) HExists() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "foo") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HExists(ctx, "myhash", "field1") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HExists(ctx, "myhash", "field2") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 foo", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HEXISTS myhash field1", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HEXISTS myhash field2", - "Response": "\"0\"" - }] - }`, - } -} - -func (c *Cases) HGet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "foo") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HGet(ctx, "myhash", "field1") - assert.Nil(t, err) - assert.Equal(t, r2, "foo") - - _, err = c.HGet(ctx, "myhash", "field2") - assert.True(t, IsErrNil(err)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 foo", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HGET myhash field1", - "Response": "\"foo\"" - }, { - "Protocol": "REDIS", - "Request": "HGET myhash field2", - "Response": "NULL" - }] - }`, - } -} - -func (c *Cases) HGetAll() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSet(ctx, "myhash", "field2", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HGetAll(ctx, "myhash") - assert.Nil(t, err) - assert.Equal(t, r3, map[string]string{ - "field1": "Hello", - "field2": "World", - }) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSET myhash field2 World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HGETALL myhash", - "Response": "\"field1\",\"Hello\",\"field2\",\"World\"" - }] - }`, - } -} - -func (c *Cases) HIncrBy() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field", 5) - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HIncrBy(ctx, "myhash", "field", 1) - assert.Nil(t, err) - assert.Equal(t, r2, int64(6)) - - r3, err := c.HIncrBy(ctx, "myhash", "field", -1) - assert.Nil(t, err) - assert.Equal(t, r3, int64(5)) - - r4, err := c.HIncrBy(ctx, "myhash", "field", -10) - assert.Nil(t, err) - assert.Equal(t, r4, int64(-5)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field 5", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBY myhash field 1", - "Response": "\"6\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBY myhash field -1", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBY myhash field -10", - "Response": "\"-5\"" - }] - }`, - } -} - -func (c *Cases) HIncrByFloat() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "mykey", "field", 10.50) - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HIncrByFloat(ctx, "mykey", "field", 0.1) - assert.Nil(t, err) - assert.Equal(t, r2, 10.6) - - r3, err := c.HIncrByFloat(ctx, "mykey", "field", -5) - assert.Nil(t, err) - assert.Equal(t, r3, 5.6) - - r4, err := c.HSet(ctx, "mykey", "field", 5.0e3) - assert.Nil(t, err) - assert.Equal(t, r4, int64(0)) - - r5, err := c.HIncrByFloat(ctx, "mykey", "field", 2.0e2) - assert.Nil(t, err) - assert.Equal(t, r5, float64(5200)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET mykey field 10.5", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBYFLOAT mykey field 0.1", - "Response": "\"10.6\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBYFLOAT mykey field -5", - "Response": "\"5.6\"" - }, { - "Protocol": "REDIS", - "Request": "HSET mykey field 5000", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "HINCRBYFLOAT mykey field 200", - "Response": "\"5200\"" - }] - }`, - } -} - -func (c *Cases) HKeys() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSet(ctx, "myhash", "field2", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HKeys(ctx, "myhash") - assert.Nil(t, err) - assert.Equal(t, r3, []string{"field1", "field2"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSET myhash field2 World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HKEYS myhash", - "Response": "\"field1\",\"field2\"" - }] - }`, - } -} - -func (c *Cases) HLen() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSet(ctx, "myhash", "field2", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HLen(ctx, "myhash") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSET myhash field2 World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HLEN myhash", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) HMGet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSet(ctx, "myhash", "field2", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HMGet(ctx, "myhash", "field1", "field2", "nofield") - assert.Nil(t, err) - assert.Equal(t, r3, []interface{}{"Hello", "World", nil}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSET myhash field2 World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HMGET myhash field1 field2 nofield", - "Response": "\"Hello\",\"World\",NULL" - }] - }`, - } -} - -func (c *Cases) HSet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HGet(ctx, "myhash", "field1") - assert.Nil(t, err) - assert.Equal(t, r2, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HGET myhash field1", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) HSetNX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSetNX(ctx, "myhash", "field", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSetNX(ctx, "myhash", "field", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(0)) - - r3, err := c.HGet(ctx, "myhash", "field") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSETNX myhash field Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSETNX myhash field World", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "HGET myhash field", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) HStrLen() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "f1", "HelloWorld", "f2", 99, "f3", -256) - assert.Nil(t, err) - assert.Equal(t, r1, int64(3)) - - r2, err := c.HStrLen(ctx, "myhash", "f1") - assert.Nil(t, err) - assert.Equal(t, r2, int64(10)) - - r3, err := c.HStrLen(ctx, "myhash", "f2") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - - r4, err := c.HStrLen(ctx, "myhash", "f3") - assert.Nil(t, err) - assert.Equal(t, r4, int64(4)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash f1 HelloWorld f2 99 f3 -256", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "HSTRLEN myhash f1", - "Response": "\"10\"" - }, { - "Protocol": "REDIS", - "Request": "HSTRLEN myhash f2", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "HSTRLEN myhash f3", - "Response": "\"4\"" - }] - }`, - } -} - -func (c *Cases) HVals() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.HSet(ctx, "myhash", "field1", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.HSet(ctx, "myhash", "field2", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.HVals(ctx, "myhash") - assert.Nil(t, err) - assert.Equal(t, r3, []string{"Hello", "World"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "HSET myhash field1 Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HSET myhash field2 World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "HVALS myhash", - "Response": "\"Hello\",\"World\"" - }] - }`, - } -} diff --git a/redis/case_hash_test.go b/redis/case_hash_test.go deleted file mode 100644 index 889c81e6..00000000 --- a/redis/case_hash_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestHDel(t *testing.T) { - runCase(t, new(redis.Cases).HDel()) -} - -func TestHExists(t *testing.T) { - runCase(t, new(redis.Cases).HExists()) -} - -func TestHGet(t *testing.T) { - runCase(t, new(redis.Cases).HGet()) -} - -func TestHGetAll(t *testing.T) { - runCase(t, new(redis.Cases).HGetAll()) -} - -func TestHIncrBy(t *testing.T) { - runCase(t, new(redis.Cases).HIncrBy()) -} - -func TestHIncrByFloat(t *testing.T) { - runCase(t, new(redis.Cases).HIncrByFloat()) -} - -func TestHKeys(t *testing.T) { - runCase(t, new(redis.Cases).HKeys()) -} - -func TestHLen(t *testing.T) { - runCase(t, new(redis.Cases).HLen()) -} - -func TestHMGet(t *testing.T) { - runCase(t, new(redis.Cases).HMGet()) -} - -func TestHSet(t *testing.T) { - runCase(t, new(redis.Cases).HSet()) -} - -func TestHSetNX(t *testing.T) { - runCase(t, new(redis.Cases).HSetNX()) -} - -func TestHStrLen(t *testing.T) { - runCase(t, new(redis.Cases).HStrLen()) -} - -func TestHVals(t *testing.T) { - runCase(t, new(redis.Cases).HVals()) -} diff --git a/redis/case_key.go b/redis/case_key.go deleted file mode 100644 index dd59c91c..00000000 --- a/redis/case_key.go +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "sort" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) Del() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Set(ctx, "key2", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.Del(ctx, "key1", "key2", "key3") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "SET key2 World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "DEL key1 key2 key3", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) Dump() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", 10) - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Dump(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, "\u0000\xC0\n\t\u0000\xBEm\u0006\x89Z(\u0000\n") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "DUMP mykey", - "Response": "\"\\x00\\xc0\\n\\t\\x00\\xbem\\x06\\x89Z(\\x00\\n\"" - }] - }`, - } -} - -func (c *Cases) Exists() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Exists(ctx, "key1") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.Exists(ctx, "nosuchkey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - - r4, err := c.Set(ctx, "key2", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.Exists(ctx, "key1", "key2", "nosuchkey") - assert.Nil(t, err) - assert.Equal(t, r5, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXISTS key1", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "EXISTS nosuchkey", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SET key2 World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXISTS key1 key2 nosuchkey", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) Expire() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Expire(ctx, "mykey", 10) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(10)) - - r4, err := c.Set(ctx, "mykey", "Hello World") - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r5, int64(-1)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXPIRE mykey 10", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"10\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey \"Hello World\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"-1\"" - }] - }`, - } -} - -func (c *Cases) ExpireAt() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Exists(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ExpireAt(ctx, "mykey", 1293840000) - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.Exists(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r4, int64(0)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXISTS mykey", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "EXPIREAT mykey 1293840000", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "EXISTS mykey", - "Response": "\"0\"" - }] - }`, - } -} - -func (c *Cases) Keys() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.MSet(ctx, "firstname", "Jack", "lastname", "Stuntman", "age", 35) - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Keys(ctx, "*name*") - assert.Nil(t, err) - sort.Strings(r2) - assert.Equal(t, r2, []string{"firstname", "lastname"}) - - r3, err := c.Keys(ctx, "a??") - assert.Nil(t, err) - assert.Equal(t, r3, []string{"age"}) - - r4, err := c.Keys(ctx, "*") - assert.Nil(t, err) - sort.Strings(r4) - assert.Equal(t, r4, []string{"age", "firstname", "lastname"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "MSET firstname Jack lastname Stuntman age 35", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "KEYS *name*", - "Response": "\"lastname\",\"firstname\"" - }, { - "Protocol": "REDIS", - "Request": "KEYS a??", - "Response": "\"age\"" - }, { - "Protocol": "REDIS", - "Request": "KEYS *", - "Response": "\"age\",\"lastname\",\"firstname\"" - }] - }`, - } -} - -func (c *Cases) Persist() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Expire(ctx, "mykey", 10) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(10)) - - r4, err := c.Persist(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r5, int64(-1)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXPIRE mykey 10", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"10\"" - }, { - "Protocol": "REDIS", - "Request": "PERSIST mykey", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"-1\"" - }] - }`, - } -} - -func (c *Cases) PExpire() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.PExpire(ctx, "mykey", 1500) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.True(t, r3 >= 1 && r3 <= 2) - - r4, err := c.PTTL(ctx, "mykey") - assert.Nil(t, err) - assert.True(t, r4 >= 1400 && r4 <= 1500) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "PEXPIRE mykey 1500", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "PTTL mykey", - "Response": "\"1499\"" - }] - }`, - } -} - -func (c *Cases) PExpireAt() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.PExpireAt(ctx, "mykey", 1555555555005) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(-2)) - - r4, err := c.PTTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r4, int64(-2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "PEXPIREAT mykey 1555555555005", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"-2\"" - }, { - "Protocol": "REDIS", - "Request": "PTTL mykey", - "Response": "\"-2\"" - }] - }`, - } -} - -func (c *Cases) PTTL() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Expire(ctx, "mykey", 1) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.PTTL(ctx, "mykey") - assert.Nil(t, err) - assert.True(t, r3 >= 900 && r3 <= 1000) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXPIRE mykey 1", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "PTTL mykey", - "Response": "\"1000\"" - }] - }`, - } -} - -func (c *Cases) Rename() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Rename(ctx, "mykey", "myotherkey") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.Get(ctx, "myotherkey") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "RENAME mykey myotherkey", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GET myotherkey", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) RenameNX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Set(ctx, "myotherkey", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.RenameNX(ctx, "mykey", "myotherkey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - - r4, err := c.Get(ctx, "myotherkey") - assert.Nil(t, err) - assert.Equal(t, r4, "World") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "SET myotherkey World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "RENAMENX mykey myotherkey", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "GET myotherkey", - "Response": "\"World\"" - }] - }`, - } -} - -func (c *Cases) Touch() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Set(ctx, "key2", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.Touch(ctx, "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "SET key2 World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "TOUCH key1 key2", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) TTL() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Expire(ctx, "mykey", 10) - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, int64(10)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "EXPIRE mykey 10", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"10\"" - }] - }`, - } -} - -func (c *Cases) Type() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "value") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.LPush(ctx, "key2", "value") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key3", "value") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.Type(ctx, "key1") - assert.Nil(t, err) - assert.Equal(t, r4, "string") - - r5, err := c.Type(ctx, "key2") - assert.Nil(t, err) - assert.Equal(t, r5, "list") - - r6, err := c.Type(ctx, "key3") - assert.Nil(t, err) - assert.Equal(t, r6, "set") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 value", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSH key2 value", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key3 value", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "TYPE key1", - "Response": "\"string\"" - }, { - "Protocol": "REDIS", - "Request": "TYPE key2", - "Response": "\"list\"" - }, { - "Protocol": "REDIS", - "Request": "TYPE key3", - "Response": "\"set\"" - }] - }`, - } -} diff --git a/redis/case_key_test.go b/redis/case_key_test.go deleted file mode 100644 index 617a2fe5..00000000 --- a/redis/case_key_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestDel(t *testing.T) { - runCase(t, new(redis.Cases).Del()) -} - -func TestDump(t *testing.T) { - runCase(t, new(redis.Cases).Dump()) -} - -func TestExists(t *testing.T) { - runCase(t, new(redis.Cases).Exists()) -} - -func TestExpire(t *testing.T) { - runCase(t, new(redis.Cases).Expire()) -} - -func TestExpireAt(t *testing.T) { - runCase(t, new(redis.Cases).ExpireAt()) -} - -func TestKeys(t *testing.T) { - runCase(t, new(redis.Cases).Keys()) -} - -func TestPersist(t *testing.T) { - runCase(t, new(redis.Cases).Persist()) -} - -func TestPExpire(t *testing.T) { - runCase(t, new(redis.Cases).PExpire()) -} - -func TestPExpireAt(t *testing.T) { - runCase(t, new(redis.Cases).PExpireAt()) -} - -func TestPTTL(t *testing.T) { - runCase(t, new(redis.Cases).PTTL()) -} - -func TestRename(t *testing.T) { - runCase(t, new(redis.Cases).Rename()) -} - -func TestRenameNX(t *testing.T) { - runCase(t, new(redis.Cases).RenameNX()) -} - -func TestTouch(t *testing.T) { - runCase(t, new(redis.Cases).Touch()) -} - -func TestTTL(t *testing.T) { - runCase(t, new(redis.Cases).TTL()) -} - -func TestType(t *testing.T) { - runCase(t, new(redis.Cases).Type()) -} diff --git a/redis/case_list.go b/redis/case_list.go deleted file mode 100644 index 8150cce6..00000000 --- a/redis/case_list.go +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) LIndex() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.LPush(ctx, "mylist", "World") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.LPush(ctx, "mylist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LIndex(ctx, "mylist", 0) - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - - r4, err := c.LIndex(ctx, "mylist", -1) - assert.Nil(t, err) - assert.Equal(t, r4, "World") - - _, err = c.LIndex(ctx, "mylist", 3) - assert.True(t, IsErrNil(err)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "LPUSH mylist World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSH mylist Hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LINDEX mylist 0", - "Response": "\"Hello\"" - }, { - "Protocol": "REDIS", - "Request": "LINDEX mylist -1", - "Response": "\"World\"" - }, { - "Protocol": "REDIS", - "Request": "LINDEX mylist 3", - "Response": "NULL" - }] - }`, - } -} - -func (c *Cases) LInsert() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LInsertBefore(ctx, "mylist", "World", "There") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"Hello", "There", "World"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist World", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LINSERT mylist BEFORE World There", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"Hello\",\"There\",\"World\"" - }] - }`, - } -} - -func (c *Cases) LLen() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.LPush(ctx, "mylist", "World") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.LPush(ctx, "mylist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LLen(ctx, "mylist") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "LPUSH mylist World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSH mylist Hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LLEN mylist", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) LMove() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.LMove(ctx, "mylist", "myotherlist", "RIGHT", "LEFT") - assert.Nil(t, err) - assert.Equal(t, r4, "three") - - r5, err := c.LMove(ctx, "mylist", "myotherlist", "LEFT", "RIGHT") - assert.Nil(t, err) - assert.Equal(t, r5, "one") - - r6, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"two"}) - - r7, err := c.LRange(ctx, "myotherlist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r7, []string{"three", "one"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist two", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "LMOVE mylist myotherlist RIGHT LEFT", - "Response": "\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LMOVE mylist myotherlist LEFT RIGHT", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"two\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE myotherlist 0 -1", - "Response": "\"three\",\"one\"" - }] - }`, - } -} - -func (c *Cases) LPop() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one", "two", "three", "four", "five") - assert.Nil(t, err) - assert.Equal(t, r1, int64(5)) - - r2, err := c.LPop(ctx, "mylist") - assert.Nil(t, err) - assert.Equal(t, r2, "one") - - r3, err := c.LPopN(ctx, "mylist", 2) - assert.Nil(t, err) - assert.Equal(t, r3, []string{"two", "three"}) - - r4, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"four", "five"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one two three four five", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "LPOP mylist", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "LPOP mylist 2", - "Response": "\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"four\",\"five\"" - }] - }`, - } -} - -func (c *Cases) LPos() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", 'a', 'b', 'c', 'd', 1, 2, 3, 4, 3, 3, 3) - assert.Nil(t, err) - assert.Equal(t, r1, int64(11)) - - r2, err := c.LPos(ctx, "mylist", 3) - assert.Nil(t, err) - assert.Equal(t, r2, int64(6)) - - r3, err := c.LPosN(ctx, "mylist", "3", 0, "RANK", 2) - assert.Nil(t, err) - assert.Equal(t, r3, []int64{8, 9, 10}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist 97 98 99 100 1 2 3 4 3 3 3", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "LPOS mylist 3", - "Response": "\"6\"" - }, { - "Protocol": "REDIS", - "Request": "LPOS mylist 3 COUNT 0 RANK 2", - "Response": "\"8\",\"9\",\"10\"" - }] - }`, - } -} - -func (c *Cases) LPush() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.LPush(ctx, "mylist", "world") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.LPush(ctx, "mylist", "hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r3, []string{"hello", "world"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "LPUSH mylist world", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSH mylist hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"hello\",\"world\"" - }] - }`, - } -} - -func (c *Cases) LPushX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.LPush(ctx, "mylist", "World") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.LPushX(ctx, "mylist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LPushX(ctx, "myotherlist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - - r4, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"Hello", "World"}) - - r5, err := c.LRange(ctx, "myotherlist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []string{}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "LPUSH mylist World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSHX mylist Hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LPUSHX myotherlist Hello", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"Hello\",\"World\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE myotherlist 0 -1", - "Response": "" - }] - }`, - } -} - -func (c *Cases) LRange() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.LRange(ctx, "mylist", 0, 0) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"one"}) - - r5, err := c.LRange(ctx, "mylist", -3, 2) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"one", "two", "three"}) - - r6, err := c.LRange(ctx, "mylist", -100, 100) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"one", "two", "three"}) - - r7, err := c.LRange(ctx, "mylist", 5, 10) - assert.Nil(t, err) - assert.Equal(t, r7, []string{}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist two", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 0", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist -3 2", - "Response": "\"one\",\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist -100 100", - "Response": "\"one\",\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 5 10", - "Response": "" - }] - }`, - } -} - -func (c *Cases) LRem() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "foo") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.RPush(ctx, "mylist", "hello") - assert.Nil(t, err) - assert.Equal(t, r4, int64(4)) - - r5, err := c.LRem(ctx, "mylist", -2, "hello") - assert.Nil(t, err) - assert.Equal(t, r5, int64(2)) - - r6, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"hello", "foo"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist foo", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist hello", - "Response": "\"4\"" - }, { - "Protocol": "REDIS", - "Request": "LREM mylist -2 hello", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"hello\",\"foo\"" - }] - }`, - } -} - -func (c *Cases) LSet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.LSet(ctx, "mylist", 0, "four") - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.LSet(ctx, "mylist", -2, "five") - assert.Nil(t, err) - assert.True(t, IsOK(r5)) - - r6, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"four", "five", "three"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist two", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "LSET mylist 0 four", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "LSET mylist -2 five", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"four\",\"five\",\"three\"" - }] - }`, - } -} - -func (c *Cases) LTrim() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.LTrim(ctx, "mylist", 1, -1) - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"two", "three"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist two", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "LTRIM mylist 1 -1", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"two\",\"three\"" - }] - }`, - } -} - -func (c *Cases) RPop() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one", "two", "three", "four", "five") - assert.Nil(t, err) - assert.Equal(t, r1, int64(5)) - - r2, err := c.RPop(ctx, "mylist") - assert.Nil(t, err) - assert.Equal(t, r2, "five") - - r3, err := c.RPopN(ctx, "mylist", 2) - assert.Nil(t, err) - assert.Equal(t, r3, []string{"four", "three"}) - - r4, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"one", "two"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one two three four five", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "RPOP mylist", - "Response": "\"five\"" - }, { - "Protocol": "REDIS", - "Request": "RPOP mylist 2", - "Response": "\"four\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"one\",\"two\"" - }] - }`, - } -} - -func (c *Cases) RPopLPush() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPush(ctx, "mylist", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(3)) - - r4, err := c.RPopLPush(ctx, "mylist", "myotherlist") - assert.Nil(t, err) - assert.Equal(t, r4, "three") - - r5, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"one", "two"}) - - r6, err := c.LRange(ctx, "myotherlist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"three"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist two", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "RPOPLPUSH mylist myotherlist", - "Response": "\"three\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"one\",\"two\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE myotherlist 0 -1", - "Response": "\"three\"" - }] - }`, - } -} - -func (c *Cases) RPush() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPush(ctx, "mylist", "world") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r3, []string{"hello", "world"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSH mylist world", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"hello\",\"world\"" - }] - }`, - } -} - -func (c *Cases) RPushX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.RPush(ctx, "mylist", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.RPushX(ctx, "mylist", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.RPushX(ctx, "myotherlist", "World") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - - r4, err := c.LRange(ctx, "mylist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"Hello", "World"}) - - r5, err := c.LRange(ctx, "myotherlist", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []string{}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "RPUSH mylist Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSHX mylist World", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "RPUSHX myotherlist World", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE mylist 0 -1", - "Response": "\"Hello\",\"World\"" - }, { - "Protocol": "REDIS", - "Request": "LRANGE myotherlist 0 -1", - "Response": "" - }] - }`, - } -} diff --git a/redis/case_list_test.go b/redis/case_list_test.go deleted file mode 100644 index 8a67afb1..00000000 --- a/redis/case_list_test.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestLIndex(t *testing.T) { - runCase(t, new(redis.Cases).LIndex()) -} - -func TestLInsert(t *testing.T) { - runCase(t, new(redis.Cases).LInsert()) -} - -func TestLLen(t *testing.T) { - runCase(t, new(redis.Cases).LLen()) -} - -func TestLMove(t *testing.T) { - runCase(t, new(redis.Cases).LMove()) -} - -func TestLPop(t *testing.T) { - runCase(t, new(redis.Cases).LPop()) -} - -func TestLPos(t *testing.T) { - runCase(t, new(redis.Cases).LPos()) -} - -func TestLPush(t *testing.T) { - runCase(t, new(redis.Cases).LPush()) -} - -func TestLPushX(t *testing.T) { - runCase(t, new(redis.Cases).LPushX()) -} - -func TestLRange(t *testing.T) { - runCase(t, new(redis.Cases).LRange()) -} - -func TestLRem(t *testing.T) { - runCase(t, new(redis.Cases).LRem()) -} - -func TestLSet(t *testing.T) { - runCase(t, new(redis.Cases).LSet()) -} - -func TestLTrim(t *testing.T) { - runCase(t, new(redis.Cases).LTrim()) -} - -func TestRPop(t *testing.T) { - runCase(t, new(redis.Cases).RPop()) -} - -func TestRPopLPush(t *testing.T) { - runCase(t, new(redis.Cases).RPopLPush()) -} - -func TestRPush(t *testing.T) { - runCase(t, new(redis.Cases).RPush()) -} - -func TestRPushX(t *testing.T) { - runCase(t, new(redis.Cases).RPushX()) -} diff --git a/redis/case_set.go b/redis/case_set.go deleted file mode 100644 index 4762d7e8..00000000 --- a/redis/case_set.go +++ /dev/null @@ -1,860 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "sort" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) SAdd() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "myset", "World") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - - r4, err := c.SMembers(ctx, "myset") - assert.Nil(t, err) - sort.Strings(r4) - assert.Equal(t, r4, []string{"Hello", "World"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset World", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myset", - "Response": "\"Hello\",\"World\"" - }] - }`, - } -} - -func (c *Cases) SCard() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SCard(ctx, "myset") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SCARD myset", - "Response": "\"2\"" - }] - }`, - } -} -func (c *Cases) SDiff() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SDiff(ctx, "key1", "key2") - assert.Nil(t, err) - sort.Strings(r7) - assert.Equal(t, r7, []string{"a", "b"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SDIFF key1 key2", - "Response": "\"a\",\"b\"" - }] - }`, - } -} - -func (c *Cases) SDiffStore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SDiffStore(ctx, "key", "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r7, int64(2)) - - r8, err := c.SMembers(ctx, "key") - assert.Nil(t, err) - sort.Strings(r8) - assert.Equal(t, r8, []string{"a", "b"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SDIFFSTORE key key1 key2", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS key", - "Response": "\"a\",\"b\"" - }] - }`, - } -} - -func (c *Cases) SInter() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SInter(ctx, "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r7, []string{"c"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SINTER key1 key2", - "Response": "\"c\"" - }] - }`, - } -} - -func (c *Cases) SInterStore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SInterStore(ctx, "key", "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r7, int64(1)) - - r8, err := c.SMembers(ctx, "key") - assert.Nil(t, err) - assert.Equal(t, r8, []string{"c"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SINTERSTORE key key1 key2", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS key", - "Response": "\"c\"" - }] - }`, - } -} - -func (c *Cases) SIsMember() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SIsMember(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SIsMember(ctx, "myset", "two") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - }, - Data: "", - } -} - -func (c *Cases) SMembers() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SMembers(ctx, "myset") - assert.Nil(t, err) - sort.Strings(r3) - assert.Equal(t, r3, []string{"Hello", "World"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset World", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myset", - "Response": "\"Hello\",\"World\"" - }] - }`, - } -} - -func (c *Cases) SMIsMember() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r2, int64(0)) - - r3, err := c.SMIsMember(ctx, "myset", "one", "notamember") - assert.Nil(t, err) - assert.Equal(t, r3, []int64{int64(1), int64(0)}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset one", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SMISMEMBER myset one notamember", - "Response": "\"1\",\"0\"" - }] - }`, - } -} - -func (c *Cases) SMove() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "myotherset", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SMove(ctx, "myset", "myotherset", "two") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SMembers(ctx, "myset") - assert.Nil(t, err) - assert.Equal(t, r5, []string{"one"}) - - r6, err := c.SMembers(ctx, "myotherset") - assert.Nil(t, err) - sort.Strings(r6) - assert.Equal(t, r6, []string{"three", "two"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myotherset three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SMOVE myset myotherset two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myset", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myotherset", - "Response": "\"two\",\"three\"" - }] - }`, - } -} - -func (c *Cases) SPop() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "myset", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SPop(ctx, "myset") - assert.Nil(t, err) - - r5, err := c.SMembers(ctx, "myset") - assert.Nil(t, err) - - r6 := append([]string{r4}, r5...) - sort.Strings(r6) - assert.Equal(t, r6, []string{"one", "three", "two"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SPOP myset", - "Response": "\"two\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myset", - "Response": "\"three\",\"one\"" - }] - }`, - } -} - -func (c *Cases) SRandMember() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one", "two", "three") - assert.Nil(t, err) - assert.Equal(t, r1, int64(3)) - - _, err = c.SRandMember(ctx, "myset") - assert.Nil(t, err) - - r3, err := c.SRandMemberN(ctx, "myset", 2) - assert.Nil(t, err) - assert.Equal(t, len(r3), 2) - - r4, err := c.SRandMemberN(ctx, "myset", -5) - assert.Nil(t, err) - assert.Equal(t, len(r4), 5) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset one two three", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "SRANDMEMBER myset", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "SRANDMEMBER myset 2", - "Response": "\"one\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "SRANDMEMBER myset -5", - "Response": "\"one\",\"one\",\"one\",\"two\",\"one\"" - }] - }`, - } -} - -func (c *Cases) SRem() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "myset", "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "myset", "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SRem(ctx, "myset", "one") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SRem(ctx, "myset", "four") - assert.Nil(t, err) - assert.Equal(t, r5, int64(0)) - - r6, err := c.SMembers(ctx, "myset") - assert.Nil(t, err) - sort.Strings(r6) - assert.Equal(t, r6, []string{"three", "two"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD myset one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD myset three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SREM myset one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SREM myset four", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS myset", - "Response": "\"three\",\"two\"" - }] - }`, - } -} - -func (c *Cases) SUnion() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SUnion(ctx, "key1", "key2") - assert.Nil(t, err) - sort.Strings(r7) - assert.Equal(t, r7, []string{"a", "b", "c", "d", "e"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SUNION key1 key2", - "Response": "\"a\",\"b\",\"c\",\"d\",\"e\"" - }] - }`, - } -} - -func (c *Cases) SUnionStore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SAdd(ctx, "key1", "a") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SAdd(ctx, "key1", "b") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.SAdd(ctx, "key1", "c") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.SAdd(ctx, "key2", "c") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.SAdd(ctx, "key2", "d") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.SAdd(ctx, "key2", "e") - assert.Nil(t, err) - assert.Equal(t, r6, int64(1)) - - r7, err := c.SUnionStore(ctx, "key", "key1", "key2") - assert.Nil(t, err) - assert.Equal(t, r7, int64(5)) - - r8, err := c.SMembers(ctx, "key") - assert.Nil(t, err) - sort.Strings(r8) - assert.Equal(t, r8, []string{"a", "b", "c", "d", "e"}) - }, - Skip: true, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SADD key1 a", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 b", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key1 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 c", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 d", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SADD key2 e", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SUNIONSTORE key key1 key2", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "SMEMBERS key", - "Response": "\"a\",\"b\",\"c\",\"d\",\"e\"" - }] - }`, - } -} diff --git a/redis/case_set_test.go b/redis/case_set_test.go deleted file mode 100644 index 04b4ac96..00000000 --- a/redis/case_set_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestSAdd(t *testing.T) { - runCase(t, new(redis.Cases).SAdd()) -} - -func TestSCard(t *testing.T) { - runCase(t, new(redis.Cases).SCard()) -} - -func TestSDiff(t *testing.T) { - runCase(t, new(redis.Cases).SDiff()) -} - -func TestSDiffStore(t *testing.T) { - runCase(t, new(redis.Cases).SDiffStore()) -} - -func TestSInter(t *testing.T) { - runCase(t, new(redis.Cases).SInter()) -} - -func TestSInterStore(t *testing.T) { - runCase(t, new(redis.Cases).SInterStore()) -} - -func TestSIsMember(t *testing.T) { - runCase(t, new(redis.Cases).SIsMember()) -} - -func TestSMembers(t *testing.T) { - runCase(t, new(redis.Cases).SMembers()) -} - -func TestSMIsMember(t *testing.T) { - runCase(t, new(redis.Cases).SMIsMember()) -} - -func TestSMove(t *testing.T) { - runCase(t, new(redis.Cases).SMove()) -} - -func TestSPop(t *testing.T) { - runCase(t, new(redis.Cases).SPop()) -} - -func TestSRandMember(t *testing.T) { - runCase(t, new(redis.Cases).SRandMember()) -} - -func TestSRem(t *testing.T) { - runCase(t, new(redis.Cases).SRem()) -} - -func TestSUnion(t *testing.T) { - runCase(t, new(redis.Cases).SUnion()) -} - -func TestSUnionStore(t *testing.T) { - runCase(t, new(redis.Cases).SUnionStore()) -} diff --git a/redis/case_string.go b/redis/case_string.go deleted file mode 100644 index 594af443..00000000 --- a/redis/case_string.go +++ /dev/null @@ -1,777 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) Append() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Exists(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r1, int64(0)) - - r2, err := c.Append(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.Equal(t, r2, int64(5)) - - r3, err := c.Append(ctx, "mykey", " World") - assert.Nil(t, err) - assert.Equal(t, r3, int64(11)) - - r4, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r4, "Hello World") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "EXISTS mykey", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "APPEND mykey Hello", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "APPEND mykey \" World\"", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello World\"" - }] - }`, - } -} - -func (c *Cases) Decr() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "10") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Decr(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(9)) - - r3, err := c.Set(ctx, "mykey", "234293482390480948029348230948") - assert.Nil(t, err) - assert.True(t, IsOK(r3)) - - _, err = c.Decr(ctx, "mykey") - assert.Error(t, err, "ERR value is not an integer or out of range") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "DECR mykey", - "Response": "\"9\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey 234293482390480948029348230948", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "DECR mykey", - "Response": "(err) ERR value is not an integer or out of range" - }] - }`, - } -} - -func (c *Cases) DecrBy() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "10") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.DecrBy(ctx, "mykey", 3) - assert.Nil(t, err) - assert.Equal(t, r2, int64(7)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "DECRBY mykey 3", - "Response": "\"7\"" - }] - }`, - } -} - -func (c *Cases) Get() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - _, err := c.Get(ctx, "nonexisting") - assert.True(t, IsErrNil(err)) - - r2, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "GET nonexisting", - "Response": "NULL" - }, { - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) GetDel() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.GetDel(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, "Hello") - - _, err = c.Get(ctx, "mykey") - assert.True(t, IsErrNil(err)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GETDEL mykey", - "Response": "\"Hello\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "NULL" - }] - }`, - } -} - -func (c *Cases) GetRange() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "This is a string") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.GetRange(ctx, "mykey", 0, 3) - assert.Nil(t, err) - assert.Equal(t, r2, "This") - - r3, err := c.GetRange(ctx, "mykey", -3, -1) - assert.Nil(t, err) - assert.Equal(t, r3, "ing") - - r4, err := c.GetRange(ctx, "mykey", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, "This is a string") - - r5, err := c.GetRange(ctx, "mykey", 10, 100) - assert.Nil(t, err) - assert.Equal(t, r5, "string") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey \"This is a string\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GETRANGE mykey 0 3", - "Response": "\"This\"" - }, { - "Protocol": "REDIS", - "Request": "GETRANGE mykey -3 -1", - "Response": "\"ing\"" - }, { - "Protocol": "REDIS", - "Request": "GETRANGE mykey 0 -1", - "Response": "\"This is a string\"" - }, { - "Protocol": "REDIS", - "Request": "GETRANGE mykey 10 100", - "Response": "\"string\"" - }] - }`, - } -} - -func (c *Cases) GetSet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Incr(ctx, "mycounter") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.GetSet(ctx, "mycounter", "0") - assert.Nil(t, err) - assert.Equal(t, r2, "1") - - r3, err := c.Get(ctx, "mycounter") - assert.Nil(t, err) - assert.Equal(t, r3, "0") - - r4, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.GetSet(ctx, "mykey", "World") - assert.Nil(t, err) - assert.Equal(t, r5, "Hello") - - r6, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r6, "World") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "INCR mycounter", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "GETSET mycounter 0", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "GET mycounter", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GETSET mykey World", - "Response": "\"Hello\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"World\"" - }] - }`, - } -} - -func (c *Cases) Incr() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "10") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Incr(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(11)) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "11") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "INCR mykey", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"11\"" - }] - }`, - } -} - -func (c *Cases) IncrBy() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "10") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.IncrBy(ctx, "mykey", 5) - assert.Nil(t, err) - assert.Equal(t, r2, int64(15)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "INCRBY mykey 5", - "Response": "\"15\"" - }] - }`, - } -} - -func (c *Cases) IncrByFloat() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", 10.50) - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.IncrByFloat(ctx, "mykey", 0.1) - assert.Nil(t, err) - assert.Equal(t, r2, 10.6) - - r3, err := c.IncrByFloat(ctx, "mykey", -5) - assert.Nil(t, err) - assert.Equal(t, r3, 5.6) - - r4, err := c.Set(ctx, "mykey", 5.0e3) - assert.Nil(t, err) - assert.True(t, IsOK(r4)) - - r5, err := c.IncrByFloat(ctx, "mykey", 2.0e2) - assert.Nil(t, err) - assert.Equal(t, r5, float64(5200)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey 10.5", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "INCRBYFLOAT mykey 0.1", - "Response": "\"10.6\"" - }, { - "Protocol": "REDIS", - "Request": "INCRBYFLOAT mykey -5", - "Response": "\"5.6\"" - }, { - "Protocol": "REDIS", - "Request": "SET mykey 5000", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "INCRBYFLOAT mykey 200", - "Response": "\"5200\"" - }] - }`, - } -} - -func (c *Cases) MGet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Set(ctx, "key2", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r2)) - - r3, err := c.MGet(ctx, "key1", "key2", "nonexisting") - assert.Nil(t, err) - assert.Equal(t, r3, []interface{}{"Hello", "World", nil}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "SET key2 World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "MGET key1 key2 nonexisting", - "Response": "\"Hello\",\"World\",NULL" - }] - }`, - } -} - -func (c *Cases) MSet() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.MSet(ctx, "key1", "Hello", "key2", "World") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Get(ctx, "key1") - assert.Nil(t, err) - assert.Equal(t, r2, "Hello") - - r3, err := c.Get(ctx, "key2") - assert.Nil(t, err) - assert.Equal(t, r3, "World") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "MSET key1 Hello key2 World", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GET key1", - "Response": "\"Hello\"" - }, { - "Protocol": "REDIS", - "Request": "GET key2", - "Response": "\"World\"" - }] - }`, - } -} - -func (c *Cases) MSetNX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.MSetNX(ctx, "key1", "Hello", "key2", "there") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.MSetNX(ctx, "key2", "new", "key3", "world") - assert.Nil(t, err) - assert.Equal(t, r2, int64(0)) - - r3, err := c.MGet(ctx, "key1", "key2", "key3") - assert.Nil(t, err) - assert.Equal(t, r3, []interface{}{"Hello", "there", nil}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "MSETNX key1 Hello key2 there", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "MSETNX key2 new key3 world", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "MGET key1 key2 key3", - "Response": "\"Hello\",\"there\",NULL" - }] - }`, - } -} - -func (c *Cases) PSetEX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.PSetEX(ctx, "mykey", "Hello", 1000) - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.PTTL(ctx, "mykey") - assert.Nil(t, err) - assert.True(t, r2 <= 1000 && r2 >= 900) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "PSETEX mykey 1000 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "PTTL mykey", - "Response": "\"1000\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) Set() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, "Hello") - - r3, err := c.SetEX(ctx, "anotherkey", "will expire in a minute", 60) - assert.Nil(t, err) - assert.True(t, IsOK(r3)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello\"" - }, { - "Protocol": "REDIS", - "Request": "SETEX anotherkey 60 \"will expire in a minute\"", - "Response": "\"OK\"" - }] - }`, - } -} - -func (c *Cases) SetEX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SetEX(ctx, "mykey", "Hello", 10) - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.TTL(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(10)) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SETEX mykey 10 Hello", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "TTL mykey", - "Response": "\"10\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) SetNX() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.SetNX(ctx, "mykey", "Hello") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.SetNX(ctx, "mykey", "World") - assert.Nil(t, err) - assert.Equal(t, r2, int64(0)) - - r3, err := c.Get(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SETNX mykey Hello", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "SETNX mykey World", - "Response": "\"0\"" - }, { - "Protocol": "REDIS", - "Request": "GET mykey", - "Response": "\"Hello\"" - }] - }`, - } -} - -func (c *Cases) SetRange() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "key1", "Hello World") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.SetRange(ctx, "key1", 6, "Redis") - assert.Nil(t, err) - assert.Equal(t, r2, int64(11)) - - r3, err := c.Get(ctx, "key1") - assert.Nil(t, err) - assert.Equal(t, r3, "Hello Redis") - - r4, err := c.SetRange(ctx, "key2", 6, "Redis") - assert.Nil(t, err) - assert.Equal(t, r4, int64(11)) - - r5, err := c.Get(ctx, "key2") - assert.Nil(t, err) - assert.Equal(t, r5, "\u0000\u0000\u0000\u0000\u0000\u0000Redis") - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET key1 \"Hello World\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "SETRANGE key1 6 Redis", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "GET key1", - "Response": "\"Hello Redis\"" - }, { - "Protocol": "REDIS", - "Request": "SETRANGE key2 6 Redis", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "GET key2", - "Response": "\"\\x00\\x00\\x00\\x00\\x00\\x00Redis\"" - }] - }`, - } -} - -func (c *Cases) StrLen() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.Set(ctx, "mykey", "Hello world") - assert.Nil(t, err) - assert.True(t, IsOK(r1)) - - r2, err := c.StrLen(ctx, "mykey") - assert.Nil(t, err) - assert.Equal(t, r2, int64(11)) - - r3, err := c.StrLen(ctx, "nonexisting") - assert.Nil(t, err) - assert.Equal(t, r3, int64(0)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "SET mykey \"Hello world\"", - "Response": "\"OK\"" - }, { - "Protocol": "REDIS", - "Request": "STRLEN mykey", - "Response": "\"11\"" - }, { - "Protocol": "REDIS", - "Request": "STRLEN nonexisting", - "Response": "\"0\"" - }] - }`, - } -} diff --git a/redis/case_string_test.go b/redis/case_string_test.go deleted file mode 100644 index 185157ef..00000000 --- a/redis/case_string_test.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestAppend(t *testing.T) { - runCase(t, new(redis.Cases).Append()) -} - -func TestDecr(t *testing.T) { - runCase(t, new(redis.Cases).Decr()) -} - -func TestDecrBy(t *testing.T) { - runCase(t, new(redis.Cases).DecrBy()) -} - -func TestGet(t *testing.T) { - runCase(t, new(redis.Cases).Get()) -} - -func TestGetDel(t *testing.T) { - runCase(t, new(redis.Cases).GetDel()) -} - -func TestGetRange(t *testing.T) { - runCase(t, new(redis.Cases).GetRange()) -} - -func TestGetSet(t *testing.T) { - runCase(t, new(redis.Cases).GetSet()) -} - -func TestIncr(t *testing.T) { - runCase(t, new(redis.Cases).Incr()) -} - -func TestIncrBy(t *testing.T) { - runCase(t, new(redis.Cases).IncrBy()) -} - -func TestIncrByFloat(t *testing.T) { - runCase(t, new(redis.Cases).IncrByFloat()) -} - -func TestMGet(t *testing.T) { - runCase(t, new(redis.Cases).MGet()) -} - -func TestMSet(t *testing.T) { - runCase(t, new(redis.Cases).MSet()) -} - -func TestMSetNX(t *testing.T) { - runCase(t, new(redis.Cases).MSetNX()) -} - -func TestPSetEX(t *testing.T) { - runCase(t, new(redis.Cases).PSetEX()) -} - -func TestSet(t *testing.T) { - runCase(t, new(redis.Cases).Set()) -} - -func TestSetEX(t *testing.T) { - runCase(t, new(redis.Cases).SetEX()) -} - -func TestSetNX(t *testing.T) { - runCase(t, new(redis.Cases).SetNX()) -} - -func TestSetRange(t *testing.T) { - runCase(t, new(redis.Cases).SetRange()) -} - -func TestStrLen(t *testing.T) { - runCase(t, new(redis.Cases).StrLen()) -} diff --git a/redis/case_zset.go b/redis/case_zset.go deleted file mode 100644 index c530cafe..00000000 --- a/redis/case_zset.go +++ /dev/null @@ -1,1375 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" -) - -func (c *Cases) ZAdd() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 1, "uno") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 2, "two", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - - r4, err := c.ZRangeWithScores(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []ZItem{{"one", 1}, {"uno", 1}, {"two", 2}, {"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 1 uno", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two 3 three", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1 WITHSCORES", - "Response": "\"one\",\"1\",\"uno\",\"1\",\"two\",\"2\",\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZCard() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZCard(ctx, "myzset") - assert.Nil(t, err) - assert.Equal(t, r3, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZCARD myzset", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) ZCount() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZCount(ctx, "myzset", "-inf", "+inf") - assert.Nil(t, err) - assert.Equal(t, r4, int64(3)) - - r5, err := c.ZCount(ctx, "myzset", "(1", "3") - assert.Nil(t, err) - assert.Equal(t, r5, int64(2)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZCOUNT myzset -inf +inf", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "ZCOUNT myzset (1 3", - "Response": "\"2\"" - }] - }`, - } -} - -func (c *Cases) ZDiff() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "zset1", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "zset1", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "zset1", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZAdd(ctx, "zset2", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZAdd(ctx, "zset2", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.ZDiff(ctx, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r6, []string{"three"}) - - r7, err := c.ZDiffWithScores(ctx, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r7, []ZItem{{"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD zset1 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset1 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset1 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZDIFF 2 zset1 zset2", - "Response": "\"three\"" - }, { - "Protocol": "REDIS", - "Request": "ZDIFF 2 zset1 zset2 WITHSCORES", - "Response": "\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZIncrBy() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZIncrBy(ctx, "myzset", 2, "one") - assert.Nil(t, err) - assert.Equal(t, r3, float64(3)) - - r4, err := c.ZRangeWithScores(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []ZItem{{"two", 2}, {"one", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZINCRBY myzset 2 one", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1 WITHSCORES", - "Response": "\"two\",\"2\",\"one\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZInter() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "zset1", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "zset1", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "zset2", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZAdd(ctx, "zset2", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZAdd(ctx, "zset2", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.ZInter(ctx, 2, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r6, []string{"one", "two"}) - - r7, err := c.ZInterWithScores(ctx, 2, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r7, []ZItem{{"one", 2}, {"two", 4}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD zset1 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset1 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZINTER 2 zset1 zset2", - "Response": "\"one\",\"two\"" - }, { - "Protocol": "REDIS", - "Request": "ZINTER 2 zset1 zset2 WITHSCORES", - "Response": "\"one\",\"2\",\"two\",\"4\"" - }] - }`, - } -} - -func (c *Cases) ZLexCount() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 0, "a", 0, "b", 0, "c", 0, "d", 0, "e") - assert.Nil(t, err) - assert.Equal(t, r1, int64(5)) - - r2, err := c.ZAdd(ctx, "myzset", 0, "f", 0, "g") - assert.Nil(t, err) - assert.Equal(t, r2, int64(2)) - - r3, err := c.ZLexCount(ctx, "myzset", "-", "+") - assert.Nil(t, err) - assert.Equal(t, r3, int64(7)) - - r4, err := c.ZLexCount(ctx, "myzset", "[b", "[f") - assert.Nil(t, err) - assert.Equal(t, r4, int64(5)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 0 a 0 b 0 c 0 d 0 e", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 0 f 0 g", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "ZLEXCOUNT myzset - +", - "Response": "\"7\"" - }, { - "Protocol": "REDIS", - "Request": "ZLEXCOUNT myzset [b [f", - "Response": "\"5\"" - }] - }`, - } -} - -func (c *Cases) ZMScore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZMScore(ctx, "myzset", "one", "two", "nofield") - assert.Nil(t, err) - assert.Equal(t, r3, []float64{1, 2, 0}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZMSCORE myzset one two nofield", - "Response": "\"1\",\"2\",NULL" - }] - }`, - } -} - -func (c *Cases) ZPopMax() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZPopMax(ctx, "myzset") - assert.Nil(t, err) - assert.Equal(t, r4, []ZItem{{"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZPOPMAX myzset", - "Response": "\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZPopMin() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZPopMin(ctx, "myzset") - assert.Nil(t, err) - assert.Equal(t, r4, []ZItem{{"one", 1}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZPOPMIN myzset", - "Response": "\"one\",\"1\"" - }] - }`, - } -} - -func (c *Cases) ZRandMember() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "dadi", 1, "uno", 2, "due", 3, "tre", 4, "quattro", 5, "cinque", 6, "sei") - assert.Nil(t, err) - assert.Equal(t, r1, int64(6)) - - r2, err := c.ZRandMember(ctx, "dadi") - assert.Nil(t, err) - assert.NotEqual(t, r2, "") - - r3, err := c.ZRandMember(ctx, "dadi") - assert.Nil(t, err) - assert.NotEqual(t, r3, "") - - r4, err := c.ZRandMemberWithScores(ctx, "dadi", -5) - assert.Nil(t, err) - assert.Equal(t, len(r4), 5) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD dadi 1 uno 2 due 3 tre 4 quattro 5 cinque 6 sei", - "Response": "\"6\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANDMEMBER dadi", - "Response": "\"sei\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANDMEMBER dadi", - "Response": "\"sei\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANDMEMBER dadi -5 WITHSCORES", - "Response": "\"uno\",\"1\",\"uno\",\"1\",\"cinque\",\"5\",\"sei\",\"6\",\"due\",\"2\"" - }] - }`, - } -} - -func (c *Cases) ZRange() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRange(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"one", "two", "three"}) - - r5, err := c.ZRange(ctx, "myzset", 2, 3) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"three"}) - - r6, err := c.ZRange(ctx, "myzset", -2, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"two", "three"}) - - r7, err := c.ZRangeWithScores(ctx, "myzset", 0, 1) - assert.Nil(t, err) - assert.Equal(t, r7, []ZItem{{"one", 1}, {"two", 2}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1", - "Response": "\"one\",\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 2 3", - "Response": "\"three\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset -2 -1", - "Response": "\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 1 WITHSCORES", - "Response": "\"one\",\"1\",\"two\",\"2\"" - }] - }`, - } -} - -func (c *Cases) ZRangeByLex() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 0, "a", 0, "b", 0, "c", 0, "d", 0, "e", 0, "f", 0, "g") - assert.Nil(t, err) - assert.Equal(t, r1, int64(7)) - - r2, err := c.ZRangeByLex(ctx, "myzset", "-", "[c") - assert.Nil(t, err) - assert.Equal(t, r2, []string{"a", "b", "c"}) - - r3, err := c.ZRangeByLex(ctx, "myzset", "-", "(c") - assert.Nil(t, err) - assert.Equal(t, r3, []string{"a", "b"}) - - r4, err := c.ZRangeByLex(ctx, "myzset", "[aaa", "(g") - assert.Nil(t, err) - assert.Equal(t, r4, []string{"b", "c", "d", "e", "f"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g", - "Response": "\"7\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYLEX myzset - [c", - "Response": "\"a\",\"b\",\"c\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYLEX myzset - (c", - "Response": "\"a\",\"b\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYLEX myzset [aaa (g", - "Response": "\"b\",\"c\",\"d\",\"e\",\"f\"" - }] - }`, - } -} - -func (c *Cases) ZRangeByScore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRangeByScore(ctx, "myzset", "-inf", "+inf") - assert.Nil(t, err) - assert.Equal(t, r4, []string{"one", "two", "three"}) - - r5, err := c.ZRangeByScore(ctx, "myzset", "1", "2") - assert.Nil(t, err) - assert.Equal(t, r5, []string{"one", "two"}) - - r6, err := c.ZRangeByScore(ctx, "myzset", "(1", "2") - assert.Nil(t, err) - assert.Equal(t, r6, []string{"two"}) - - r7, err := c.ZRangeByScore(ctx, "myzset", "(1", "(2") - assert.Nil(t, err) - assert.Equal(t, len(r7), 0) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYSCORE myzset -inf +inf", - "Response": "\"one\",\"two\",\"three\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYSCORE myzset 1 2", - "Response": "\"one\",\"two\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYSCORE myzset (1 2", - "Response": "\"two\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGEBYSCORE myzset (1 (2", - "Response": "" - }] - }`, - } -} - -func (c *Cases) ZRank() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRank(ctx, "myzset", "three") - assert.Nil(t, err) - assert.Equal(t, r4, int64(2)) - - _, err = c.ZRank(ctx, "myzset", "four") - assert.True(t, IsErrNil(err)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANK myzset three", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANK myzset four", - "Response": "NULL" - }] - }`, - } -} - -func (c *Cases) ZRem() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRem(ctx, "myzset", "two") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZRangeWithScores(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []ZItem{{"one", 1}, {"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREM myzset two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1 WITHSCORES", - "Response": "\"one\",\"1\",\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZRemRangeByLex() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 0, "aaaa", 0, "b", 0, "c", 0, "d", 0, "e") - assert.Nil(t, err) - assert.Equal(t, r1, int64(5)) - - r2, err := c.ZAdd(ctx, "myzset", 0, "foo", 0, "zap", 0, "zip", 0, "ALPHA", 0, "alpha") - assert.Nil(t, err) - assert.Equal(t, r2, int64(5)) - - r3, err := c.ZRange(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r3, []string{ - "ALPHA", "aaaa", "alpha", "b", "c", "d", "e", "foo", "zap", "zip", - }) - - r4, err := c.ZRemRangeByLex(ctx, "myzset", "[alpha", "[omega") - assert.Nil(t, err) - assert.Equal(t, r4, int64(6)) - - r5, err := c.ZRange(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"ALPHA", "aaaa", "zap", "zip"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 0 aaaa 0 b 0 c 0 d 0 e", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 0 foo 0 zap 0 zip 0 ALPHA 0 alpha", - "Response": "\"5\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1", - "Response": "\"ALPHA\",\"aaaa\",\"alpha\",\"b\",\"c\",\"d\",\"e\",\"foo\",\"zap\",\"zip\"" - }, { - "Protocol": "REDIS", - "Request": "ZREMRANGEBYLEX myzset [alpha [omega", - "Response": "\"6\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1", - "Response": "\"ALPHA\",\"aaaa\",\"zap\",\"zip\"" - }] - }`, - } -} - -func (c *Cases) ZRemRangeByRank() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRemRangeByRank(ctx, "myzset", 0, 1) - assert.Nil(t, err) - assert.Equal(t, r4, int64(2)) - - r5, err := c.ZRangeWithScores(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []ZItem{{"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREMRANGEBYRANK myzset 0 1", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1 WITHSCORES", - "Response": "\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZRemRangeByScore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRemRangeByScore(ctx, "myzset", "-inf", "(2") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZRangeWithScores(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r5, []ZItem{{"two", 2}, {"three", 3}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREMRANGEBYSCORE myzset -inf (2", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE myzset 0 -1 WITHSCORES", - "Response": "\"two\",\"2\",\"three\",\"3\"" - }] - }`, - } -} - -func (c *Cases) ZRevRange() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRevRange(ctx, "myzset", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r4, []string{"three", "two", "one"}) - - r5, err := c.ZRevRange(ctx, "myzset", 2, 3) - assert.Nil(t, err) - assert.Equal(t, r5, []string{"one"}) - - r6, err := c.ZRevRange(ctx, "myzset", -2, -1) - assert.Nil(t, err) - assert.Equal(t, r6, []string{"two", "one"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGE myzset 0 -1", - "Response": "\"three\",\"two\",\"one\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGE myzset 2 3", - "Response": "\"one\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGE myzset -2 -1", - "Response": "\"two\",\"one\"" - }] - }`, - } -} - -func (c *Cases) ZRevRangeByLex() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 0, "a", 0, "b", 0, "c", 0, "d", 0, "e", 0, "f", 0, "g") - assert.Nil(t, err) - assert.Equal(t, r1, int64(7)) - - r2, err := c.ZRevRangeByLex(ctx, "myzset", "[c", "-") - assert.Nil(t, err) - assert.Equal(t, r2, []string{"c", "b", "a"}) - - r3, err := c.ZRevRangeByLex(ctx, "myzset", "(c", "-") - assert.Nil(t, err) - assert.Equal(t, r3, []string{"b", "a"}) - - r4, err := c.ZRevRangeByLex(ctx, "myzset", "(g", "[aaa") - assert.Nil(t, err) - assert.Equal(t, r4, []string{"f", "e", "d", "c", "b"}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g", - "Response": "\"7\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYLEX myzset [c -", - "Response": "\"c\",\"b\",\"a\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYLEX myzset (c -", - "Response": "\"b\",\"a\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYLEX myzset (g [aaa", - "Response": "\"f\",\"e\",\"d\",\"c\",\"b\"" - }] - }`, - } -} - -func (c *Cases) ZRevRangeByScore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRevRangeByScore(ctx, "myzset", "+inf", "-inf") - assert.Nil(t, err) - assert.Equal(t, r4, []string{"three", "two", "one"}) - - r5, err := c.ZRevRangeByScore(ctx, "myzset", "2", "1") - assert.Nil(t, err) - assert.Equal(t, r5, []string{"two", "one"}) - - r6, err := c.ZRevRangeByScore(ctx, "myzset", "2", "(1") - assert.Nil(t, err) - assert.Equal(t, r6, []string{"two"}) - - r7, err := c.ZRevRangeByScore(ctx, "myzset", "(2", "(1") - assert.Nil(t, err) - assert.Equal(t, len(r7), 0) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYSCORE myzset +inf -inf", - "Response": "\"three\",\"two\",\"one\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYSCORE myzset 2 1", - "Response": "\"two\",\"one\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYSCORE myzset 2 (1", - "Response": "\"two\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANGEBYSCORE myzset (2 (1", - "Response": "" - }] - }`, - } -} - -func (c *Cases) ZRevRank() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "myzset", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "myzset", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZRevRank(ctx, "myzset", "one") - assert.Nil(t, err) - assert.Equal(t, r4, int64(2)) - - _, err = c.ZRevRank(ctx, "myzset", "four") - assert.True(t, IsErrNil(err)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD myzset 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANK myzset one", - "Response": "\"2\"" - }, { - "Protocol": "REDIS", - "Request": "ZREVRANK myzset four", - "Response": "NULL" - }] - }`, - } -} -func (c *Cases) ZScore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "myzset", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZScore(ctx, "myzset", "one") - assert.Nil(t, err) - assert.Equal(t, r2, float64(1)) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD myzset 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZSCORE myzset one", - "Response": "\"1\"" - }] - }`, - } -} - -func (c *Cases) ZUnion() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "zset1", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "zset1", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "zset2", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZAdd(ctx, "zset2", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZAdd(ctx, "zset2", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.ZUnion(ctx, 2, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r6, []string{"one", "three", "two"}) - - r7, err := c.ZUnionWithScores(ctx, 2, "zset1", "zset2") - assert.Nil(t, err) - assert.Equal(t, r7, []ZItem{{"one", 2}, {"three", 3}, {"two", 4}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD zset1 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset1 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZUNION 2 zset1 zset2", - "Response": "\"one\",\"three\",\"two\"" - }, { - "Protocol": "REDIS", - "Request": "ZUNION 2 zset1 zset2 WITHSCORES", - "Response": "\"one\",\"2\",\"three\",\"3\",\"two\",\"4\"" - }] - }`, - } -} - -func (c *Cases) ZUnionStore() *Case { - return &Case{ - Func: func(t *testing.T, ctx context.Context, c *Client) { - - r1, err := c.ZAdd(ctx, "zset1", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r1, int64(1)) - - r2, err := c.ZAdd(ctx, "zset1", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r2, int64(1)) - - r3, err := c.ZAdd(ctx, "zset2", 1, "one") - assert.Nil(t, err) - assert.Equal(t, r3, int64(1)) - - r4, err := c.ZAdd(ctx, "zset2", 2, "two") - assert.Nil(t, err) - assert.Equal(t, r4, int64(1)) - - r5, err := c.ZAdd(ctx, "zset2", 3, "three") - assert.Nil(t, err) - assert.Equal(t, r5, int64(1)) - - r6, err := c.ZUnionStore(ctx, "out", 2, "zset1", "zset2", "WEIGHTS", 2, 3) - assert.Nil(t, err) - assert.Equal(t, r6, int64(3)) - - r7, err := c.ZRangeWithScores(ctx, "out", 0, -1) - assert.Nil(t, err) - assert.Equal(t, r7, []ZItem{{"one", 5}, {"three", 9}, {"two", 10}}) - }, - Data: ` - { - "Session": "df3b64266ebe4e63a464e135000a07cd", - "Actions": [{ - "Protocol": "REDIS", - "Request": "ZADD zset1 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset1 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 1 one", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 2 two", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZADD zset2 3 three", - "Response": "\"1\"" - }, { - "Protocol": "REDIS", - "Request": "ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3", - "Response": "\"3\"" - }, { - "Protocol": "REDIS", - "Request": "ZRANGE out 0 -1 WITHSCORES", - "Response": "\"one\",\"5\",\"three\",\"9\",\"two\",\"10\"" - }] - }`, - } -} diff --git a/redis/case_zset_test.go b/redis/case_zset_test.go deleted file mode 100644 index 0815f7a1..00000000 --- a/redis/case_zset_test.go +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "testing" - - "github.com/go-spring/spring-core/redis" -) - -func TestZAdd(t *testing.T) { - runCase(t, new(redis.Cases).ZAdd()) -} - -func TestZCard(t *testing.T) { - runCase(t, new(redis.Cases).ZCard()) -} - -func TestZCount(t *testing.T) { - runCase(t, new(redis.Cases).ZCount()) -} - -func TestZDiff(t *testing.T) { - runCase(t, new(redis.Cases).ZDiff()) -} - -func TestZIncrBy(t *testing.T) { - runCase(t, new(redis.Cases).ZIncrBy()) -} - -func TestZInter(t *testing.T) { - runCase(t, new(redis.Cases).ZInter()) -} - -func TestZLexCount(t *testing.T) { - runCase(t, new(redis.Cases).ZLexCount()) -} - -func TestZMScore(t *testing.T) { - runCase(t, new(redis.Cases).ZMScore()) -} - -func TestZPopMax(t *testing.T) { - runCase(t, new(redis.Cases).ZPopMax()) -} - -func TestZPopMin(t *testing.T) { - runCase(t, new(redis.Cases).ZPopMin()) -} - -func TestZRandMember(t *testing.T) { - runCase(t, new(redis.Cases).ZRandMember()) -} - -func TestZRange(t *testing.T) { - runCase(t, new(redis.Cases).ZRange()) -} - -func TestZRangeByLex(t *testing.T) { - runCase(t, new(redis.Cases).ZRangeByLex()) -} - -func TestZRangeByScore(t *testing.T) { - runCase(t, new(redis.Cases).ZRangeByScore()) -} - -func TestZRank(t *testing.T) { - runCase(t, new(redis.Cases).ZRank()) -} - -func TestZRem(t *testing.T) { - runCase(t, new(redis.Cases).ZRem()) -} - -func TestZRemRangeByLex(t *testing.T) { - runCase(t, new(redis.Cases).ZRemRangeByLex()) -} - -func TestZRemRangeByRank(t *testing.T) { - runCase(t, new(redis.Cases).ZRemRangeByRank()) -} - -func TestZRemRangeByScore(t *testing.T) { - runCase(t, new(redis.Cases).ZRemRangeByScore()) -} - -func TestZRevRange(t *testing.T) { - runCase(t, new(redis.Cases).ZRevRange()) -} - -func TestZRevRangeByLex(t *testing.T) { - runCase(t, new(redis.Cases).ZRevRangeByLex()) -} - -func TestZRevRangeByScore(t *testing.T) { - runCase(t, new(redis.Cases).ZRevRangeByScore()) -} - -func TestZRevRank(t *testing.T) { - runCase(t, new(redis.Cases).ZRevRank()) -} - -func TestZScore(t *testing.T) { - runCase(t, new(redis.Cases).ZScore()) -} - -func TestZUnion(t *testing.T) { - runCase(t, new(redis.Cases).ZUnion()) -} - -func TestZUnionStore(t *testing.T) { - runCase(t, new(redis.Cases).ZUnionStore()) -} diff --git a/redis/ops_bitmap.go b/redis/ops_bitmap.go deleted file mode 100644 index 695665a1..00000000 --- a/redis/ops_bitmap.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// BitCount https://redis.io/commands/bitcount -// Command: BITCOUNT key [start end] -// Integer reply: The number of bits set to 1. -func (c *Client) BitCount(ctx context.Context, key string, args ...interface{}) (int64, error) { - args = append([]interface{}{"BITCOUNT", key}, args...) - return c.Int(ctx, args...) -} - -// BitOpAnd https://redis.io/commands/bitop -// Command: BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN -// Integer reply: The size of the string stored in the destination key, -// that is equal to the size of the longest input string. -func (c *Client) BitOpAnd(ctx context.Context, destKey string, keys ...string) (int64, error) { - args := []interface{}{"BITOP", "AND", destKey} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// BitOpOr https://redis.io/commands/bitop -// Command: BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN -// Integer reply: The size of the string stored in the destination key, -// that is equal to the size of the longest input string. -func (c *Client) BitOpOr(ctx context.Context, destKey string, keys ...string) (int64, error) { - args := []interface{}{"BITOP", "OR", destKey} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// BitOpXor https://redis.io/commands/bitop -// Command: BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN -// Integer reply: The size of the string stored in the destination key, -// that is equal to the size of the longest input string. -func (c *Client) BitOpXor(ctx context.Context, destKey string, keys ...string) (int64, error) { - args := []interface{}{"BITOP", "XOR", destKey} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// BitOpNot https://redis.io/commands/bitop -// Command: BITOP NOT destkey srckey -// Integer reply: The size of the string stored in the destination key, -// that is equal to the size of the longest input string. -func (c *Client) BitOpNot(ctx context.Context, destKey string, key string) (int64, error) { - args := []interface{}{"BITOP", "NOT", destKey, key} - return c.Int(ctx, args...) -} - -// BitPos https://redis.io/commands/bitpos -// Command: BITPOS key bit [start [end]] -// Integer reply: The command returns the position of the first bit -// set to 1 or 0 according to the request. -func (c *Client) BitPos(ctx context.Context, key string, bit int64, args ...interface{}) (int64, error) { - args = append([]interface{}{"BITPOS", key, bit}, args...) - return c.Int(ctx, args...) -} - -// GetBit https://redis.io/commands/getbit -// Command: GETBIT key offset -// Integer reply: the bit value stored at offset. -func (c *Client) GetBit(ctx context.Context, key string, offset int64) (int64, error) { - args := []interface{}{"GETBIT", key, offset} - return c.Int(ctx, args...) -} - -// SetBit https://redis.io/commands/setbit -// Command: SETBIT key offset value -// Integer reply: the original bit value stored at offset. -func (c *Client) SetBit(ctx context.Context, key string, offset int64, value int) (int64, error) { - args := []interface{}{"SETBIT", key, offset, value} - return c.Int(ctx, args...) -} diff --git a/redis/ops_hash.go b/redis/ops_hash.go deleted file mode 100644 index b15231be..00000000 --- a/redis/ops_hash.go +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// HDel https://redis.io/commands/hdel -// Command: HDEL key field [field ...] -// Integer reply: the number of fields that were removed -// from the hash, not including specified but non-existing fields. -func (c *Client) HDel(ctx context.Context, key string, fields ...string) (int64, error) { - args := []interface{}{"HDEL", key} - for _, field := range fields { - args = append(args, field) - } - return c.Int(ctx, args...) -} - -// HExists https://redis.io/commands/hexists -// Command: HEXISTS key field -// Integer reply: 1 if the hash contains field, -// 0 if the hash does not contain field, or key does not exist. -func (c *Client) HExists(ctx context.Context, key, field string) (int64, error) { - args := []interface{}{"HEXISTS", key, field} - return c.Int(ctx, args...) -} - -// HGet https://redis.io/commands/hget -// Command: HGET key field -// Bulk string reply: the value associated with field, -// or nil when field is not present in the hash or key does not exist. -func (c *Client) HGet(ctx context.Context, key string, field string) (string, error) { - args := []interface{}{"HGET", key, field} - return c.String(ctx, args...) -} - -// HGetAll https://redis.io/commands/hgetall -// Command: HGETALL key -// Array reply: list of fields and their values stored -// in the hash, or an empty list when key does not exist. -func (c *Client) HGetAll(ctx context.Context, key string) (map[string]string, error) { - args := []interface{}{"HGETALL", key} - return c.StringMap(ctx, args...) -} - -// HIncrBy https://redis.io/commands/hincrby -// Command: HINCRBY key field increment -// Integer reply: the value at field after the increment operation. -func (c *Client) HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error) { - args := []interface{}{"HINCRBY", key, field, incr} - return c.Int(ctx, args...) -} - -// HIncrByFloat https://redis.io/commands/hincrbyfloat -// Command: HINCRBYFLOAT key field increment -// Bulk string reply: the value of field after the increment. -func (c *Client) HIncrByFloat(ctx context.Context, key, field string, incr float64) (float64, error) { - args := []interface{}{"HINCRBYFLOAT", key, field, incr} - return c.Float(ctx, args...) -} - -// HKeys https://redis.io/commands/hkeys -// Command: HKEYS key -// Array reply: list of fields in the hash, or an empty list when key does not exist. -func (c *Client) HKeys(ctx context.Context, key string) ([]string, error) { - args := []interface{}{"HKEYS", key} - return c.StringSlice(ctx, args...) -} - -// HLen https://redis.io/commands/hlen -// Command: HLEN key -// Integer reply: number of fields in the hash, or 0 when key does not exist. -func (c *Client) HLen(ctx context.Context, key string) (int64, error) { - args := []interface{}{"HLEN", key} - return c.Int(ctx, args...) -} - -// HMGet https://redis.io/commands/hmget -// Command: HMGET key field [field ...] -// Array reply: list of values associated with the -// given fields, in the same order as they are requested. -func (c *Client) HMGet(ctx context.Context, key string, fields ...string) ([]interface{}, error) { - args := []interface{}{"HMGET", key} - for _, field := range fields { - args = append(args, field) - } - return c.Slice(ctx, args...) -} - -// HSet https://redis.io/commands/hset -// Command: HSET key field value [field value ...] -// Integer reply: The number of fields that were added. -func (c *Client) HSet(ctx context.Context, key string, args ...interface{}) (int64, error) { - args = append([]interface{}{"HSET", key}, args...) - return c.Int(ctx, args...) -} - -// HSetNX https://redis.io/commands/hsetnx -// Command: HSETNX key field value -// Integer reply: 1 if field is a new field in the hash and value was set, -// 0 if field already exists in the hash and no operation was performed. -func (c *Client) HSetNX(ctx context.Context, key, field string, value interface{}) (int64, error) { - args := []interface{}{"HSETNX", key, field, value} - return c.Int(ctx, args...) -} - -// HStrLen https://redis.io/commands/hstrlen -// Command: HSTRLEN key field -// Integer reply: the string length of the value associated with field, -// or zero when field is not present in the hash or key does not exist at all. -func (c *Client) HStrLen(ctx context.Context, key, field string) (int64, error) { - args := []interface{}{"HSTRLEN", key, field} - return c.Int(ctx, args...) -} - -// HVals https://redis.io/commands/hvals -// Command: HVALS key -// Array reply: list of values in the hash, or an empty list when key does not exist. -func (c *Client) HVals(ctx context.Context, key string) ([]string, error) { - args := []interface{}{"HVALS", key} - return c.StringSlice(ctx, args...) -} diff --git a/redis/ops_key.go b/redis/ops_key.go deleted file mode 100644 index 75c64b7d..00000000 --- a/redis/ops_key.go +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// Del https://redis.io/commands/del -// Command: DEL key [key ...] -// Integer reply: The number of keys that were removed. -func (c *Client) Del(ctx context.Context, keys ...string) (int64, error) { - args := []interface{}{"DEL"} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// Dump https://redis.io/commands/dump -// Command: DUMP key -// Bulk string reply: the serialized value. -// If key does not exist a nil bulk reply is returned. -func (c *Client) Dump(ctx context.Context, key string) (string, error) { - args := []interface{}{"DUMP", key} - return c.String(ctx, args...) -} - -// Exists https://redis.io/commands/exists -// Command: EXISTS key [key ...] -// Integer reply: The number of keys existing among the -// ones specified as arguments. Keys mentioned multiple -// times and existing are counted multiple times. -func (c *Client) Exists(ctx context.Context, keys ...string) (int64, error) { - args := []interface{}{"EXISTS"} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// Expire https://redis.io/commands/expire -// Command: EXPIRE key seconds [NX|XX|GT|LT] -// Integer reply: 1 if the timeout was set, 0 if the timeout was not set. -func (c *Client) Expire(ctx context.Context, key string, expire int64, args ...interface{}) (int64, error) { - args = append([]interface{}{"EXPIRE", key, expire}, args...) - return c.Int(ctx, args...) -} - -// ExpireAt https://redis.io/commands/expireat -// Command: EXPIREAT key timestamp [NX|XX|GT|LT] -// Integer reply: 1 if the timeout was set, 0 if the timeout was not set. -func (c *Client) ExpireAt(ctx context.Context, key string, expireAt int64, args ...interface{}) (int64, error) { - args = append([]interface{}{"EXPIREAT", key, expireAt}, args...) - return c.Int(ctx, args...) -} - -// Keys https://redis.io/commands/keys -// Command: KEYS pattern -// Array reply: list of keys matching pattern. -func (c *Client) Keys(ctx context.Context, pattern string) ([]string, error) { - args := []interface{}{"KEYS", pattern} - return c.StringSlice(ctx, args...) -} - -// Persist https://redis.io/commands/persist -// Command: PERSIST key -// Integer reply: 1 if the timeout was removed, -// 0 if key does not exist or does not have an associated timeout. -func (c *Client) Persist(ctx context.Context, key string) (int64, error) { - args := []interface{}{"PERSIST", key} - return c.Int(ctx, args...) -} - -// PExpire https://redis.io/commands/pexpire -// Command: PEXPIRE key milliseconds [NX|XX|GT|LT] -// Integer reply: 1 if the timeout was set, 0 if the timeout was not set. -func (c *Client) PExpire(ctx context.Context, key string, expire int64, args ...interface{}) (int64, error) { - args = append([]interface{}{"PEXPIRE", key, expire}, args...) - return c.Int(ctx, args...) -} - -// PExpireAt https://redis.io/commands/pexpireat -// Command: PEXPIREAT key milliseconds-timestamp [NX|XX|GT|LT] -// Integer reply: 1 if the timeout was set, 0 if the timeout was not set. -func (c *Client) PExpireAt(ctx context.Context, key string, expireAt int64, args ...interface{}) (int64, error) { - args = append([]interface{}{"PEXPIREAT", key, expireAt}, args...) - return c.Int(ctx, args...) -} - -// PTTL https://redis.io/commands/pttl -// Command: PTTL key -// Integer reply: TTL in milliseconds, -1 if the key exists -// but has no associated expire, -2 if the key does not exist. -func (c *Client) PTTL(ctx context.Context, key string) (int64, error) { - args := []interface{}{"PTTL", key} - return c.Int(ctx, args...) -} - -// RandomKey https://redis.io/commands/randomkey -// Command: RANDOMKEY -// Bulk string reply: the random key, or nil when the database is empty. -func (c *Client) RandomKey(ctx context.Context) (string, error) { - return c.String(ctx, "RANDOMKEY") -} - -// Rename https://redis.io/commands/rename -// Command: RENAME key newkey -// Simple string reply. -func (c *Client) Rename(ctx context.Context, key, newKey string) (string, error) { - args := []interface{}{"RENAME", key, newKey} - return c.String(ctx, args...) -} - -// RenameNX https://redis.io/commands/renamenx -// Command: RENAMENX key newkey -// Integer reply: 1 if key was renamed to newKey, 0 if newKey already exists. -func (c *Client) RenameNX(ctx context.Context, key, newKey string) (int64, error) { - args := []interface{}{"RENAMENX", key, newKey} - return c.Int(ctx, args...) -} - -// Touch https://redis.io/commands/touch -// Command: TOUCH key [key ...] -// Integer reply: The number of keys that were touched. -func (c *Client) Touch(ctx context.Context, keys ...string) (int64, error) { - args := []interface{}{"TOUCH"} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// TTL https://redis.io/commands/ttl -// Command: TTL key -// Integer reply: TTL in seconds, -1 if the key exists -// but has no associated expire, -2 if the key does not exist. -func (c *Client) TTL(ctx context.Context, key string) (int64, error) { - args := []interface{}{"TTL", key} - return c.Int(ctx, args...) -} - -// Type https://redis.io/commands/type -// Command: TYPE key -// Simple string reply: type of key, or none when key does not exist. -func (c *Client) Type(ctx context.Context, key string) (string, error) { - args := []interface{}{"TYPE", key} - return c.String(ctx, args...) -} diff --git a/redis/ops_list.go b/redis/ops_list.go deleted file mode 100644 index 9f2b07a6..00000000 --- a/redis/ops_list.go +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// LIndex https://redis.io/commands/lindex -// Command: LINDEX key index -// Bulk string reply: the requested element, or nil when index is out of range. -func (c *Client) LIndex(ctx context.Context, key string, index int64) (string, error) { - args := []interface{}{"LINDEX", key, index} - return c.String(ctx, args...) -} - -// LInsertBefore https://redis.io/commands/linsert -// Command: LINSERT key BEFORE|AFTER pivot element -// Integer reply: the length of the list after the -// insert operation, or -1 when the value pivot was not found. -func (c *Client) LInsertBefore(ctx context.Context, key string, pivot, value interface{}) (int64, error) { - args := []interface{}{"LINSERT", key, "BEFORE", pivot, value} - return c.Int(ctx, args...) -} - -// LInsertAfter https://redis.io/commands/linsert -// Command: LINSERT key BEFORE|AFTER pivot element -// Integer reply: the length of the list after the -// insert operation, or -1 when the value pivot was not found. -func (c *Client) LInsertAfter(ctx context.Context, key string, pivot, value interface{}) (int64, error) { - args := []interface{}{"LINSERT", key, "AFTER", pivot, value} - return c.Int(ctx, args...) -} - -// LLen https://redis.io/commands/llen -// Command: LLEN key -// Integer reply: the length of the list at key. -func (c *Client) LLen(ctx context.Context, key string) (int64, error) { - args := []interface{}{"LLEN", key} - return c.Int(ctx, args...) -} - -// LMove https://redis.io/commands/lmove -// Command: LMOVE source destination LEFT|RIGHT LEFT|RIGHT -// Bulk string reply: the element being popped and pushed. -func (c *Client) LMove(ctx context.Context, source, destination, srcPos, destPos string) (string, error) { - args := []interface{}{"LMOVE", source, destination, srcPos, destPos} - return c.String(ctx, args...) -} - -// LPop https://redis.io/commands/lpop -// Command: LPOP key [count] -// Bulk string reply: the value of the first element, or nil when key does not exist. -func (c *Client) LPop(ctx context.Context, key string) (string, error) { - args := []interface{}{"LPOP", key} - return c.String(ctx, args...) -} - -// LPopN https://redis.io/commands/lpop -// Command: LPOP key [count] -// Array reply: list of popped elements, or nil when key does not exist. -func (c *Client) LPopN(ctx context.Context, key string, count int) ([]string, error) { - args := []interface{}{"LPOP", key, count} - return c.StringSlice(ctx, args...) -} - -// LPos https://redis.io/commands/lpos -// Command: LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len] -// The command returns the integer representing the matching element, -// or nil if there is no match. However, if the COUNT option is given -// the command returns an array (empty if there are no matches). -func (c *Client) LPos(ctx context.Context, key string, value interface{}, args ...interface{}) (int64, error) { - args = append([]interface{}{"LPOS", key, value}, args...) - return c.Int(ctx, args...) -} - -// LPosN https://redis.io/commands/lpos -// Command: LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len] -// The command returns the integer representing the matching element, -// or nil if there is no match. However, if the COUNT option is given -// the command returns an array (empty if there are no matches). -func (c *Client) LPosN(ctx context.Context, key string, value interface{}, count int64, args ...interface{}) ([]int64, error) { - args = append([]interface{}{"LPOS", key, value, "COUNT", count}, args...) - return c.IntSlice(ctx, args...) -} - -// LPush https://redis.io/commands/lpush -// Command: LPUSH key element [element ...] -// Integer reply: the length of the list after the push operations. -func (c *Client) LPush(ctx context.Context, key string, values ...interface{}) (int64, error) { - args := []interface{}{"LPUSH", key} - for _, value := range values { - args = append(args, value) - } - return c.Int(ctx, args...) -} - -// LPushX https://redis.io/commands/lpushx -// Command: LPUSHX key element [element ...] -// Integer reply: the length of the list after the push operation. -func (c *Client) LPushX(ctx context.Context, key string, values ...interface{}) (int64, error) { - args := []interface{}{"LPUSHX", key} - for _, value := range values { - args = append(args, value) - } - return c.Int(ctx, args...) -} - -// LRange https://redis.io/commands/lrange -// Command: LRANGE key start stop -// Array reply: list of elements in the specified range. -func (c *Client) LRange(ctx context.Context, key string, start, stop int64) ([]string, error) { - args := []interface{}{"LRANGE", key, start, stop} - return c.StringSlice(ctx, args...) -} - -// LRem https://redis.io/commands/lrem -// Command: LREM key count element -// Integer reply: the number of removed elements. -func (c *Client) LRem(ctx context.Context, key string, count int64, value interface{}) (int64, error) { - args := []interface{}{"LREM", key, count, value} - return c.Int(ctx, args...) -} - -// LSet https://redis.io/commands/lset -// Command: LSET key index element -// Simple string reply -func (c *Client) LSet(ctx context.Context, key string, index int64, value interface{}) (string, error) { - args := []interface{}{"LSET", key, index, value} - return c.String(ctx, args...) -} - -// LTrim https://redis.io/commands/ltrim -// Command: LTRIM key start stop -// Simple string reply -func (c *Client) LTrim(ctx context.Context, key string, start, stop int64) (string, error) { - args := []interface{}{"LTRIM", key, start, stop} - return c.String(ctx, args...) -} - -// RPop https://redis.io/commands/rpop -// Command: RPOP key [count] -// Bulk string reply: the value of the last element, or nil when key does not exist. -func (c *Client) RPop(ctx context.Context, key string) (string, error) { - args := []interface{}{"RPOP", key} - return c.String(ctx, args...) -} - -// RPopN https://redis.io/commands/rpop -// Command: RPOP key [count] -// Array reply: list of popped elements, or nil when key does not exist. -func (c *Client) RPopN(ctx context.Context, key string, count int) ([]string, error) { - args := []interface{}{"RPOP", key, count} - return c.StringSlice(ctx, args...) -} - -// RPopLPush https://redis.io/commands/rpoplpush -// Command: RPOPLPUSH source destination -// Bulk string reply: the element being popped and pushed. -func (c *Client) RPopLPush(ctx context.Context, source, destination string) (string, error) { - args := []interface{}{"RPOPLPUSH", source, destination} - return c.String(ctx, args...) -} - -// RPush https://redis.io/commands/rpush -// Command: RPUSH key element [element ...] -// Integer reply: the length of the list after the push operation. -func (c *Client) RPush(ctx context.Context, key string, values ...interface{}) (int64, error) { - args := []interface{}{"RPUSH", key} - for _, value := range values { - args = append(args, value) - } - return c.Int(ctx, args...) -} - -// RPushX https://redis.io/commands/rpushx -// Command: RPUSHX key element [element ...] -// Integer reply: the length of the list after the push operation. -func (c *Client) RPushX(ctx context.Context, key string, values ...interface{}) (int64, error) { - args := []interface{}{"RPUSHX", key} - for _, value := range values { - args = append(args, value) - } - return c.Int(ctx, args...) -} diff --git a/redis/ops_server.go b/redis/ops_server.go deleted file mode 100644 index 979329d1..00000000 --- a/redis/ops_server.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// FlushAll https://redis.io/commands/flushall -// Command: FLUSHALL [ASYNC|SYNC] -// Simple string reply -func (c *Client) FlushAll(ctx context.Context, args ...interface{}) (string, error) { - args = append([]interface{}{"FLUSHALL"}, args...) - return c.String(ctx, args...) -} diff --git a/redis/ops_set.go b/redis/ops_set.go deleted file mode 100644 index 3cf87247..00000000 --- a/redis/ops_set.go +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// SAdd https://redis.io/commands/sadd -// Command: SADD key member [member ...] -// Integer reply: the number of elements that were added to the set, -// not including all the elements already present in the set. -func (c *Client) SAdd(ctx context.Context, key string, members ...interface{}) (int64, error) { - args := []interface{}{"SADD", key} - args = append(args, members...) - return c.Int(ctx, args...) -} - -// SCard https://redis.io/commands/scard -// Command: SCARD key -// Integer reply: the cardinality (number of elements) of the set, -// or 0 if key does not exist. -func (c *Client) SCard(ctx context.Context, key string) (int64, error) { - args := []interface{}{"SCARD", key} - return c.Int(ctx, args...) -} - -// SDiff https://redis.io/commands/sdiff -// Command: SDIFF key [key ...] -// Array reply: list with members of the resulting set. -func (c *Client) SDiff(ctx context.Context, keys ...string) ([]string, error) { - args := []interface{}{"SDIFF"} - for _, key := range keys { - args = append(args, key) - } - return c.StringSlice(ctx, args...) -} - -// SDiffStore https://redis.io/commands/sdiffstore -// Command: SDIFFSTORE destination key [key ...] -// Integer reply: the number of elements in the resulting set. -func (c *Client) SDiffStore(ctx context.Context, destination string, keys ...string) (int64, error) { - args := []interface{}{"SDIFFSTORE", destination} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// SInter https://redis.io/commands/sinter -// Command: SINTER key [key ...] -// Array reply: list with members of the resulting set. -func (c *Client) SInter(ctx context.Context, keys ...string) ([]string, error) { - args := []interface{}{"SINTER"} - for _, key := range keys { - args = append(args, key) - } - return c.StringSlice(ctx, args...) -} - -// SInterStore https://redis.io/commands/sinterstore -// Command: SINTERSTORE destination key [key ...] -// Integer reply: the number of elements in the resulting set. -func (c *Client) SInterStore(ctx context.Context, destination string, keys ...string) (int64, error) { - args := []interface{}{"SINTERSTORE", destination} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} - -// SIsMember https://redis.io/commands/sismember -// Command: SISMEMBER key member -// Integer reply: 1 if the element is a member of the set, -// 0 if the element is not a member of the set, or if key does not exist. -func (c *Client) SIsMember(ctx context.Context, key string, member interface{}) (int64, error) { - args := []interface{}{"SISMEMBER", key, member} - return c.Int(ctx, args...) -} - -// SMembers https://redis.io/commands/smembers -// Command: SMEMBERS key -// Array reply: all elements of the set. -func (c *Client) SMembers(ctx context.Context, key string) ([]string, error) { - args := []interface{}{"SMEMBERS", key} - return c.StringSlice(ctx, args...) -} - -// SMIsMember https://redis.io/commands/smismember -// Command: SMISMEMBER key member [member ...] -// Array reply: list representing the membership of the given elements, -// in the same order as they are requested. -func (c *Client) SMIsMember(ctx context.Context, key string, members ...interface{}) ([]int64, error) { - args := []interface{}{"SMISMEMBER", key} - for _, member := range members { - args = append(args, member) - } - return c.IntSlice(ctx, args...) -} - -// SMove https://redis.io/commands/smove -// Command: SMOVE source destination member -// Integer reply: 1 if the element is moved, 0 if the element -// is not a member of source and no operation was performed. -func (c *Client) SMove(ctx context.Context, source, destination string, member interface{}) (int64, error) { - args := []interface{}{"SMOVE", source, destination, member} - return c.Int(ctx, args...) -} - -// SPop https://redis.io/commands/spop -// Command: SPOP key [count] -// Bulk string reply: the removed member, or nil when key does not exist. -func (c *Client) SPop(ctx context.Context, key string) (string, error) { - args := []interface{}{"SPOP", key} - return c.String(ctx, args...) -} - -// SPopN https://redis.io/commands/spop -// Command: SPOP key [count] -// Array reply: the removed members, or an empty array when key does not exist. -func (c *Client) SPopN(ctx context.Context, key string, count int64) ([]string, error) { - args := []interface{}{"SPOP", key, count} - return c.StringSlice(ctx, args...) -} - -// SRandMember https://redis.io/commands/srandmember -// Command: SRANDMEMBER key [count] -// Returns a Bulk Reply with the randomly selected element, -// or nil when key does not exist. -func (c *Client) SRandMember(ctx context.Context, key string) (string, error) { - args := []interface{}{"SRANDMEMBER", key} - return c.String(ctx, args...) -} - -// SRandMemberN https://redis.io/commands/srandmember -// Command: SRANDMEMBER key [count] -// Returns an array of elements, or an empty array when key does not exist. -func (c *Client) SRandMemberN(ctx context.Context, key string, count int64) ([]string, error) { - args := []interface{}{"SRANDMEMBER", key, count} - return c.StringSlice(ctx, args...) -} - -// SRem https://redis.io/commands/srem -// Command: SREM key member [member ...] -// Integer reply: the number of members that were removed from the set, -// not including non-existing members. -func (c *Client) SRem(ctx context.Context, key string, members ...interface{}) (int64, error) { - args := []interface{}{"SREM", key} - for _, member := range members { - args = append(args, member) - } - return c.Int(ctx, args...) -} - -// SUnion https://redis.io/commands/sunion -// Command: SUNION key [key ...] -// Array reply: list with members of the resulting set. -func (c *Client) SUnion(ctx context.Context, keys ...string) ([]string, error) { - args := []interface{}{"SUNION"} - for _, key := range keys { - args = append(args, key) - } - return c.StringSlice(ctx, args...) -} - -// SUnionStore https://redis.io/commands/sunionstore -// Command: SUNIONSTORE destination key [key ...] -// Integer reply: the number of elements in the resulting set. -func (c *Client) SUnionStore(ctx context.Context, destination string, keys ...string) (int64, error) { - args := []interface{}{"SUNIONSTORE", destination} - for _, key := range keys { - args = append(args, key) - } - return c.Int(ctx, args...) -} diff --git a/redis/ops_string.go b/redis/ops_string.go deleted file mode 100644 index 150767f2..00000000 --- a/redis/ops_string.go +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -// Append https://redis.io/commands/append -// Command: APPEND key value -// Integer reply: the length of the string after the append operation. -func (c *Client) Append(ctx context.Context, key, value string) (int64, error) { - args := []interface{}{"APPEND", key, value} - return c.Int(ctx, args...) -} - -// Decr https://redis.io/commands/decr -// Command: DECR key -// Integer reply: the value of key after the decrement -func (c *Client) Decr(ctx context.Context, key string) (int64, error) { - args := []interface{}{"DECR", key} - return c.Int(ctx, args...) -} - -// DecrBy https://redis.io/commands/decrby -// Command: DECRBY key decrement -// Integer reply: the value of key after the decrement. -func (c *Client) DecrBy(ctx context.Context, key string, decrement int64) (int64, error) { - args := []interface{}{"DECRBY", key, decrement} - return c.Int(ctx, args...) -} - -// Get https://redis.io/commands/get -// Command: GET key -// Bulk string reply: the value of key, or nil when key does not exist. -func (c *Client) Get(ctx context.Context, key string) (string, error) { - args := []interface{}{"GET", key} - return c.String(ctx, args...) -} - -// GetDel https://redis.io/commands/getdel -// Command: GETDEL key -// Bulk string reply: the value of key, nil when key does not exist, -// or an error if the key's value type isn't a string. -func (c *Client) GetDel(ctx context.Context, key string) (string, error) { - args := []interface{}{"GETDEL", key} - return c.String(ctx, args...) -} - -// GetEx https://redis.io/commands/getex -// Command: GETEX key [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|PERSIST] -// Bulk string reply: the value of key, or nil when key does not exist. -func (c *Client) GetEx(ctx context.Context, key string, args ...interface{}) (string, error) { - args = append([]interface{}{"GETEX", key}, args...) - return c.String(ctx, args...) -} - -// GetRange https://redis.io/commands/getrange -// Command: GETRANGE key start end -// Bulk string reply -func (c *Client) GetRange(ctx context.Context, key string, start, end int64) (string, error) { - args := []interface{}{"GETRANGE", key, start, end} - return c.String(ctx, args...) -} - -// GetSet https://redis.io/commands/getset -// Command: GETSET key value -// Bulk string reply: the old value stored at key, or nil when key did not exist. -func (c *Client) GetSet(ctx context.Context, key string, value interface{}) (string, error) { - args := []interface{}{"GETSET", key, value} - return c.String(ctx, args...) -} - -// Incr https://redis.io/commands/incr -// Command: INCR key -// Integer reply: the value of key after the increment -func (c *Client) Incr(ctx context.Context, key string) (int64, error) { - args := []interface{}{"INCR", key} - return c.Int(ctx, args...) -} - -// IncrBy https://redis.io/commands/incrby -// Command: INCRBY key increment -// Integer reply: the value of key after the increment. -func (c *Client) IncrBy(ctx context.Context, key string, value int64) (int64, error) { - args := []interface{}{"INCRBY", key, value} - return c.Int(ctx, args...) -} - -// IncrByFloat https://redis.io/commands/incrbyfloat -// Command: INCRBYFLOAT key increment -// Bulk string reply: the value of key after the increment. -func (c *Client) IncrByFloat(ctx context.Context, key string, value float64) (float64, error) { - args := []interface{}{"INCRBYFLOAT", key, value} - return c.Float(ctx, args...) -} - -// MGet https://redis.io/commands/mget -// Command: MGET key [key ...] -// Array reply: list of values at the specified keys. -func (c *Client) MGet(ctx context.Context, keys ...string) ([]interface{}, error) { - args := []interface{}{"MGET"} - for _, key := range keys { - args = append(args, key) - } - return c.Slice(ctx, args...) -} - -// MSet https://redis.io/commands/mset -// Command: MSET key value [key value ...] -// Simple string reply: always OK since MSET can't fail. -func (c *Client) MSet(ctx context.Context, args ...interface{}) (string, error) { - args = append([]interface{}{"MSET"}, args...) - return c.String(ctx, args...) -} - -// MSetNX https://redis.io/commands/msetnx -// Command: MSETNX key value [key value ...] -// MSETNX is atomic, so all given keys are set at once -// Integer reply: 1 if the all the keys were set, 0 if no -// key was set (at least one key already existed). -func (c *Client) MSetNX(ctx context.Context, args ...interface{}) (int64, error) { - args = append([]interface{}{"MSETNX"}, args...) - return c.Int(ctx, args...) -} - -// PSetEX https://redis.io/commands/psetex -// Command: PSETEX key milliseconds value -// Simple string reply -func (c *Client) PSetEX(ctx context.Context, key string, value interface{}, expire int64) (string, error) { - args := []interface{}{"PSETEX", key, expire, value} - return c.String(ctx, args...) -} - -// Set https://redis.io/commands/set -// Command: SET key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT milliseconds-timestamp|KEEPTTL] [NX|XX] [GET] -// Simple string reply: OK if SET was executed correctly. -func (c *Client) Set(ctx context.Context, key string, value interface{}, args ...interface{}) (string, error) { - args = append([]interface{}{"SET", key, value}, args...) - return c.String(ctx, args...) -} - -// SetEX https://redis.io/commands/setex -// Command: SETEX key seconds value -// Simple string reply -func (c *Client) SetEX(ctx context.Context, key string, value interface{}, expire int64) (string, error) { - args := []interface{}{"SETEX", key, expire, value} - return c.String(ctx, args...) -} - -// SetNX https://redis.io/commands/setnx -// Command: SETNX key value -// Integer reply: 1 if the key was set, 0 if the key was not set. -func (c *Client) SetNX(ctx context.Context, key string, value interface{}) (int64, error) { - args := []interface{}{"SETNX", key, value} - return c.Int(ctx, args...) -} - -// SetRange https://redis.io/commands/setrange -// Command: SETRANGE key offset value -// Integer reply: the length of the string after it was modified by the command. -func (c *Client) SetRange(ctx context.Context, key string, offset int64, value string) (int64, error) { - args := []interface{}{"SETRANGE", key, offset, value} - return c.Int(ctx, args...) -} - -// StrLen https://redis.io/commands/strlen -// Command: STRLEN key -// Integer reply: the length of the string at key, or 0 when key does not exist. -func (c *Client) StrLen(ctx context.Context, key string) (int64, error) { - args := []interface{}{"STRLEN", key} - return c.Int(ctx, args...) -} diff --git a/redis/ops_zset.go b/redis/ops_zset.go deleted file mode 100644 index afb6747e..00000000 --- a/redis/ops_zset.go +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import ( - "context" -) - -type ZItem struct { - Member interface{} - Score float64 -} - -// ZAdd https://redis.io/commands/zadd -// Command: ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] -// Integer reply, the number of elements added to the -// sorted set (excluding score updates). -func (c *Client) ZAdd(ctx context.Context, key string, args ...interface{}) (int64, error) { - args = append([]interface{}{"ZADD", key}, args...) - return c.Int(ctx, args...) -} - -// ZCard https://redis.io/commands/zcard -// Command: ZCARD key -// Integer reply: the cardinality (number of elements) -// of the sorted set, or 0 if key does not exist. -func (c *Client) ZCard(ctx context.Context, key string) (int64, error) { - args := []interface{}{"ZCARD", key} - return c.Int(ctx, args...) -} - -// ZCount https://redis.io/commands/zcount -// Command: ZCOUNT key min max -// Integer reply: the number of elements in the specified score range. -func (c *Client) ZCount(ctx context.Context, key, min, max string) (int64, error) { - args := []interface{}{"ZCOUNT", key, min, max} - return c.Int(ctx, args...) -} - -// ZDiff https://redis.io/commands/zdiff -// Command: ZDIFF numkeys key [key ...] [WITHSCORES] -// Array reply: the result of the difference. -func (c *Client) ZDiff(ctx context.Context, keys ...string) ([]string, error) { - args := []interface{}{"ZDIFF", len(keys)} - for _, key := range keys { - args = append(args, key) - } - return c.StringSlice(ctx, args...) -} - -// ZDiffWithScores https://redis.io/commands/zdiff -// Command: ZDIFF numkeys key [key ...] [WITHSCORES] -// Array reply: the result of the difference. -func (c *Client) ZDiffWithScores(ctx context.Context, keys ...string) ([]ZItem, error) { - args := []interface{}{"ZDIFF", len(keys)} - for _, key := range keys { - args = append(args, key) - } - args = append(args, "WITHSCORES") - return c.ZItemSlice(ctx, args...) -} - -// ZIncrBy https://redis.io/commands/zincrby -// Command: ZINCRBY key increment member -// Bulk string reply: the new score of member -// (a double precision floating point number), represented as string. -func (c *Client) ZIncrBy(ctx context.Context, key string, increment float64, member string) (float64, error) { - args := []interface{}{"ZINCRBY", key, increment, member} - return c.Float(ctx, args...) -} - -// ZInter https://redis.io/commands/zinter -// Command: ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] -// Array reply: the result of intersection. -func (c *Client) ZInter(ctx context.Context, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZINTER"}, args...) - return c.StringSlice(ctx, args...) -} - -// ZInterWithScores https://redis.io/commands/zinter -// Command: ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] -// Array reply: the result of intersection. -func (c *Client) ZInterWithScores(ctx context.Context, args ...interface{}) ([]ZItem, error) { - args = append([]interface{}{"ZINTER"}, args...) - args = append(args, "WITHSCORES") - return c.ZItemSlice(ctx, args...) -} - -// ZLexCount https://redis.io/commands/zlexcount -// Command: ZLEXCOUNT key min max -// Integer reply: the number of elements in the specified score range. -func (c *Client) ZLexCount(ctx context.Context, key, min, max string) (int64, error) { - args := []interface{}{"ZLEXCOUNT", key, min, max} - return c.Int(ctx, args...) -} - -// ZMScore https://redis.io/commands/zmscore -// Command: ZMSCORE key member [member ...] -// Array reply: list of scores or nil associated with the specified member -// values (a double precision floating point number), represented as strings. -func (c *Client) ZMScore(ctx context.Context, key string, members ...string) ([]float64, error) { - args := []interface{}{"ZMSCORE", key} - for _, member := range members { - args = append(args, member) - } - return c.FloatSlice(ctx, args...) -} - -// ZPopMax https://redis.io/commands/zpopmax -// Command: ZPOPMAX key [count] -// Array reply: list of popped elements and scores. -func (c *Client) ZPopMax(ctx context.Context, key string) ([]ZItem, error) { - args := []interface{}{"ZPOPMAX", key} - return c.ZItemSlice(ctx, args...) -} - -// ZPopMaxN https://redis.io/commands/zpopmax -// Command: ZPOPMAX key [count] -// Array reply: list of popped elements and scores. -func (c *Client) ZPopMaxN(ctx context.Context, key string, count int64) ([]ZItem, error) { - args := []interface{}{"ZPOPMAX", key, count} - return c.ZItemSlice(ctx, args...) -} - -// ZPopMin https://redis.io/commands/zpopmin -// Command: ZPOPMIN key [count] -// Array reply: list of popped elements and scores. -func (c *Client) ZPopMin(ctx context.Context, key string) ([]ZItem, error) { - args := []interface{}{"ZPOPMIN", key} - return c.ZItemSlice(ctx, args...) -} - -// ZPopMinN https://redis.io/commands/zpopmin -// Command: ZPOPMIN key [count] -// Array reply: list of popped elements and scores. -func (c *Client) ZPopMinN(ctx context.Context, key string, count int64) ([]ZItem, error) { - args := []interface{}{"ZPOPMIN", key, count} - return c.ZItemSlice(ctx, args...) -} - -// ZRandMember https://redis.io/commands/zrandmember -// Command: ZRANDMEMBER key [count [WITHSCORES]] -// Bulk Reply with the randomly selected element, or nil when key does not exist. -func (c *Client) ZRandMember(ctx context.Context, key string) (string, error) { - args := []interface{}{"ZRANDMEMBER", key} - return c.String(ctx, args...) -} - -// ZRandMemberN https://redis.io/commands/zrandmember -// Command: ZRANDMEMBER key [count [WITHSCORES]] -// Bulk Reply with the randomly selected element, or nil when key does not exist. -func (c *Client) ZRandMemberN(ctx context.Context, key string, count int) ([]string, error) { - args := []interface{}{"ZRANDMEMBER", key, count} - return c.StringSlice(ctx, args...) -} - -// ZRandMemberWithScores https://redis.io/commands/zrandmember -// Command: ZRANDMEMBER key [count [WITHSCORES]] -// Bulk Reply with the randomly selected element, or nil when key does not exist. -func (c *Client) ZRandMemberWithScores(ctx context.Context, key string, count int) ([]ZItem, error) { - args := []interface{}{"ZRANDMEMBER", key, count, "WITHSCORES"} - return c.ZItemSlice(ctx, args...) -} - -// ZRange https://redis.io/commands/zrange -// Command: ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES] -// Array reply: list of elements in the specified range. -func (c *Client) ZRange(ctx context.Context, key string, start, stop int64, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZRANGE", key, start, stop}, args...) - return c.StringSlice(ctx, args...) -} - -// ZRangeWithScores https://redis.io/commands/zrange -// Command: ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES] -// Array reply: list of elements in the specified range. -func (c *Client) ZRangeWithScores(ctx context.Context, key string, start, stop int64, args ...interface{}) ([]ZItem, error) { - args = append([]interface{}{"ZRANGE", key, start, stop}, args...) - args = append(args, "WITHSCORES") - return c.ZItemSlice(ctx, args...) -} - -// ZRangeByLex https://redis.io/commands/zrangebylex -// Command: ZRANGEBYLEX key min max [LIMIT offset count] -// Array reply: list of elements in the specified score range. -func (c *Client) ZRangeByLex(ctx context.Context, key string, min, max string, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZRANGEBYLEX", key, min, max}, args...) - return c.StringSlice(ctx, args...) -} - -// ZRangeByScore https://redis.io/commands/zrangebyscore -// Command: ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] -// Array reply: list of elements in the specified score range. -func (c *Client) ZRangeByScore(ctx context.Context, key string, min, max string, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZRANGEBYSCORE", key, min, max}, args...) - return c.StringSlice(ctx, args...) -} - -// ZRank https://redis.io/commands/zrank -// Command: ZRANK key member -// If member exists in the sorted set, Integer reply: the rank of member. -// If member does not exist in the sorted set or key does not exist, Bulk string reply: nil. -func (c *Client) ZRank(ctx context.Context, key, member string) (int64, error) { - args := []interface{}{"ZRANK", key, member} - return c.Int(ctx, args...) -} - -// ZRem https://redis.io/commands/zrem -// Command: ZREM key member [member ...] -// Integer reply, The number of members removed from the sorted set, not including non existing members. -func (c *Client) ZRem(ctx context.Context, key string, members ...interface{}) (int64, error) { - args := []interface{}{"ZREM", key} - for _, member := range members { - args = append(args, member) - } - return c.Int(ctx, args...) -} - -// ZRemRangeByLex https://redis.io/commands/zremrangebylex -// Command: ZREMRANGEBYLEX key min max -// Integer reply: the number of elements removed. -func (c *Client) ZRemRangeByLex(ctx context.Context, key, min, max string) (int64, error) { - args := []interface{}{"ZREMRANGEBYLEX", key, min, max} - return c.Int(ctx, args...) -} - -// ZRemRangeByRank https://redis.io/commands/zremrangebyrank -// Command: ZREMRANGEBYRANK key start stop -// Integer reply: the number of elements removed. -func (c *Client) ZRemRangeByRank(ctx context.Context, key string, start, stop int64) (int64, error) { - args := []interface{}{"ZREMRANGEBYRANK", key, start, stop} - return c.Int(ctx, args...) -} - -// ZRemRangeByScore https://redis.io/commands/zremrangebyscore -// Command: ZREMRANGEBYSCORE key min max -// Integer reply: the number of elements removed. -func (c *Client) ZRemRangeByScore(ctx context.Context, key, min, max string) (int64, error) { - args := []interface{}{"ZREMRANGEBYSCORE", key, min, max} - return c.Int(ctx, args...) -} - -// ZRevRange https://redis.io/commands/zrevrange -// Command: ZREVRANGE key start stop [WITHSCORES] -// Array reply: list of elements in the specified range. -func (c *Client) ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error) { - args := []interface{}{"ZREVRANGE", key, start, stop} - return c.StringSlice(ctx, args...) -} - -// ZRevRangeWithScores https://redis.io/commands/zrevrange -// Command: ZREVRANGE key start stop [WITHSCORES] -// Array reply: list of elements in the specified range. -func (c *Client) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) ([]string, error) { - args := []interface{}{"ZREVRANGE", key, start, stop, "WITHSCORES"} - return c.StringSlice(ctx, args...) -} - -// ZRevRangeByLex https://redis.io/commands/zrevrangebylex -// Command: ZREVRANGEBYLEX key max min [LIMIT offset count] -// Array reply: list of elements in the specified score range. -func (c *Client) ZRevRangeByLex(ctx context.Context, key string, min, max string, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZREVRANGEBYLEX", key, min, max}, args...) - return c.StringSlice(ctx, args...) -} - -// ZRevRangeByScore https://redis.io/commands/zrevrangebyscore -// Command: ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] -// Array reply: list of elements in the specified score range. -func (c *Client) ZRevRangeByScore(ctx context.Context, key string, min, max string, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZREVRANGEBYSCORE", key, min, max}, args...) - return c.StringSlice(ctx, args...) -} - -// ZRevRank https://redis.io/commands/zrevrank -// Command: ZREVRANK key member -// If member exists in the sorted set, Integer reply: the rank of member. -// If member does not exist in the sorted set or key does not exist, Bulk string reply: nil. -func (c *Client) ZRevRank(ctx context.Context, key, member string) (int64, error) { - args := []interface{}{"ZREVRANK", key, member} - return c.Int(ctx, args...) -} - -// ZScore https://redis.io/commands/zscore -// Command: ZSCORE key member -// Bulk string reply: the score of member (a double precision floating point number), represented as string. -func (c *Client) ZScore(ctx context.Context, key, member string) (float64, error) { - args := []interface{}{"ZSCORE", key, member} - return c.Float(ctx, args...) -} - -// ZUnion https://redis.io/commands/zunion -// Command: ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] -// Array reply: the result of union. -func (c *Client) ZUnion(ctx context.Context, args ...interface{}) ([]string, error) { - args = append([]interface{}{"ZUNION"}, args...) - return c.StringSlice(ctx, args...) -} - -// ZUnionWithScores https://redis.io/commands/zunion -// Command: ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] -// Array reply: the result of union. -func (c *Client) ZUnionWithScores(ctx context.Context, args ...interface{}) ([]ZItem, error) { - args = append([]interface{}{"ZUNION"}, args...) - args = append(args, "WITHSCORES") - return c.ZItemSlice(ctx, args...) -} - -// ZUnionStore https://redis.io/commands/zunionstore -// Command: ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] -// Integer reply: the number of elements in the resulting sorted set at destination. -func (c *Client) ZUnionStore(ctx context.Context, dest string, args ...interface{}) (int64, error) { - args = append([]interface{}{"ZUNIONSTORE", dest}, args...) - return c.Int(ctx, args...) -} diff --git a/redis/redis.go b/redis/redis.go deleted file mode 100644 index de9f6627..00000000 --- a/redis/redis.go +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//go:generate mockgen -build_flags="-mod=mod" -package=redis -source=redis.go -destination=redis_mock.go - -// Package redis provides operations for redis commands. -package redis - -import ( - "context" - "fmt" - "strconv" -) - -// IsOK returns whether s equals "OK". -func IsOK(s string) bool { - return "OK" == s -} - -// Config Is the configuration of redis client. -type Config struct { - Host string `value:"${host:=127.0.0.1}"` - Port int `value:"${port:=6379}"` - Username string `value:"${username:=}"` - Password string `value:"${password:=}"` - Database int `value:"${database:=0}"` - Ping bool `value:"${ping:=true}"` - IdleTimeout int `value:"${idle-timeout:=0}"` - ConnectTimeout int `value:"${connect-timeout:=0}"` - ReadTimeout int `value:"${read-timeout:=0}"` - WriteTimeout int `value:"${write-timeout:=0}"` -} - -type Result struct { - Data []string -} - -type Driver interface { - Exec(ctx context.Context, args []interface{}) (interface{}, error) -} - -var ( - Recorder func(Driver) Driver - Replayer func(Driver) Driver -) - -// Client provides operations for redis commands. -type Client struct { - driver Driver -} - -// NewClient returns a new *Client. -func NewClient(driver Driver) *Client { - if Recorder != nil { - driver = Recorder(driver) - } - if Replayer != nil { - driver = Replayer(driver) - } - return &Client{driver: driver} -} - -func toInt64(v interface{}, err error) (int64, error) { - if err != nil { - return 0, err - } - switch r := v.(type) { - case int64: - return r, nil - case float64: - return int64(r), nil - case string: - return strconv.ParseInt(r, 10, 64) - case *Result: - if len(r.Data) == 0 { - return 0, fmt.Errorf("redis: no data") - } - return toInt64(r.Data[0], nil) - default: - return 0, fmt.Errorf("redis: unexpected type %T for int64", v) - } -} - -// Int executes a command whose reply is a `int64`. -func (c *Client) Int(ctx context.Context, args ...interface{}) (int64, error) { - return toInt64(c.driver.Exec(ctx, args)) -} - -func toFloat64(v interface{}, err error) (float64, error) { - if err != nil { - return 0, err - } - switch r := v.(type) { - case nil: - return 0, nil - case int64: - return float64(r), nil - case string: - return strconv.ParseFloat(r, 64) - case *Result: - if len(r.Data) == 0 { - return 0, fmt.Errorf("redis: no data") - } - return toFloat64(r.Data[0], nil) - default: - return 0, fmt.Errorf("redis: unexpected type=%T for float64", r) - } -} - -// Float executes a command whose reply is a `float64`. -func (c *Client) Float(ctx context.Context, args ...interface{}) (float64, error) { - return toFloat64(c.driver.Exec(ctx, args)) -} - -func toString(v interface{}, err error) (string, error) { - if err != nil { - return "", err - } - switch r := v.(type) { - case string: - return r, nil - case *Result: - if len(r.Data) == 0 { - return "", fmt.Errorf("redis: no data") - } - return r.Data[0], nil - default: - return "", fmt.Errorf("redis: unexpected type %T for string", v) - } -} - -// String executes a command whose reply is a `string`. -func (c *Client) String(ctx context.Context, args ...interface{}) (string, error) { - return toString(c.driver.Exec(ctx, args)) -} - -func toSlice(v interface{}, err error) ([]interface{}, error) { - if err != nil { - return nil, err - } - switch r := v.(type) { - case []interface{}: - return r, nil - case []string: - var slice []interface{} - for _, str := range r { - if str == "NULL" { - slice = append(slice, nil) - } else { - slice = append(slice, str) - } - } - return slice, nil - case *Result: - return toSlice(r.Data, nil) - default: - return nil, fmt.Errorf("redis: unexpected type %T for []interface{}", v) - } -} - -// Slice executes a command whose reply is a `[]interface{}`. -func (c *Client) Slice(ctx context.Context, args ...interface{}) ([]interface{}, error) { - return toSlice(c.driver.Exec(ctx, args)) -} - -func toInt64Slice(v interface{}, err error) ([]int64, error) { - slice, err := toSlice(v, err) - if err != nil { - return nil, err - } - val := make([]int64, len(slice)) - for i, r := range slice { - var n int64 - n, err = toInt64(r, nil) - if err != nil { - return nil, err - } - val[i] = n - } - return val, nil -} - -// IntSlice executes a command whose reply is a `[]int64`. -func (c *Client) IntSlice(ctx context.Context, args ...interface{}) ([]int64, error) { - return toInt64Slice(c.driver.Exec(ctx, args)) -} - -func toFloat64Slice(v interface{}, err error) ([]float64, error) { - slice, err := toSlice(v, err) - if err != nil { - return nil, err - } - val := make([]float64, len(slice)) - for i, r := range slice { - var f float64 - f, err = toFloat64(r, nil) - if err != nil { - return nil, err - } - val[i] = f - } - return val, nil -} - -// FloatSlice executes a command whose reply is a `[]float64`. -func (c *Client) FloatSlice(ctx context.Context, args ...interface{}) ([]float64, error) { - return toFloat64Slice(c.driver.Exec(ctx, args)) -} - -func toStringSlice(v interface{}, err error) ([]string, error) { - slice, err := toSlice(v, err) - if err != nil { - return nil, err - } - val := make([]string, len(slice)) - for i, r := range slice { - var str string - str, err = toString(r, nil) - if err != nil { - return nil, err - } - val[i] = str - } - return val, nil -} - -// StringSlice executes a command whose reply is a `[]string`. -func (c *Client) StringSlice(ctx context.Context, args ...interface{}) ([]string, error) { - return toStringSlice(c.driver.Exec(ctx, args)) -} - -func toStringMap(v interface{}, err error) (map[string]string, error) { - if err != nil { - return nil, err - } - slice, err := toStringSlice(v, err) - if err != nil { - return nil, err - } - val := make(map[string]string, len(slice)/2) - for i := 0; i < len(slice); i += 2 { - val[slice[i]] = slice[i+1] - } - return val, nil -} - -// StringMap executes a command whose reply is a `map[string]string`. -func (c *Client) StringMap(ctx context.Context, args ...interface{}) (map[string]string, error) { - return toStringMap(c.driver.Exec(ctx, args)) -} - -func toZItemSlice(v interface{}, err error) ([]ZItem, error) { - if err != nil { - return nil, err - } - slice, err := toStringSlice(v, err) - if err != nil { - return nil, err - } - val := make([]ZItem, len(slice)/2) - for i := 0; i < len(val); i++ { - idx := i * 2 - var score float64 - score, err = toFloat64(slice[idx+1], nil) - if err != nil { - return nil, err - } - val[i].Member = slice[idx] - val[i].Score = score - } - return val, nil -} - -// ZItemSlice executes a command whose reply is a `[]ZItem`. -func (c *Client) ZItemSlice(ctx context.Context, args ...interface{}) ([]ZItem, error) { - return toZItemSlice(c.driver.Exec(ctx, args)) -} diff --git a/redis/redis_error.go b/redis/redis_error.go deleted file mode 100644 index 0dded1c1..00000000 --- a/redis/redis_error.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis - -import "errors" - -var ( - errNil = errors.New("redis: nil") -) - -// ErrNil returns the `errNil` error. -func ErrNil() error { - return errNil -} - -// IsErrNil returns whether err is the `errNil` error. -func IsErrNil(err error) bool { - return errors.Is(err, errNil) -} diff --git a/redis/redis_mock.go b/redis/redis_mock.go deleted file mode 100644 index 708fc553..00000000 --- a/redis/redis_mock.go +++ /dev/null @@ -1,50 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: redis.go - -// Package redis is a generated GoMock package. -package redis - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockDriver is a mock of Driver interface. -type MockDriver struct { - ctrl *gomock.Controller - recorder *MockDriverMockRecorder -} - -// MockDriverMockRecorder is the mock recorder for MockDriver. -type MockDriverMockRecorder struct { - mock *MockDriver -} - -// NewMockDriver creates a new mock instance. -func NewMockDriver(ctrl *gomock.Controller) *MockDriver { - mock := &MockDriver{ctrl: ctrl} - mock.recorder = &MockDriverMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDriver) EXPECT() *MockDriverMockRecorder { - return m.recorder -} - -// Exec mocks base method. -func (m *MockDriver) Exec(ctx context.Context, args []interface{}) (interface{}, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Exec", ctx, args) - ret0, _ := ret[0].(interface{}) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Exec indicates an expected call of Exec. -func (mr *MockDriverMockRecorder) Exec(ctx, args interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockDriver)(nil).Exec), ctx, args) -} diff --git a/redis/redis_mock_test.go b/redis/redis_mock_test.go deleted file mode 100644 index 24e0604f..00000000 --- a/redis/redis_mock_test.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package redis_test - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/redis" - "github.com/golang/mock/gomock" -) - -func TestMock(t *testing.T) { - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - conn := redis.NewMockDriver(ctrl) - conn.EXPECT().Exec(ctx, []interface{}{"EXISTS", "mykey"}).Return(int64(0), nil) - - c := redis.NewClient(conn) - r1, _ := c.Exists(ctx, "mykey") - assert.Equal(t, r1, int64(0)) -} diff --git a/validate/validate.go b/validate/validate.go deleted file mode 100644 index 7d364a40..00000000 --- a/validate/validate.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package validate - -import ( - "fmt" - - "github.com/go-spring/spring-core/expr" -) - -type Interface interface { - TagName() string - Struct(i interface{}) error - Field(i interface{}, tag string) error -} - -var Validator Interface = &Validate{} - -func TagName() string { - return Validator.TagName() -} - -func Struct(i interface{}) error { - return Validator.Struct(i) -} - -func Field(i interface{}, tag string) error { - return Validator.Field(i, tag) -} - -type Validate struct{} - -func (d Validate) TagName() string { - return "expr" -} - -func (d Validate) Struct(i interface{}) error { - return nil -} - -func (d Validate) Field(i interface{}, tag string) error { - if tag == "" { - return nil - } - ok, err := expr.Eval(tag, i) - if err != nil { - return err - } - if !ok { - return fmt.Errorf("validate failed on %q for value %v", tag, i) - } - return nil -} diff --git a/web/README.md b/web/README.md deleted file mode 100644 index 3a53ea5b..00000000 --- a/web/README.md +++ /dev/null @@ -1,259 +0,0 @@ -# web - -为社区优秀的 Web 服务器提供一个抽象层,使得底层可以灵活切换。 - -- [Guide](#guide) - - [Installation](#installation) - - [Routing](#routing) - - [Context](#context) - - [Request](#request) - - [Response](#response) - - [Binding](#binding) - - [Cookies](#cookies) - - [Validator](#validator) - - [Error Handling](#error handling) - - [IP Address](#ip address) - - [HTTPS Server](#https server) - - [Static Files](#static files) - - [Templates](#templates) -- [Middleware](#middleware) - - [Basic Auth](#basic auth) - - [Method Override](#method override) - - [Redirect](#redirect) - - [Request ID](#request id) - - [Rewrite](#rewrite) -- [Cookbook](#cookbook) - - [Hello World](#hello world) - - [Graceful Shutdown](#graceful shutdown) - - [JSONP](#jsonp) - - [File Upload](#file upload) - - [File Download](#file download) - -## Installation - -``` -go get github.com/go-spring/spring-core@v1.1.0-rc2 -``` - -## Import - -``` -import "github.com/go-spring/spring-core/web" -``` - -## Example - -### 普通路由 - -``` -package main - -import ( - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -func main() { - gs.GetMapping("/a/b/c", func(ctx web.Context) { - ctx.String("OK") - }) - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl http://127.0.0.1:8080/a/b/c -OK -``` - -### java 风格路由 - -``` -package main - -import ( - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -func main() { - gs.GetMapping("/:a/b/:c/{*:d}", func(ctx web.Context) { - ctx.String("a=%s b=%s *=%s\n", ctx.PathParam("a"), ctx.PathParam("c"), ctx.PathParam("*")) - ctx.String("a=%s b=%s *=%s\n", ctx.PathParam("a"), ctx.PathParam("c"), ctx.PathParam("d")) - }) - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl http://127.0.0.1:8080/a/b/c/d -a=a b=c *=d -a=a b=c *=d -``` - -### echo 风格路由 - -``` -package main - -import ( - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -func main() { - gs.GetMapping("/:a/b/:c/*", func(ctx web.Context) { - ctx.String("a=%s c=%s *=%s", ctx.PathParam("a"), ctx.PathParam("c"), ctx.PathParam("*")) - }) - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl http://127.0.0.1:8080/a/b/c/d -a=a c=c *=d -``` - -### gin 风格路由 - -``` -package main - -import ( - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -func main() { - gs.GetMapping("/:a/b/:c/*d", func(ctx web.Context) { - ctx.String("a=%s b=%s *=%s\n", ctx.PathParam("a"), ctx.PathParam("c"), ctx.PathParam("*")) - ctx.String("a=%s b=%s *=%s\n", ctx.PathParam("a"), ctx.PathParam("c"), ctx.PathParam("d")) - }) - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl http://127.0.0.1:8080/a/b/c/d -a=a b=c *=d -a=a b=c *=d -``` - -### 文件服务器 - -``` -package main - -import ( - "fmt" - "net/http" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -func main() { - gs.HandleGet("/public/*", web.WrapH(http.StripPrefix("/public/", http.FileServer(http.Dir("public"))))) - fmt.Println(gs.Run()) -} -``` - -然后在项目下创建一个 public 目录,里面创建一个内容为 hello world! 的 a.txt 文件。 - -``` -➜ curl http://127.0.0.1:8080/public/a.txt -hello world! -``` - -### BIND 模式 - -``` -package main - -import ( - "context" - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - _ "github.com/go-spring/starter-echo" -) - -type HelloReq struct { - Name string `form:"name"` -} - -type HelloResp struct { - Body string `json:"body"` -} - -func main() { - gs.GetBinding("/hello", func(ctx context.Context, req *HelloReq) *web.RpcResult { - return web.SUCCESS.Data(&HelloResp{Body: "hello " + req.Name + "!"}) - }) - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl 'http://127.0.0.1:8080/hello?name=lvan100' -{"code":200,"msg":"SUCCESS","data":{"body":"hello lvan100!"}} -``` - -### 中间件 - -#### Basic Auth - -``` -package main - -import ( - "fmt" - - "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/web" - "github.com/go-spring/spring-echo" - _ "github.com/go-spring/starter-echo" - "github.com/labstack/echo" - "github.com/labstack/echo/middleware" -) - -func main() { - - gs.Provide(func( /* 可以通过配置将用户名密码传进来 */ ) web.Filter { - m := middleware.BasicAuth(func(u string, p string, e echo.Context) (bool, error) { - if u == "lvan100" && p == "123456" { - return true, nil - } - return false, nil - }) - return SpringEcho.Filter(m) - }) - - gs.GetMapping("/hello", func(ctx web.Context) { - ctx.String("hello %s!", ctx.QueryParam("name")) - }) - - fmt.Println(gs.Run()) -} -``` - -``` -➜ curl 'http://127.0.0.1:8080/hello?name=lvan100' -Unauthorized -➜ curl 'http://127.0.0.1:8080/hello?name=lvan100' -H'Authorization: Basic bHZhbjEwMDoxMjM0NTY=' -{"code":200,"msg":"SUCCESS","data":{"body":"hello lvan100!"}} -``` \ No newline at end of file diff --git a/web/basic-auth.go b/web/basic-auth.go deleted file mode 100644 index 9407d0ff..00000000 --- a/web/basic-auth.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "bytes" - "encoding/base64" - "fmt" - "strings" - - "github.com/go-spring/spring-base/util" -) - -const ( - basicPrefix = "Basic " - defaultRealm = "Authorization Required" -) - -const ( - AuthUserKey = "::user::" -) - -type BasicAuthConfig struct { - Accounts map[string]string - Realm string -} - -// basicAuthFilter 封装 http 基础认证功能的过滤器。 -type basicAuthFilter struct { - config BasicAuthConfig -} - -// NewBasicAuthFilter 创建封装 http 基础认证功能的过滤器。 -func NewBasicAuthFilter(config BasicAuthConfig) Filter { - if config.Realm == "" { - config.Realm = defaultRealm - } - return &basicAuthFilter{config: config} -} - -func (f *basicAuthFilter) Invoke(ctx Context, chain FilterChain) { - - auth := ctx.Header(HeaderWWWAuthenticate) - if len(auth) <= len(basicPrefix) { - f.unauthorized(ctx) - return - } - - if !strings.EqualFold(auth[:len(basicPrefix)], basicPrefix) { - f.unauthorized(ctx) - return - } - - b, err := base64.StdEncoding.DecodeString(auth[len(basicPrefix):]) - util.Panic(err).When(err != nil) - - i := bytes.IndexByte(b, ':') - if i <= 0 { - f.unauthorized(ctx) - return - } - - user := string(b[:i]) - password := string(b[i+1:]) - - ok := false - for k, v := range f.config.Accounts { - if k == user && v == password { - ok = true - break - } - } - - if !ok { - f.unauthorized(ctx) - return - } - - ctx.Set(AuthUserKey, user) - chain.Next(ctx, Iterative) -} - -func (f *basicAuthFilter) unauthorized(ctx Context) { - ctx.SetHeader(HeaderWWWAuthenticate, fmt.Sprintf("Basic realm=%q", f.config.Realm)) -} diff --git a/web/basic-auth_test.go b/web/basic-auth_test.go deleted file mode 100644 index 22cfd1f6..00000000 --- a/web/basic-auth_test.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestBasicAuthFilter(t *testing.T) { - r, _ := http.NewRequest(http.MethodPost, "http://127.0.0.1:8080/", nil) - r.Header.Set(web.HeaderWWWAuthenticate, "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==") - w := httptest.NewRecorder() - ctx := web.NewBaseContext("", nil, r, &web.SimpleResponse{ResponseWriter: w}) - f := web.NewBasicAuthFilter(web.BasicAuthConfig{ - Accounts: map[string]string{"Aladdin": "open sesame"}, - }) - web.NewFilterChain([]web.Filter{f}).Next(ctx, web.Recursive) - user := ctx.Get(web.AuthUserKey) - assert.Equal(t, user, "Aladdin") -} diff --git a/web/binding.go b/web/binding.go deleted file mode 100644 index 532f0eac..00000000 --- a/web/binding.go +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "fmt" - "reflect" - "strconv" - - "github.com/go-spring/spring-core/validate" -) - -type BindScope int - -const ( - BindScopeURI BindScope = iota - BindScopeQuery - BindScopeHeader - BindScopeBody -) - -var scopeBinders = map[BindScope][]string{ - BindScopeURI: {"uri", "path"}, - BindScopeQuery: {"query", "param"}, - BindScopeHeader: {"header"}, -} - -var scopeGetters = map[BindScope]func(ctx Context, name string) string{ - BindScopeURI: Context.PathParam, - BindScopeQuery: Context.QueryParam, - BindScopeHeader: Context.Header, -} - -type BodyBinder func(i interface{}, ctx Context) error - -var bodyBinders = map[string]BodyBinder{ - MIMEApplicationForm: BindForm, - MIMEMultipartForm: BindForm, - MIMEApplicationJSON: BindJSON, - MIMEApplicationXML: BindXML, - MIMETextXML: BindXML, -} - -func RegisterScopeBinder(scope BindScope, tag string) { - tags := scopeBinders[scope] - for _, s := range tags { - if s == tag { - return - } - } - tags = append(tags, tag) - scopeBinders[scope] = tags -} - -func RegisterBodyBinder(mime string, binder BodyBinder) { - bodyBinders[mime] = binder -} - -func Bind(i interface{}, ctx Context) error { - if err := bindScope(i, ctx); err != nil { - return err - } - if err := bindBody(i, ctx); err != nil { - return err - } - return validate.Struct(i) -} - -func bindBody(i interface{}, ctx Context) error { - binder, ok := bodyBinders[ctx.ContentType()] - if !ok { - binder = bodyBinders[MIMEApplicationForm] - } - return binder(i, ctx) -} - -func bindScope(i interface{}, ctx Context) error { - t := reflect.TypeOf(i) - if t.Kind() != reflect.Ptr { - return nil - } - et := t.Elem() - if et.Kind() != reflect.Struct { - return nil - } - ev := reflect.ValueOf(i).Elem() - for j := 0; j < ev.NumField(); j++ { - fv := ev.Field(j) - ft := et.Field(j) - for scope := BindScopeURI; scope < BindScopeBody; scope++ { - err := bindScopeField(scope, fv, ft, ctx) - if err != nil { - return err - } - } - } - return nil -} - -func bindScopeField(scope BindScope, v reflect.Value, field reflect.StructField, ctx Context) error { - for _, tag := range scopeBinders[scope] { - if name, ok := field.Tag.Lookup(tag); ok { - if name == "-" { - continue - } - val := scopeGetters[scope](ctx, name) - err := bindData(v, val) - if err != nil { - return err - } - } - } - return nil -} - -func bindData(v reflect.Value, val string) error { - switch v.Kind() { - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - u, err := strconv.ParseUint(val, 0, 0) - if err != nil { - return err - } - v.SetUint(u) - return nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - i, err := strconv.ParseInt(val, 0, 0) - if err != nil { - return err - } - v.SetInt(i) - return nil - case reflect.Float32, reflect.Float64: - f, err := strconv.ParseFloat(val, 64) - if err != nil { - return err - } - v.SetFloat(f) - return nil - case reflect.Bool: - b, err := strconv.ParseBool(val) - if err != nil { - return err - } - v.SetBool(b) - return nil - case reflect.String: - v.SetString(val) - return nil - default: - return fmt.Errorf("unsupported binding type %q", v.Type().String()) - } -} diff --git a/web/binding_form.go b/web/binding_form.go deleted file mode 100644 index 964e7626..00000000 --- a/web/binding_form.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "net/url" - "reflect" -) - -func BindForm(i interface{}, ctx Context) error { - params, err := ctx.FormParams() - if err != nil { - return err - } - t := reflect.TypeOf(i) - if t.Kind() != reflect.Ptr { - return nil - } - et := t.Elem() - if et.Kind() != reflect.Struct { - return nil - } - ev := reflect.ValueOf(i).Elem() - return bindFormStruct(ev, et, params) -} - -func bindFormStruct(v reflect.Value, t reflect.Type, params url.Values) error { - for j := 0; j < t.NumField(); j++ { - ft := t.Field(j) - fv := v.Field(j) - if ft.Anonymous { - if ft.Type.Kind() != reflect.Struct { - continue - } - err := bindFormStruct(fv, ft.Type, params) - if err != nil { - return err - } - continue - } - name, ok := ft.Tag.Lookup("form") - if !ok || !fv.CanInterface() { - continue - } - values := params[name] - if len(values) == 0 { - continue - } - err := bindFormField(fv, ft.Type, values) - if err != nil { - return err - } - } - return nil -} - -func bindFormField(v reflect.Value, t reflect.Type, values []string) error { - if v.Kind() == reflect.Slice { - slice := reflect.MakeSlice(t, 0, len(values)) - defer func() { v.Set(slice) }() - et := t.Elem() - for _, value := range values { - ev := reflect.New(et).Elem() - err := bindData(ev, value) - if err != nil { - return err - } - slice = reflect.Append(slice, ev) - } - return nil - } - return bindData(v, values[0]) -} diff --git a/web/binding_form_test.go b/web/binding_form_test.go deleted file mode 100644 index eb345bd7..00000000 --- a/web/binding_form_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http/httptest" - "net/url" - "strings" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -type FormBindParamCommon struct { - A string `form:"a"` - B []string `form:"b"` -} - -type FormBindParam struct { - FormBindParamCommon - C int `form:"c"` - D []int `form:"d"` -} - -func TestBindForm(t *testing.T) { - - data := url.Values{ - "a": {"1"}, - "b": {"2", "3"}, - "c": {"4"}, - "d": {"5", "6"}, - } - target := "http://localhost:8080/1/2" - body := strings.NewReader(data.Encode()) - req := httptest.NewRequest("POST", target, body) - req.Header.Set(web.HeaderContentType, web.MIMEApplicationForm) - ctx := &MockContext{ - BaseContext: web.NewBaseContext("/:a/:b", nil, req, nil), - } - - expect := FormBindParam{ - FormBindParamCommon: FormBindParamCommon{ - A: "1", - B: []string{"2", "3"}, - }, - C: 4, - D: []int{5, 6}, - } - - var p FormBindParam - err := web.Bind(&p, ctx) - assert.Nil(t, err) - assert.Equal(t, p, expect) -} diff --git a/web/binding_json.go b/web/binding_json.go deleted file mode 100644 index 39eb4594..00000000 --- a/web/binding_json.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "bytes" - "encoding/json" -) - -func BindJSON(i interface{}, ctx Context) error { - body, err := ctx.RequestBody() - if err != nil { - return err - } - r := bytes.NewReader(body) - decoder := json.NewDecoder(r) - return decoder.Decode(i) -} diff --git a/web/binding_json_test.go b/web/binding_json_test.go deleted file mode 100644 index 975309e1..00000000 --- a/web/binding_json_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "bytes" - "encoding/json" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -type JSONBindParamCommon struct { - A string `json:"a"` - B []string `json:"b"` -} - -type JSONBindParam struct { - JSONBindParamCommon - C int `json:"c"` - D []int `json:"d"` -} - -func TestBindJSON(t *testing.T) { - - data, err := json.Marshal(map[string]interface{}{ - "a": "1", - "b": []string{"2", "3"}, - "c": 4, - "d": []int64{5, 6}, - }) - if err != nil { - return - } - target := "http://localhost:8080/1/2" - body := bytes.NewReader(data) - req := httptest.NewRequest("POST", target, body) - req.Header.Set(web.HeaderContentType, web.MIMEApplicationJSON) - ctx := &MockContext{ - BaseContext: web.NewBaseContext("/:a/:b", nil, req, nil), - } - - expect := JSONBindParam{ - JSONBindParamCommon: JSONBindParamCommon{ - A: "1", - B: []string{"2", "3"}, - }, - C: 4, - D: []int{5, 6}, - } - - var p JSONBindParam - err = web.Bind(&p, ctx) - assert.Nil(t, err) - assert.Equal(t, p, expect) -} diff --git a/web/binding_test.go b/web/binding_test.go deleted file mode 100644 index 9080c4a5..00000000 --- a/web/binding_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -type ScopeBindParam struct { - A string `uri:"a"` - B string `path:"b"` - C string `uri:"c" query:"c"` - D string `param:"d"` - E string `query:"e" header:"e"` -} - -type MockContext struct { - *web.BaseContext - uriParam map[string]string -} - -func (ctx *MockContext) PathParam(name string) string { - return ctx.uriParam[name] -} - -func TestScopeBind(t *testing.T) { - - target := "http://localhost:8080/1/2?c=3&d=4&e=5" - req := httptest.NewRequest("GET", target, nil) - req.Header.Set("e", "6") - - ctx := &MockContext{ - BaseContext: web.NewBaseContext("/:a/:b", nil, req, nil), - uriParam: map[string]string{ - "a": "1", - "b": "2", - }, - } - - expect := ScopeBindParam{ - A: "1", - B: "2", - C: "3", - D: "4", - E: "6", - } - - var p ScopeBindParam - err := web.Bind(&p, ctx) - assert.Nil(t, err) - assert.Equal(t, p, expect) -} diff --git a/web/binding_xml.go b/web/binding_xml.go deleted file mode 100644 index 965cec0e..00000000 --- a/web/binding_xml.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "bytes" - "encoding/xml" -) - -func BindXML(i interface{}, ctx Context) error { - body, err := ctx.RequestBody() - if err != nil { - return err - } - r := bytes.NewReader(body) - decoder := xml.NewDecoder(r) - return decoder.Decode(i) -} diff --git a/web/binding_xml_test.go b/web/binding_xml_test.go deleted file mode 100644 index 67ff5c78..00000000 --- a/web/binding_xml_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "bytes" - "encoding/xml" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -type XMLBindParamCommon struct { - A string `xml:"a"` - B []string `xml:"b"` -} - -type XMLBindParam struct { - XMLBindParamCommon - C int `xml:"c"` - D []int `xml:"d"` -} - -func TestBindXML(t *testing.T) { - - data, err := xml.Marshal(map[string]interface{}{ - "a": "1", - "b": []string{"2", "3"}, - "c": 4, - "d": []int64{5, 6}, - }) - if err != nil { - return - } - target := "http://localhost:8080/1/2" - body := bytes.NewReader(data) - req := httptest.NewRequest("POST", target, body) - req.Header.Set(web.HeaderContentType, web.MIMEApplicationXML) - ctx := &MockContext{ - BaseContext: web.NewBaseContext("/:a/:b", nil, req, nil), - } - - expect := XMLBindParam{ - XMLBindParamCommon: XMLBindParamCommon{ - A: "1", - B: []string{"2", "3"}, - }, - C: 4, - D: []int{5, 6}, - } - - var p XMLBindParam - err = web.Bind(&p, ctx) - assert.Nil(t, err) - assert.Equal(t, p, expect) -} diff --git a/web/constants.go b/web/constants.go deleted file mode 100644 index e50f6a12..00000000 --- a/web/constants.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -const ( - HeaderAccept = "Accept" - HeaderAcceptEncoding = "Accept-Encoding" - HeaderAllow = "Allow" - HeaderAuthorization = "Authorization" - HeaderContentDisposition = "Content-Disposition" - HeaderContentEncoding = "Content-Encoding" - HeaderContentLength = "Content-Length" - HeaderContentType = "Content-Type" - HeaderCookie = "Cookie" - HeaderSetCookie = "Set-Cookie" - HeaderIfModifiedSince = "If-Modified-Since" - HeaderLastModified = "Last-Modified" - HeaderLocation = "Location" - HeaderUpgrade = "Upgrade" - HeaderVary = "Vary" - HeaderWWWAuthenticate = "WWW-Authenticate" - HeaderXForwardedFor = "X-Forwarded-For" - HeaderXForwardedProto = "X-Forwarded-Proto" - HeaderXForwardedProtocol = "X-Forwarded-Protocol" - HeaderXForwardedSsl = "X-Forwarded-Ssl" - HeaderXUrlScheme = "X-Url-Scheme" - HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" - HeaderXRealIP = "X-Real-IP" - HeaderXRequestID = "X-Request-ID" - HeaderXCorrelationID = "X-Correlation-ID" - HeaderXRequestedWith = "X-Requested-With" - HeaderServer = "Server" - HeaderOrigin = "Origin" -) - -const ( - HeaderStrictTransportSecurity = "Strict-Transport-Security" - HeaderXContentTypeOptions = "X-Content-Type-Options" - HeaderXXSSProtection = "X-XSS-Protection" - HeaderXFrameOptions = "X-Frame-Options" - HeaderContentSecurityPolicy = "Content-Security-Policy" - HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only" - HeaderXCSRFToken = "X-CSRF-Token" - HeaderReferrerPolicy = "Referrer-Policy" -) - -const ( - HeaderAccessControlRequestMethod = "Access-Control-Request-Method" - HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers" - HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin" - HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods" - HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers" - HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" - HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers" - HeaderAccessControlMaxAge = "Access-Control-Max-Age" -) - -const ( - CharsetUTF8 = "charset=UTF-8" -) - -const ( - MIMEApplicationJSON = "application/json" - MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + CharsetUTF8 - MIMEApplicationJavaScript = "application/javascript" - MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + CharsetUTF8 - MIMEApplicationXML = "application/xml" - MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + CharsetUTF8 - MIMETextXML = "text/xml" - MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + CharsetUTF8 - MIMEApplicationForm = "application/x-www-form-urlencoded" - MIMEApplicationProtobuf = "application/protobuf" - MIMEApplicationMsgpack = "application/msgpack" - MIMETextHTML = "text/html" - MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + CharsetUTF8 - MIMETextPlain = "text/plain" - MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + CharsetUTF8 - MIMEMultipartForm = "multipart/form-data" - MIMEOctetStream = "application/octet-stream" - MIMEJsonAPI = "application/vnd.api+json" - MIMEJsonStream = "application/x-json-stream" - MIMEImagePng = "image/png" - MIMEImageJpeg = "image/jpeg" - MIMEImageGif = "image/gif" -) diff --git a/web/context-base.go b/web/context-base.go deleted file mode 100644 index 3f96ea60..00000000 --- a/web/context-base.go +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "context" - "encoding/json" - "encoding/xml" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net" - "net/http" - "net/url" - "os" - "strings" - - "github.com/go-spring/spring-base/knife" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" -) - -type BaseContext struct { - Logger *log.Logger - - w Response - r *http.Request - - path string - handler Handler - query url.Values -} - -// NewBaseContext 创建 *BaseContext 对象。 -func NewBaseContext(path string, handler Handler, r *http.Request, w Response) *BaseContext { - if ctx, cached := knife.New(r.Context()); !cached { - r = r.WithContext(ctx) - } - ret := &BaseContext{r: r, w: w, path: path, handler: handler} - ret.Logger = log.GetLogger(util.TypeName(ret)) - return ret -} - -// NativeContext 返回封装的底层上下文对象 -func (c *BaseContext) NativeContext() interface{} { - return nil -} - -// Get retrieves data from the context. -func (c *BaseContext) Get(key string) interface{} { - v, err := knife.Load(c.Context(), key) - if err != nil { - c.Logger.WithContext(c.Context()).Error(err) - return nil - } - return v -} - -// Set saves data in the context. -func (c *BaseContext) Set(key string, val interface{}) error { - return knife.Store(c.Context(), key, val) -} - -// Request returns `*http.Request`. -func (c *BaseContext) Request() *http.Request { - return c.r -} - -// SetContext sets context.Context. -func (c *BaseContext) SetContext(ctx context.Context) { - c.r = c.r.WithContext(ctx) -} - -// Context 返回 Request 绑定的 context.Context 对象 -func (c *BaseContext) Context() context.Context { - return c.r.Context() -} - -// IsTLS returns true if HTTP connection is TLS otherwise false. -func (c *BaseContext) IsTLS() bool { - return c.r.TLS != nil -} - -// IsWebSocket returns true if HTTP connection is WebSocket otherwise false. -func (c *BaseContext) IsWebSocket() bool { - upgrade := c.r.Header.Get(HeaderUpgrade) - return strings.EqualFold(upgrade, "websocket") -} - -// Scheme returns the HTTP protocol scheme, `http` or `https`. -func (c *BaseContext) Scheme() string { - if c.IsTLS() { - return "https" - } - if scheme := c.Header(HeaderXForwardedProto); scheme != "" { - return scheme - } - if scheme := c.Header(HeaderXForwardedProtocol); scheme != "" { - return scheme - } - if ssl := c.Header(HeaderXForwardedSsl); ssl == "on" { - return "https" - } - if scheme := c.Header(HeaderXUrlScheme); scheme != "" { - return scheme - } - return "http" -} - -// ClientIP implements a best effort algorithm to return the real client IP. -func (c *BaseContext) ClientIP() string { - if ip := c.Header(HeaderXForwardedFor); ip != "" { - if i := strings.Index(ip, ","); i > 0 { - return strings.TrimSpace(ip[:i]) - } - return ip - } - - if ip := c.Header(HeaderXRealIP); ip != "" { - return ip - } - host, _, _ := net.SplitHostPort(c.r.RemoteAddr) - return host -} - -// Path returns the registered path for the handler. -func (c *BaseContext) Path() string { - return c.path -} - -// Handler returns the matched handler by router. -func (c *BaseContext) Handler() Handler { - return c.handler -} - -// ContentType returns the Content-Type header of the request. -func (c *BaseContext) ContentType() string { - s := c.Header("Content-Type") - return filterFlags(s) -} - -// Header returns value from request headers. -func (c *BaseContext) Header(key string) string { - return c.r.Header.Get(key) -} - -// Cookies returns the HTTP cookies sent with the request. -func (c *BaseContext) Cookies() []*http.Cookie { - return c.r.Cookies() -} - -// Cookie returns the named cookie provided in the request. -func (c *BaseContext) Cookie(name string) (*http.Cookie, error) { - return c.r.Cookie(name) -} - -// PathParamNames returns path parameter names. -func (c *BaseContext) PathParamNames() []string { - panic(util.UnimplementedMethod) -} - -// PathParamValues returns path parameter values. -func (c *BaseContext) PathParamValues() []string { - panic(util.UnimplementedMethod) -} - -// PathParam returns path parameter by name. -func (c *BaseContext) PathParam(name string) string { - panic(util.UnimplementedMethod) -} - -// QueryString returns the URL query string. -func (c *BaseContext) QueryString() string { - return c.r.URL.RawQuery -} - -func (c *BaseContext) initQueryCache() { - if c.query == nil { - c.query = c.r.URL.Query() - } -} - -// QueryParams returns the query parameters as `url.Values`. -func (c *BaseContext) QueryParams() url.Values { - c.initQueryCache() - return c.query -} - -// QueryParam returns the query param for the provided name. -func (c *BaseContext) QueryParam(name string) string { - c.initQueryCache() - return c.query.Get(name) -} - -// FormParams returns the form parameters as `url.Values`. -func (c *BaseContext) FormParams() (url.Values, error) { - if strings.HasPrefix(c.ContentType(), MIMEMultipartForm) { - if _, err := c.MultipartForm(); err != nil { - return nil, err - } - } else { - if err := c.r.ParseForm(); err != nil { - return nil, err - } - } - return c.r.Form, nil -} - -// FormValue returns the form field value for the provided name. -func (c *BaseContext) FormValue(name string) string { - return c.r.FormValue(name) -} - -// MultipartForm returns the multipart form. -func (c *BaseContext) MultipartForm() (*multipart.Form, error) { - err := c.r.ParseMultipartForm(32 << 20 /* 32MB */) - return c.r.MultipartForm, err -} - -// FormFile returns the multipart form file for the provided name. -func (c *BaseContext) FormFile(name string) (*multipart.FileHeader, error) { - f, fh, err := c.r.FormFile(name) - if err != nil { - return nil, err - } - f.Close() - return fh, nil -} - -// SaveUploadedFile uploads the form file to specific dst. -func (c *BaseContext) SaveUploadedFile(file *multipart.FileHeader, dst string) error { - src, err := file.Open() - if err != nil { - return err - } - defer src.Close() - out, err := os.Create(dst) - if err != nil { - return err - } - defer out.Close() - _, err = io.Copy(out, src) - return err -} - -// RequestBody return stream data. -func (c *BaseContext) RequestBody() ([]byte, error) { - return ioutil.ReadAll(c.Request().Body) -} - -// Bind binds the request body into provided type `i`. -func (c *BaseContext) Bind(i interface{}) error { - return Bind(i, c) -} - -// Response returns Response. -func (c *BaseContext) Response() Response { - return c.w -} - -// SetStatus sets the HTTP response code. -func (c *BaseContext) SetStatus(code int) { - c.w.WriteHeader(code) -} - -// SetHeader is a intelligent shortcut for c.Writer.Header().Set(key, value). -func (c *BaseContext) SetHeader(key, value string) { - c.w.Header().Set(key, value) -} - -// SetContentType 设置 ResponseWriter 的 ContentType 。 -func (c *BaseContext) SetContentType(typ string) { - c.SetHeader(HeaderContentType, typ) -} - -// SetCookie adds a `Set-Cookie` header in HTTP response. -func (c *BaseContext) SetCookie(cookie *http.Cookie) { - http.SetCookie(c.Response(), cookie) -} - -// NoContent sends a response with no body and a status code. -func (c *BaseContext) NoContent(code int) { - c.SetStatus(code) -} - -// String writes the given string into the response body. -func (c *BaseContext) String(format string, values ...interface{}) { - s := fmt.Sprintf(format, values...) - c.Blob(MIMETextPlainCharsetUTF8, []byte(s)) -} - -// HTML sends an HTTP response. -func (c *BaseContext) HTML(html string) { - c.HTMLBlob([]byte(html)) -} - -// HTMLBlob sends an HTTP blob response. -func (c *BaseContext) HTMLBlob(b []byte) { - c.Blob(MIMETextHTMLCharsetUTF8, b) -} - -// JSON sends a JSON response. -func (c *BaseContext) JSON(i interface{}) { - var ( - b []byte - err error - ) - if _, pretty := c.QueryParams()["pretty"]; pretty { - b, err = json.MarshalIndent(i, "", " ") - } else { - b, err = json.Marshal(i) - } - util.Panic(err).When(err != nil) - c.Blob(MIMEApplicationJSONCharsetUTF8, b) -} - -// JSONPretty sends a pretty-print JSON. -func (c *BaseContext) JSONPretty(i interface{}, indent string) { - b, err := json.MarshalIndent(i, "", indent) - util.Panic(err).When(err != nil) - c.Blob(MIMEApplicationJSONCharsetUTF8, b) -} - -// JSONBlob sends a JSON blob response. -func (c *BaseContext) JSONBlob(b []byte) { - c.Blob(MIMEApplicationJSONCharsetUTF8, b) -} - -func (c *BaseContext) jsonPBlob(callback string, data func(http.ResponseWriter) error) error { - c.SetContentType(MIMEApplicationJavaScriptCharsetUTF8) - if _, err := c.w.Write([]byte(callback + "(")); err != nil { - return err - } - if err := data(c.w); err != nil { - return err - } - if _, err := c.w.Write([]byte(");")); err != nil { - return err - } - return nil -} - -// JSONP sends a JSONP response. -func (c *BaseContext) JSONP(callback string, i interface{}) { - err := c.jsonPBlob(callback, func(response http.ResponseWriter) error { - var ( - data []byte - err error - ) - if _, pretty := c.QueryParams()["pretty"]; pretty { - data, err = json.MarshalIndent(i, "", " ") - } else { - data, err = json.Marshal(i) - } - if err != nil { - return err - } - _, err = response.Write(data) - return err - }) - util.Panic(err).When(err != nil) -} - -// JSONPBlob sends a JSONP blob response. -func (c *BaseContext) JSONPBlob(callback string, b []byte) { - err := c.jsonPBlob(callback, func(response http.ResponseWriter) error { - _, err := response.Write(b) - return err - }) - util.Panic(err).When(err != nil) -} - -func (c *BaseContext) xml(i interface{}, indent string) error { - c.SetContentType(MIMEApplicationXMLCharsetUTF8) - enc := xml.NewEncoder(c.w) - if indent != "" { - enc.Indent("", indent) - } - if _, err := c.w.Write([]byte(xml.Header)); err != nil { - return err - } - return enc.Encode(i) -} - -// XML sends an XML response. -func (c *BaseContext) XML(i interface{}) { - indent := "" - if _, ok := c.QueryParams()["pretty"]; ok { - indent = " " - } - err := c.xml(i, indent) - util.Panic(err).When(err != nil) -} - -// XMLPretty sends a pretty-print XML. -func (c *BaseContext) XMLPretty(i interface{}, indent string) { - err := c.xml(i, indent) - util.Panic(err).When(err != nil) -} - -// XMLBlob sends an XML blob response. -func (c *BaseContext) XMLBlob(b []byte) { - c.SetContentType(MIMEApplicationXMLCharsetUTF8) - _, err := c.w.Write([]byte(xml.Header)) - util.Panic(err).When(err != nil) - _, err = c.w.Write(b) - util.Panic(err).When(err != nil) -} - -// Blob sends a blob response with content type. -func (c *BaseContext) Blob(contentType string, b []byte) { - c.SetContentType(contentType) - _, err := c.w.Write(b) - util.Panic(err).When(err != nil) -} - -// File sends a response with the content of the file. -func (c *BaseContext) File(file string) { - http.ServeFile(c.w, c.r, file) -} - -func (c *BaseContext) contentDisposition(file, name, dispositionType string) { - s := fmt.Sprintf("%s; filename=%q", dispositionType, name) - c.SetHeader(HeaderContentDisposition, s) - c.File(file) -} - -// Attachment sends a response as attachment -func (c *BaseContext) Attachment(file string, name string) { - c.contentDisposition(file, name, "attachment") -} - -// Inline sends a response as inline -func (c *BaseContext) Inline(file string, name string) { - c.contentDisposition(file, name, "inline") -} - -// Redirect redirects the request to a provided URL with status code. -func (c *BaseContext) Redirect(code int, url string) { - if (code < http.StatusMultipleChoices || code > http.StatusPermanentRedirect) && code != http.StatusCreated { - panic(fmt.Sprintf("cann't redirect with status code %d", code)) - } - http.Redirect(c.w, c.r, url, code) -} - -// SSEvent writes a Server-Sent Event into the body stream. -func (c *BaseContext) SSEvent(name string, message interface{}) { - panic(util.UnimplementedMethod) -} diff --git a/web/context-base_test.go b/web/context-base_test.go deleted file mode 100644 index 0a032a1e..00000000 --- a/web/context-base_test.go +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test diff --git a/web/context.go b/web/context.go deleted file mode 100644 index 78c67e9a..00000000 --- a/web/context.go +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "bytes" - "context" - "fmt" - "mime/multipart" - "net/http" - "net/url" - "strings" -) - -// ContextKey Context 和 NativeContext 相互转换的 Key -const ContextKey = "@@WebCtx@@" - -// FuncErrorHandler func 形式定义错误处理接口 -type FuncErrorHandler func(ctx Context, err *HttpError) - -func (f FuncErrorHandler) Invoke(ctx Context, err *HttpError) { - f(ctx, err) -} - -// HttpError represents an error that occurred while handling a request. -type HttpError struct { - Code int // HTTP 错误码 - Message string // 自定义错误消息 - Internal interface{} // 保存的原始异常 -} - -// NewHttpError creates a new HttpError instance. -func NewHttpError(code int, message ...string) *HttpError { - e := &HttpError{Code: code} - if len(message) > 0 { - e.Message = message[0] - } else { - e.Message = http.StatusText(code) - } - return e -} - -// Error makes it compatible with `error` interface. -func (e *HttpError) Error() string { - if e.Internal == nil { - return fmt.Sprintf("code=%d, message=%s", e.Code, e.Message) - } - return fmt.Sprintf("code=%d, message=%s, error=%v", e.Code, e.Message, e.Internal) -} - -// SetInternal sets error to HTTPError.Internal -func (e *HttpError) SetInternal(err error) *HttpError { - e.Internal = err - return e -} - -type Response interface { - http.ResponseWriter - Get() http.ResponseWriter - Set(w http.ResponseWriter) -} - -type SimpleResponse struct { - http.ResponseWriter -} - -func (resp *SimpleResponse) Get() http.ResponseWriter { - return resp.ResponseWriter -} - -func (resp *SimpleResponse) Set(w http.ResponseWriter) { - resp.ResponseWriter = w -} - -// Context 封装 *http.Request 和 http.ResponseWriter 对象,简化操作接口。 -type Context interface { - - // NativeContext 返回封装的底层上下文对象 - NativeContext() interface{} - - // Get retrieves data from the context. - Get(key string) interface{} - - // Set saves data in the context. - Set(key string, val interface{}) error - - ///////////////////////////////////////// - // Request Part - - // Request returns `*http.Request`. - Request() *http.Request - - // Context 返回 Request 绑定的 context.Context 对象 - Context() context.Context - - // SetContext sets context.Context. - SetContext(ctx context.Context) - - // IsTLS returns true if HTTP connection is TLS otherwise false. - IsTLS() bool - - // IsWebSocket returns true if HTTP connection is WebSocket otherwise false. - IsWebSocket() bool - - // Scheme returns the HTTP protocol scheme, `http` or `https`. - Scheme() string - - // ClientIP implements a best effort algorithm to return the real client IP, - // it parses X-Real-IP and X-Forwarded-For in order to work properly with - // reverse-proxies such us: nginx or haproxy. Use X-Forwarded-For before - // X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP. - ClientIP() string - - // Path returns the registered path for the handler. - Path() string - - // Handler returns the matched handler by router. - Handler() Handler - - // ContentType returns the Content-Type header of the request. - ContentType() string - - // Header returns value from request headers. - Header(key string) string - - // Cookies returns the HTTP cookies sent with the request. - Cookies() []*http.Cookie - - // Cookie returns the named cookie provided in the request. - Cookie(name string) (*http.Cookie, error) - - // PathParamNames returns path parameter names. - PathParamNames() []string - - // PathParamValues returns path parameter values. - PathParamValues() []string - - // PathParam returns path parameter by name. - PathParam(name string) string - - // QueryString returns the URL query string. - QueryString() string - - // QueryParams returns the query parameters as `url.Values`. - QueryParams() url.Values - - // QueryParam returns the query param for the provided name. - QueryParam(name string) string - - // FormParams returns the form parameters as `url.Values`. - FormParams() (url.Values, error) - - // FormValue returns the form field value for the provided name. - FormValue(name string) string - - // MultipartForm returns the multipart form. - MultipartForm() (*multipart.Form, error) - - // FormFile returns the multipart form file for the provided name. - FormFile(name string) (*multipart.FileHeader, error) - - // SaveUploadedFile uploads the form file to specific dst. - SaveUploadedFile(file *multipart.FileHeader, dst string) error - - // RequestBody return stream data. - RequestBody() ([]byte, error) - - // Bind binds the request body into provided type `i`. The default binder - // does it based on Content-Type header. - Bind(i interface{}) error - - ///////////////////////////////////////// - // Response Part - - // Response returns Response. - Response() Response - - // SetStatus sets the HTTP response code. - SetStatus(code int) - - // SetHeader is a intelligent shortcut for c.Writer.Header().Set(key, value). - // It writes a header in the response. - // If value == "", this method removes the header `c.Writer.Header().Del(key)` - SetHeader(key, value string) - - // SetContentType 设置 ResponseWriter 的 ContentType 。 - SetContentType(typ string) - - // SetCookie adds a `Set-Cookie` header in HTTP response. - SetCookie(cookie *http.Cookie) - - // NoContent sends a response with no body and a status code. Maybe panic. - NoContent(code int) - - // String writes the given string into the response body. Maybe panic. - String(format string, values ...interface{}) - - // HTML sends an HTTP response. Maybe panic. - HTML(html string) - - // HTMLBlob sends an HTTP blob response. Maybe panic. - HTMLBlob(b []byte) - - // JSON sends a JSON response. Maybe panic. - JSON(i interface{}) - - // JSONPretty sends a pretty-print JSON. Maybe panic. - JSONPretty(i interface{}, indent string) - - // JSONBlob sends a JSON blob response. Maybe panic. - JSONBlob(b []byte) - - // JSONP sends a JSONP response. It uses `callback` - // to construct the JSONP payload. Maybe panic. - JSONP(callback string, i interface{}) - - // JSONPBlob sends a JSONP blob response. It uses - // `callback` to construct the JSONP payload. Maybe panic. - JSONPBlob(callback string, b []byte) - - // XML sends an XML response. Maybe panic. - XML(i interface{}) - - // XMLPretty sends a pretty-print XML. Maybe panic. - XMLPretty(i interface{}, indent string) - - // XMLBlob sends an XML blob response. Maybe panic. - XMLBlob(b []byte) - - // Blob sends a blob response and content type. Maybe panic. - Blob(contentType string, b []byte) - - // File sends a response with the content of the file. Maybe panic. - File(file string) - - // Attachment sends a response as attachment, prompting client to - // save the file. Maybe panic. - Attachment(file string, name string) - - // Inline sends a response as inline, opening the file in the browser. Maybe panic. - Inline(file string, name string) - - // Redirect redirects the request to a provided URL with status code. Maybe panic. - Redirect(code int, url string) - - // SSEvent writes a Server-Sent Event into the body stream. Maybe panic. - SSEvent(name string, message interface{}) -} - -// BufferedResponseWriter http.ResponseWriter 的一种增强型实现. -type BufferedResponseWriter struct { - http.ResponseWriter - buf bytes.Buffer - size int - status int -} - -// Status Returns the HTTP response status code of the current request. -func (w *BufferedResponseWriter) Status() int { - return w.status -} - -// Size Returns the number of bytes already written into the response http body. -func (w *BufferedResponseWriter) Size() int { - return w.size -} - -// Body 返回发送给客户端的数据,当前仅支持 MIMEApplicationJSON 格式. -func (w *BufferedResponseWriter) Body() string { - return w.buf.String() -} - -func filterFlags(content string) string { - for i, char := range strings.ToLower(content) { - if char == ' ' || char == ';' { - return content[:i] - } - } - return content -} - -func canPrintResponse(response http.ResponseWriter) bool { - switch filterFlags(response.Header().Get(HeaderContentType)) { - case MIMEApplicationJSON, MIMEApplicationXML, MIMETextPlain, MIMETextXML: - return true - case MIMEApplicationJavaScript, MIMETextHTML: - return true - } - return false -} - -func (w *BufferedResponseWriter) WriteHeader(code int) { - w.ResponseWriter.WriteHeader(code) - w.status = code -} - -func (w *BufferedResponseWriter) Write(data []byte) (n int, err error) { - if n, err = w.ResponseWriter.Write(data); err == nil && n > 0 { - if canPrintResponse(w.ResponseWriter) { - w.buf.Write(data[:n]) - w.size += n - } - } - return -} diff --git a/web/filter.go b/web/filter.go deleted file mode 100644 index ac2801a8..00000000 --- a/web/filter.go +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "regexp" -) - -// Filter 过滤器接口,Invoke 通过 chain.Next() 驱动链条向后执行。 -type Filter interface { - Invoke(ctx Context, chain FilterChain) -} - -// handlerFilter 包装 Web 处理接口的过滤器 -type handlerFilter struct { - fn Handler -} - -// HandlerFilter 把 Web 处理接口转换成过滤器。 -func HandlerFilter(fn Handler) Filter { - return &handlerFilter{fn: fn} -} - -func (h *handlerFilter) Invoke(ctx Context, _ FilterChain) { - h.fn.Invoke(ctx) -} - -type FilterFunc func(ctx Context, chain FilterChain) - -// funcFilter 封装 func 形式的过滤器。 -type funcFilter struct { - f FilterFunc -} - -// FuncFilter 封装 func 形式的过滤器。 -func FuncFilter(f FilterFunc) *funcFilter { - return &funcFilter{f: f} -} - -func (f *funcFilter) Invoke(ctx Context, chain FilterChain) { - f.f(ctx, chain) -} - -// URLPatterns 返回带 URLPatterns 信息的过滤器。 -func (f *funcFilter) URLPatterns(s ...string) *urlPatternFilter { - return URLPatternFilter(f, s...) -} - -// urlPatternFilter 封装带 URLPatterns 信息的过滤器。 -type urlPatternFilter struct { - Filter - s []string -} - -// URLPatternFilter 封装带 URLPatterns 信息的过滤器。 -func URLPatternFilter(f Filter, s ...string) *urlPatternFilter { - return &urlPatternFilter{Filter: f, s: s} -} - -func (f *urlPatternFilter) URLPatterns() []string { - return f.s -} - -type NextOperation int - -const ( - Recursive NextOperation = 0 - Iterative = 1 -) - -// FilterChain 过滤器链条接口 -type FilterChain interface { - Next(ctx Context, op NextOperation) -} - -// filterChain 默认的过滤器链条 -type filterChain struct { - filters []Filter - index int - lazyNext bool -} - -// NewFilterChain filterChain 的构造函数 -func NewFilterChain(filters []Filter) FilterChain { - return &filterChain{filters: filters} -} - -func (chain *filterChain) Next(ctx Context, op NextOperation) { - if op == Iterative { - chain.lazyNext = true - return - } - if chain.index < len(chain.filters) { - chain.next(ctx) - for chain.lazyNext && chain.index < len(chain.filters) { - chain.lazyNext = false - chain.next(ctx) - } - } -} - -func (chain *filterChain) next(ctx Context) { - f := chain.filters[chain.index] - chain.index++ - f.Invoke(ctx, chain) -} - -type urlPatterns struct { - m map[*regexp.Regexp][]Filter -} - -func (p *urlPatterns) Get(path string) []Filter { - for pattern, filters := range p.m { - if pattern.MatchString(path) { - return filters - } - } - return nil -} - -// URLPatterns 根据 Filter 的 URL 匹配表达式进行分组。 -func URLPatterns(filters []Filter) (*urlPatterns, error) { - - filterMap := make(map[string][]Filter) - for _, filter := range filters { - var patterns []string - if p, ok := filter.(interface{ URLPatterns() []string }); ok { - patterns = p.URLPatterns() - } else { - patterns = []string{"/*"} - } - for _, pattern := range patterns { - filterMap[pattern] = append(filterMap[pattern], filter) - } - } - - filterPatterns := make(map[*regexp.Regexp][]Filter) - for pattern, filter := range filterMap { - exp, err := regexp.Compile(pattern) - if err != nil { - return nil, err - } - filterPatterns[exp] = filter - } - return &urlPatterns{m: filterPatterns}, nil -} diff --git a/web/filter_test.go b/web/filter_test.go deleted file mode 100644 index 2228d32f..00000000 --- a/web/filter_test.go +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "fmt" - "net/http" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/web" -) - -func TestFuncFilter(t *testing.T) { - - funcFilter := web.FuncFilter(func(ctx web.Context, chain web.FilterChain) { - fmt.Println("@FuncFilter") - chain.Next(ctx, web.Recursive) - }).URLPatterns("/func") - - handlerFilter := web.HandlerFilter(web.FUNC(func(ctx web.Context) { - fmt.Println("@HandlerFilter") - })) - - web.NewFilterChain([]web.Filter{funcFilter, handlerFilter}).Next(nil, web.Recursive) -} - -type Counter struct{} - -func (ctr *Counter) ServeHTTP(http.ResponseWriter, *http.Request) {} - -func TestWrapH(t *testing.T) { - - c := &Counter{} - fmt.Println(util.FileLine(c.ServeHTTP)) - - h := &Counter{} - fmt.Println(util.FileLine(h.ServeHTTP)) - - fmt.Println(web.WrapH(&Counter{}).FileLine()) -} - -func TestFilterChain_Continue(t *testing.T) { - - var stack []int - var result [][]int - - filterImpl := func(i int) web.Filter { - return web.FuncFilter(func(ctx web.Context, chain web.FilterChain) { - stack = append(stack, i) - result = append(result, stack) - defer func() { - stack = append([]int{}, stack[:len(stack)-1]...) - result = append(result, stack) - }() - if i > 5 { - return - } - if i%2 == 0 { - chain.Next(ctx, web.Recursive) - } else { - chain.Next(ctx, web.Iterative) - } - }) - } - - web.NewFilterChain([]web.Filter{ - filterImpl(1), - filterImpl(2), - filterImpl(3), - filterImpl(4), - filterImpl(5), - filterImpl(6), - filterImpl(7), - }).Next(nil, web.Recursive) - - assert.Equal(t, result, [][]int{ - {1}, - {}, - {2}, - {2, 3}, - {2}, - {2, 4}, - {2, 4, 5}, - {2, 4}, - {2, 4, 6}, - {2, 4}, - {2}, - {}, - }) -} - -func TestFilterChain_Next(t *testing.T) { - - var stack []int - var result [][]int - - filterImpl := func(i int) web.Filter { - return web.FuncFilter(func(ctx web.Context, chain web.FilterChain) { - stack = append(stack, i) - result = append(result, stack) - defer func() { - stack = append([]int{}, stack[:len(stack)-1]...) - result = append(result, stack) - }() - if i > 5 { - return - } - chain.Next(ctx, web.Recursive) - }) - } - - web.NewFilterChain([]web.Filter{ - filterImpl(1), - filterImpl(2), - filterImpl(3), - filterImpl(4), - filterImpl(5), - filterImpl(6), - filterImpl(7), - }).Next(nil, web.Recursive) - - assert.Equal(t, result, [][]int{ - {1}, - {1, 2}, - {1, 2, 3}, - {1, 2, 3, 4}, - {1, 2, 3, 4, 5}, - {1, 2, 3, 4, 5, 6}, - {1, 2, 3, 4, 5}, - {1, 2, 3, 4}, - {1, 2, 3}, - {1, 2}, - {1}, - {}, - }) -} diff --git a/web/gzip.go b/web/gzip.go deleted file mode 100644 index bd288c81..00000000 --- a/web/gzip.go +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "compress/gzip" - "io/ioutil" - "net/http" - "path/filepath" - "regexp" - "strconv" - "strings" - "sync" -) - -type GzipFilter struct { - pool *sync.Pool - ExcludedExtensions []string - ExcludedPaths []string - ExcludedPathsRegexes []*regexp.Regexp -} - -// NewGzipFilter The compression level can be gzip.DefaultCompression, -// gzip.NoCompression, gzip.HuffmanOnly or any integer value between -// gip.BestSpeed and gzip.BestCompression inclusive. -func NewGzipFilter(level int) (Filter, error) { - _, err := gzip.NewWriterLevel(ioutil.Discard, level) - if err != nil { - return nil, err - } - return &GzipFilter{ - pool: &sync.Pool{ - New: func() interface{} { - w, _ := gzip.NewWriterLevel(ioutil.Discard, level) - return w - }, - }, - }, nil -} - -func (f *GzipFilter) Invoke(ctx Context, chain FilterChain) { - - if !f.shouldCompress(ctx.Request()) { - chain.Next(ctx, Iterative) - return - } - - w := f.pool.Get().(*gzip.Writer) - defer f.pool.Put(w) - - defer w.Reset(ioutil.Discard) - w.Reset(ctx.Response().Get()) - - ctx.SetHeader(HeaderContentEncoding, "gzip") - ctx.SetHeader(HeaderVary, HeaderAcceptEncoding) - - zw := &gzipWriter{ctx.Response().Get(), w, 0} - ctx.Response().Set(zw) - defer func() { - w.Close() - ctx.SetHeader(HeaderContentLength, strconv.Itoa(zw.size)) - }() - - chain.Next(ctx, Recursive) -} - -func (f *GzipFilter) shouldCompress(req *http.Request) bool { - if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") || - strings.Contains(req.Header.Get("Connection"), "Upgrade") || - strings.Contains(req.Header.Get("Accept"), "text/event-stream") { - return false - } - ext := filepath.Ext(req.URL.Path) - for _, s := range f.ExcludedExtensions { - if s == ext { - return false - } - } - for _, s := range f.ExcludedPaths { - if strings.HasPrefix(req.URL.Path, s) { - return false - } - } - for _, r := range f.ExcludedPathsRegexes { - if r.MatchString(req.URL.Path) { - return false - } - } - return true -} - -type gzipWriter struct { - http.ResponseWriter - writer *gzip.Writer - size int -} - -func (g *gzipWriter) WriteHeader(code int) { - g.Header().Del("Content-Length") - g.ResponseWriter.WriteHeader(code) -} - -func (g *gzipWriter) WriteString(s string) (int, error) { - g.Header().Del("Content-Length") - n, err := g.writer.Write([]byte(s)) - g.size += n - return n, err -} - -func (g *gzipWriter) Write(data []byte) (int, error) { - g.Header().Del("Content-Length") - n, err := g.writer.Write(data) - g.size += n - return n, err -} diff --git a/web/gzip_test.go b/web/gzip_test.go deleted file mode 100644 index 73e04f6a..00000000 --- a/web/gzip_test.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-core/web" -) - -func TestGzipFilter(t *testing.T) { - filter, _ := web.NewGzipFilter(5) - r := httptest.NewRequest("GET", "http://127.0.0.1/test", nil) - r.Header.Set(web.HeaderAcceptEncoding, "gzip") - w := httptest.NewRecorder() - ctx := web.NewBaseContext("", nil, r, &web.SimpleResponse{ResponseWriter: w}) - web.NewFilterChain([]web.Filter{filter}).Next(ctx, web.Recursive) -} diff --git a/web/i18n/i18n.go b/web/i18n/i18n.go deleted file mode 100644 index b1815c95..00000000 --- a/web/i18n/i18n.go +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package i18n - -import ( - "context" - "errors" - "os" - "path/filepath" - "strings" - - "github.com/go-spring/spring-base/code" - "github.com/go-spring/spring-base/knife" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/conf" -) - -// Languages 语言缩写代码表。 -var _ = []string{ - "af", //南非语 - "af-ZA", //南非语 - "ar", //阿拉伯语 - "ar-AE", //阿拉伯语(阿联酋) - "ar-BH", //阿拉伯语(巴林) - "ar-DZ", //阿拉伯语(阿尔及利亚) - "ar-EG", //阿拉伯语(埃及) - "ar-IQ", //阿拉伯语(伊拉克) - "ar-JO", //阿拉伯语(约旦) - "ar-KW", //阿拉伯语(科威特) - "ar-LB", //阿拉伯语(黎巴嫩) - "ar-LY", //阿拉伯语(利比亚) - "ar-MA", //阿拉伯语(摩洛哥) - "ar-OM", //阿拉伯语(阿曼) - "ar-QA", //阿拉伯语(卡塔尔) - "ar-SA", //阿拉伯语(沙特阿拉伯) - "ar-SY", //阿拉伯语(叙利亚) - "ar-TN", //阿拉伯语(突尼斯) - "ar-YE", //阿拉伯语(也门) - "az", //阿塞拜疆语 - "az-AZ", //阿塞拜疆语(拉丁文) - "az-AZ", //阿塞拜疆语(西里尔文) - "be", //比利时语 - "be-BY", //比利时语 - "bg", //保加利亚语 - "bg-BG", //保加利亚语 - "bs-BA", //波斯尼亚语(拉丁文,波斯尼亚和黑塞哥维那) - "ca", //加泰隆语 - "ca-ES", //加泰隆语 - "cs", //捷克语 - "cs-CZ", //捷克语 - "cy", //威尔士语 - "cy-GB", //威尔士语 - "da", //丹麦语 - "da-DK", //丹麦语 - "de", //德语 - "de-AT", //德语(奥地利) - "de-CH", //德语(瑞士) - "de-DE", //德语(德国) - "de-LI", //德语(列支敦士登) - "de-LU", //德语(卢森堡) - "dv", //第维埃语 - "dv-MV", //第维埃语 - "el", //希腊语 - "el-GR", //希腊语 - "en", //英语 - "en-AU", //英语(澳大利亚) - "en-BZ", //英语(伯利兹) - "en-CA", //英语(加拿大) - "en-CB", //英语(加勒比海) - "en-GB", //英语(英国) - "en-IE", //英语(爱尔兰) - "en-JM", //英语(牙买加) - "en-NZ", //英语(新西兰) - "en-PH", //英语(菲律宾) - "en-TT", //英语(特立尼达) - "en-US", //英语(美国) - "en-ZA", //英语(南非) - "en-ZW", //英语(津巴布韦) - "eo", //世界语 - "es", //西班牙语 - "es-AR", //西班牙语(阿根廷) - "es-BO", //西班牙语(玻利维亚) - "es-CL", //西班牙语(智利) - "es-CO", //西班牙语(哥伦比亚) - "es-CR", //西班牙语(哥斯达黎加) - "es-DO", //西班牙语(多米尼加共和国) - "es-EC", //西班牙语(厄瓜多尔) - "es-ES", //西班牙语(传统) - "es-ES", //西班牙语(国际) - "es-GT", //西班牙语(危地马拉) - "es-HN", //西班牙语(洪都拉斯) - "es-MX", //西班牙语(墨西哥) - "es-NI", //西班牙语(尼加拉瓜) - "es-PA", //西班牙语(巴拿马) - "es-PE", //西班牙语(秘鲁) - "es-PR", //西班牙语(波多黎各(美)) - "es-PY", //西班牙语(巴拉圭) - "es-SV", //西班牙语(萨尔瓦多) - "es-UY", //西班牙语(乌拉圭) - "es-VE", //西班牙语(委内瑞拉) - "et", //爱沙尼亚语 - "et-EE", //爱沙尼亚语 - "eu", //巴士克语 - "eu-ES", //巴士克语 - "fa", //法斯语 - "fa-IR", //法斯语 - "fi", //芬兰语 - "fi-FI", //芬兰语 - "fo", //法罗语 - "fo-FO", //法罗语 - "fr", //法语 - "fr-BE", //法语(比利时) - "fr-CA", //法语(加拿大) - "fr-CH", //法语(瑞士) - "fr-FR", //法语(法国) - "fr-LU", //法语(卢森堡) - "fr-MC", //法语(摩纳哥) - "gl", //加里西亚语 - "gl-ES", //加里西亚语 - "gu", //古吉拉特语 - "gu-IN", //古吉拉特语 - "he", //希伯来语 - "he-IL", //希伯来语 - "hi", //印地语 - "hi-IN", //印地语 - "hr", //克罗地亚语 - "hr-BA", //克罗地亚语(波斯尼亚和黑塞哥维那) - "hr-HR", //克罗地亚语 - "hu", //匈牙利语 - "hu-HU", //匈牙利语 - "hy", //亚美尼亚语 - "hy-AM", //亚美尼亚语 - "id", //印度尼西亚语 - "id-ID", //印度尼西亚语 - "is", //冰岛语 - "is-IS", //冰岛语 - "it", //意大利语 - "it-CH", //意大利语(瑞士) - "it-IT", //意大利语(意大利) - "ja", //日语 - "ja-JP", //日语 - "ka", //格鲁吉亚语 - "ka-GE", //格鲁吉亚语 - "kk", //哈萨克语 - "kk-KZ", //哈萨克语 - "kn", //卡纳拉语 - "kn-IN", //卡纳拉语 - "ko", //朝鲜语 - "ko-KR", //朝鲜语 - "kok", //孔卡尼语 - "kok-IN", //孔卡尼语 - "ky", //吉尔吉斯语 - "ky-KG", //吉尔吉斯语(西里尔文) - "lt", //立陶宛语 - "lt-LT", //立陶宛语 - "lv", //拉脱维亚语 - "lv-LV", //拉脱维亚语 - "mi", //毛利语 - "mi-NZ", //毛利语 - "mk", //马其顿语 - "mk-MK", //马其顿语(FYROM) - "mn", //蒙古语 - "mn-MN", //蒙古语(西里尔文) - "mr", //马拉地语 - "mr-IN", //马拉地语 - "ms", //马来语 - "ms-BN", //马来语(文莱达鲁萨兰) - "ms-MY", //马来语(马来西亚) - "mt", //马耳他语 - "mt-MT", //马耳他语 - "nb", //挪威语(伯克梅尔) - "nb-NO", //挪威语(伯克梅尔)(挪威) - "nl", //荷兰语 - "nl-BE", //荷兰语(比利时) - "nl-NL", //荷兰语(荷兰) - "nn-NO", //挪威语(尼诺斯克)(挪威) - "ns", //北梭托语 - "ns-ZA", //北梭托语 - "pa", //旁遮普语 - "pa-IN", //旁遮普语 - "pl", //波兰语 - "pl-PL", //波兰语 - "pt", //葡萄牙语 - "pt-BR", //葡萄牙语(巴西) - "pt-PT", //葡萄牙语(葡萄牙) - "qu", //克丘亚语 - "qu-BO", //克丘亚语(玻利维亚) - "qu-EC", //克丘亚语(厄瓜多尔) - "qu-PE", //克丘亚语(秘鲁) - "ro", //罗马尼亚语 - "ro-RO", //罗马尼亚语 - "ru", //俄语 - "ru-RU", //俄语 - "sa", //梵文 - "sa-IN", //梵文 - "se", //北萨摩斯语 - "se-FI", //北萨摩斯语(芬兰) - "se-FI", //斯科特萨摩斯语(芬兰) - "se-FI", //伊那里萨摩斯语(芬兰) - "se-NO", //北萨摩斯语(挪威) - "se-NO", //律勒欧萨摩斯语(挪威) - "se-NO", //南萨摩斯语(挪威) - "se-SE", //北萨摩斯语(瑞典) - "se-SE", //律勒欧萨摩斯语(瑞典) - "se-SE", //南萨摩斯语(瑞典) - "sk", //斯洛伐克语 - "sk-SK", //斯洛伐克语 - "sl", //斯洛文尼亚语 - "sl-SI", //斯洛文尼亚语 - "sq", //阿尔巴尼亚语 - "sq-AL", //阿尔巴尼亚语 - "sr-BA", //塞尔维亚语(拉丁文,波斯尼亚和黑塞哥维那) - "sr-BA", //塞尔维亚语(西里尔文,波斯尼亚和黑塞哥维那) - "sr-SP", //塞尔维亚(拉丁) - "sr-SP", //塞尔维亚(西里尔文) - "sv", //瑞典语 - "sv-FI", //瑞典语(芬兰) - "sv-SE", //瑞典语 - "sw", //斯瓦希里语 - "sw-KE", //斯瓦希里语 - "syr", //叙利亚语 - "syr-SY", //叙利亚语 - "ta", //泰米尔语 - "ta-IN", //泰米尔语 - "te", //泰卢固语 - "te-IN", //泰卢固语 - "th", //泰语 - "th-TH", //泰语 - "tl", //塔加路语 - "tl-PH", //塔加路语(菲律宾) - "tn", //茨瓦纳语 - "tn-ZA", //茨瓦纳语 - "tr", //土耳其语 - "tr-TR", //土耳其语 - "ts", //宗加语 - "tt", //鞑靼语 - "tt-RU", //鞑靼语 - "uk", //乌克兰语 - "uk-UA", //乌克兰语 - "ur", //乌都语 - "ur-PK", //乌都语 - "uz", //乌兹别克语 - "uz-UZ", //乌兹别克语(拉丁文) - "uz-UZ", //乌兹别克语(西里尔文) - "vi", //越南语 - "vi-VN", //越南语 - "xh", //班图语 - "xh-ZA", //班图语 - "zh", //中文 - "zh-CN", //中文(简体) - "zh-HK", //中文(香港) - "zh-MO", //中文(澳门) - "zh-SG", //中文(新加坡) - "zh-TW", //中文(繁体) - "zu", //祖鲁语 - "zu-ZA", //祖鲁语 -} - -// defaultLanguage 设置默认语言。 -var defaultLanguage = "zh-CN" - -// languageMap 语言配置表。 -var languageMap = make(map[string]*conf.Properties) - -// Register 注册语言配置表。 -func Register(language string, data *conf.Properties) error { - if _, ok := languageMap[language]; ok { - return errors.New("duplicate language") - } - languageMap[language] = data - return nil -} - -// LoadLanguage 加载语言文件。 -func LoadLanguage(filename string) error { - fileInfo, err := os.Stat(filename) - if err != nil { - return err - } - if fileInfo.IsDir() { - return loadLanguageFromDir(filename) - } - return loadLanguageFromFile(filename) -} - -func loadLanguageFromDir(dir string) error { - dirNames, err := util.ReadDirNames(dir) - if err != nil { - return err - } - p := conf.New() - var fileInfo os.FileInfo - for _, name := range dirNames { - filename := filepath.Join(dir, name) - fileInfo, err = os.Stat(filename) - if err != nil { - return err - } - if fileInfo.IsDir() { - continue - } - if err = p.Load(filename); err != nil { - return err - } - } - language := filepath.Base(dir) - return Register(language, p) -} - -func loadLanguageFromFile(file string) error { - p, err := conf.Load(file) - if err != nil { - return err - } - filename := filepath.Base(file) - language := strings.Split(filename, ".")[0] - return Register(language, p) -} - -const languageKey = "::language::" - -// SetDefaultLanguage 设置默认语言。 -func SetDefaultLanguage(language string) { - defaultLanguage = language -} - -// SetLanguage 设置上下文语言。 -func SetLanguage(ctx context.Context, language string) error { - return knife.Store(ctx, languageKey, language) -} - -// Get 获取语言对应的配置项,从 context.Context 中获取上下文语言。 -func Get(ctx context.Context, key string) string { - - language := defaultLanguage - v, err := knife.Load(ctx, languageKey) - if err == nil { - str, ok := v.(string) - if ok { - language = str - } - } - - if m, ok := languageMap[language]; ok && m != nil { - if m.Has(key) { - return m.Get(key) - } - } - - ss := strings.SplitN(language, "-", 2) - if len(ss) < 2 { - return "" - } - - if m, ok := languageMap[ss[0]]; ok && m != nil { - return m.Get(key) - } - return "" -} - -// Resolve 获取语言对应的配置项,从 context.Context 中获取上下文语言。 -func Resolve(ctx context.Context, s string) (string, error) { - return resolveString(ctx, s) -} - -func resolveString(ctx context.Context, s string) (string, error) { - - n := len(s) - count := 0 - found := false - start, end := -1, -1 - - for i := 0; i < len(s); i++ { - switch s[i] { - case '{': - if i < n-1 { - if s[i+1] == '{' { - if count == 0 { - start = i - } - count++ - } - } - case '}': - if i < n-1 { - if s[i+1] == '}' { - count-- - if count == 0 { - found = true - i++ - end = i - } - } - } - } - if found { - break - } - } - - if start < 0 || end < 0 { - return s, nil - } - - if count > 0 { - return "", util.Errorf(code.FileLine(), "%s 语法错误", s) - } - - key := strings.TrimSpace(s[start+2 : end-1]) - s1 := Get(ctx, key) - - s2, err := resolveString(ctx, s[end+1:]) - if err != nil { - return "", err - } - - return s[:start] + s1 + s2, nil -} diff --git a/web/i18n/i18n_test.go b/web/i18n/i18n_test.go deleted file mode 100644 index adedb37d..00000000 --- a/web/i18n/i18n_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package i18n_test - -import ( - "context" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/knife" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/web/i18n" -) - -func init() { - - p, err := conf.Map(map[string]interface{}{ - "message": "这是一条消息", - }) - err = i18n.Register("zh-CN", p) - util.Panic(err).When(err != nil) - - err = i18n.LoadLanguage("testdata/zh.properties") - util.Panic(err).When(err != nil) - - p, err = conf.Map(map[string]interface{}{ - "message": "this is a message", - }) - err = i18n.Register("en-US", p) - util.Panic(err).When(err != nil) - - err = i18n.LoadLanguage("testdata/en/") - util.Panic(err).When(err != nil) -} - -func TestGet(t *testing.T) { - - ctx, _ := knife.New(context.Background()) - assert.Equal(t, i18n.Get(ctx, "message"), "这是一条消息") - - ctx, _ = knife.New(context.Background()) - err := i18n.SetLanguage(ctx, "en-US") - assert.Nil(t, err) - assert.Equal(t, i18n.Get(ctx, "message"), "this is a message") - - ctx, _ = knife.New(context.Background()) - err = i18n.SetLanguage(ctx, "en") - assert.Nil(t, err) - assert.Equal(t, i18n.Get(ctx, "hello"), "hello world!") - - ctx, _ = knife.New(context.Background()) - err = i18n.SetLanguage(ctx, "fr") - assert.Nil(t, err) - assert.Equal(t, i18n.Get(ctx, "message"), "") - - ctx, _ = knife.New(context.Background()) - err = i18n.SetLanguage(ctx, "zh-CN") - assert.Nil(t, err) - assert.Equal(t, i18n.Get(ctx, "hello"), "你好,世界!") -} - -func TestResolve(t *testing.T) { - - ctx, _ := knife.New(context.Background()) - err := i18n.SetLanguage(ctx, "zh-CN") - assert.Nil(t, err) - - str, err := i18n.Resolve(ctx, "@@ {{hello}} @@") - assert.Nil(t, err) - assert.Equal(t, str, "@@ 你好,世界! @@") - - str, err = i18n.Resolve(ctx, "@@ {{hello}} {{hello} @@") - assert.Nil(t, err) - assert.Equal(t, str, "@@ 你好,世界! {{hello} @@") - - str, err = i18n.Resolve(ctx, "@@ {{hello}} {{hello} {hello}} @@") - assert.Nil(t, err) - assert.Equal(t, str, "@@ 你好,世界! @@") -} diff --git a/web/i18n/testdata/en/file.properties b/web/i18n/testdata/en/file.properties deleted file mode 100644 index fea7b86c..00000000 --- a/web/i18n/testdata/en/file.properties +++ /dev/null @@ -1 +0,0 @@ -hello=hello world! \ No newline at end of file diff --git a/web/i18n/testdata/zh.properties b/web/i18n/testdata/zh.properties deleted file mode 100644 index ce98b0ec..00000000 --- a/web/i18n/testdata/zh.properties +++ /dev/null @@ -1 +0,0 @@ -hello=你好,世界! \ No newline at end of file diff --git a/web/method-override.go b/web/method-override.go deleted file mode 100644 index fbb14d71..00000000 --- a/web/method-override.go +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "net/http" - "strings" -) - -type MethodOverrideGetter func(ctx Context) string - -type MethodOverrideConfig struct { - getters []MethodOverrideGetter -} - -func NewMethodOverrideConfig() *MethodOverrideConfig { - return new(MethodOverrideConfig) -} - -func (config *MethodOverrideConfig) ByHeader(key string) *MethodOverrideConfig { - config.getters = append(config.getters, func(ctx Context) string { - return ctx.Header(key) - }) - return config -} - -func (config *MethodOverrideConfig) ByQueryParam(name string) *MethodOverrideConfig { - config.getters = append(config.getters, func(ctx Context) string { - return ctx.QueryParam(name) - }) - return config -} - -func (config *MethodOverrideConfig) ByFormValue(name string) *MethodOverrideConfig { - config.getters = append(config.getters, func(ctx Context) string { - return ctx.FormValue(name) - }) - return config -} - -func (config *MethodOverrideConfig) get(ctx Context) string { - for _, getter := range config.getters { - if method := getter(ctx); method != "" { - return method - } - } - return "" -} - -func NewMethodOverrideFilter(config *MethodOverrideConfig) *Prefilter { - if len(config.getters) == 0 { - config.ByHeader("X-HTTP-Method"). - ByHeader("X-HTTP-Method-Override"). - ByHeader("X-Method-Override"). - ByQueryParam("_method"). - ByFormValue("_method") - } - return FuncPrefilter(func(ctx Context, chain FilterChain) { - req := ctx.Request() - if strings.ToUpper(req.Method) == http.MethodPost { - if method := config.get(ctx); method != "" { - req.Method = strings.ToUpper(method) - } - } - chain.Next(ctx, Iterative) - }) -} diff --git a/web/method-override_test.go b/web/method-override_test.go deleted file mode 100644 index 628cbab9..00000000 --- a/web/method-override_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestMethodOverride(t *testing.T) { - r, _ := http.NewRequest(http.MethodPost, "http://127.0.0.1:8080/?_method=GET", nil) - w := httptest.NewRecorder() - ctx := web.NewBaseContext("", nil, r, &web.SimpleResponse{ResponseWriter: w}) - f := web.NewMethodOverrideFilter(web.NewMethodOverrideConfig().ByQueryParam("_method")) - web.NewFilterChain([]web.Filter{f}).Next(ctx, web.Recursive) - assert.Equal(t, ctx.Request().Method, http.MethodGet) -} diff --git a/web/middleware.go b/web/middleware.go deleted file mode 100644 index 543c76d8..00000000 --- a/web/middleware.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -// key_auth -// echo: https://github.com/labstack/echo/blob/master/middleware/key_auth.go -// gin: - -// body_limit (413 - Request Entity Too Large) -// echo: https://github.com/labstack/echo/blob/master/middleware/body_limit.go -// gin: https://github.com/gin-contrib/size/blob/master/size.go - -// compress (gzip) -// echo: https://github.com/labstack/echo/blob/master/middleware/compress.go -// gin: https://github.com/gin-contrib/gzip/blob/master/gzip.go - -// cors (Cross-Origin Resource Sharing) -// echo: https://github.com/labstack/echo/blob/master/middleware/cors.go -// gin: https://github.com/gin-contrib/cors/blob/master/cors.go - -// csrf (Cross-Site Request Forgery) -// echo: https://github.com/labstack/echo/blob/master/middleware/csrf.go -// gin: - -// secure (XSS) -// echo: https://github.com/labstack/echo/blob/master/middleware/secure.go -// gin: - -// jwt (JSON Web Token) -// echo: https://github.com/labstack/echo/blob/master/middleware/jwt.go -// gin: https://github.com/appleboy/gin-jwt/blob/master/auth_jwt.go - -// proxy -// echo: https://github.com/labstack/echo/blob/master/middleware/proxy.go -// gin: - -// casbin -// echo: https://github.com/labstack/echo-contrib/blob/master/casbin/casbin.go -// gin: https://github.com/gin-contrib/authz/blob/master/authz.go - -// tracing -// echo: https://github.com/labstack/echo-contrib/blob/master/jaegertracing/jaegertracing.go -// gin: https://github.com/gin-contrib/opengintracing/blob/master/tracing.go - -// prometheus -// echo: https://github.com/labstack/echo-contrib/blob/master/prometheus/prometheus.go -// gin: https://github.com/zsais/go-gin-prometheus/blob/master/middleware.go - -// session -// echo: https://github.com/labstack/echo-contrib/blob/master/session/session.go -// gin: https://github.com/gin-contrib/sessions/blob/master/sessions.go - -// pprof -// echo: -// gin: https://github.com/gin-contrib/pprof/blob/master/pprof.go - -// oauth2 -// echo: -// gin: https://github.com/zalando/gin-oauth2/blob/master/ginoauth2.go diff --git a/web/prefilter.go b/web/prefilter.go deleted file mode 100644 index ca52c306..00000000 --- a/web/prefilter.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -// Prefilter 前置过滤器,用于路由未决策前。 -type Prefilter struct { - Filter -} - -// NewPrefilter 封装前置过滤器,用于路由未决策前。 -func NewPrefilter(f Filter) *Prefilter { - return &Prefilter{Filter: f} -} - -// FuncPrefilter 封装前置过滤器,用于路由未决策前。 -func FuncPrefilter(f FilterFunc) *Prefilter { - return &Prefilter{Filter: FuncFilter(f)} -} diff --git a/web/redirect.go b/web/redirect.go deleted file mode 100644 index ccfaec51..00000000 --- a/web/redirect.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "net/http" - "strings" -) - -type RedirectConfig struct { - Code int -} - -func NewRedirectConfig() RedirectConfig { - return RedirectConfig{ - Code: http.StatusMovedPermanently, - } -} - -// redirectFilter 重定向过滤器。 -type redirectFilter struct { - code int - redirect func(scheme, host, uri string) (ok bool, url string) -} - -func HTTPSRedirect(config RedirectConfig) Filter { - return &redirectFilter{ - code: config.Code, - redirect: func(scheme, host, uri string) (ok bool, url string) { - if scheme != "https" { - return true, "https://" + host + uri - } - return false, "" - }, - } -} - -func HTTPSWWWRedirect(config RedirectConfig) Filter { - return &redirectFilter{ - code: config.Code, - redirect: func(scheme, host, uri string) (ok bool, url string) { - if scheme != "https" && !strings.HasPrefix(host, "www.") { - return true, "https://www." + host + uri - } - return false, "" - }, - } -} - -func HTTPSNonWWWRedirect(config RedirectConfig) Filter { - return &redirectFilter{ - code: config.Code, - redirect: func(scheme, host, uri string) (ok bool, url string) { - if scheme != "https" { - host = strings.TrimPrefix(host, "www.") - return true, "https://" + host + uri - } - return false, "" - }, - } -} - -func WWWRedirect(config RedirectConfig) Filter { - return &redirectFilter{ - code: config.Code, - redirect: func(scheme, host, uri string) (ok bool, url string) { - if !strings.HasPrefix(host, "www.") { - return true, scheme + "://" + host[4:] + uri - } - return false, "" - }, - } -} - -func NonWWWRedirect(config RedirectConfig) Filter { - return &redirectFilter{ - code: config.Code, - redirect: func(scheme, host, uri string) (ok bool, url string) { - if strings.HasPrefix(host, "www.") { - return true, scheme + "://" + host[4:] + uri - } - return false, "" - }, - } -} - -func (f *redirectFilter) Invoke(ctx Context, chain FilterChain) { - req := ctx.Request() - ok, url := f.redirect(ctx.Scheme(), req.Host, req.RequestURI) - if ok { - code := http.StatusMovedPermanently - if f.code != 0 { - code = f.code - } - ctx.Redirect(code, url) - return - } - chain.Next(ctx, Iterative) -} diff --git a/web/redirect_test.go b/web/redirect_test.go deleted file mode 100644 index ecf62d52..00000000 --- a/web/redirect_test.go +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestRedirectFilter(t *testing.T) { - r, _ := http.NewRequest(http.MethodPost, "http://127.0.0.1:8080/", nil) - w := httptest.NewRecorder() - ctx := web.NewBaseContext("", nil, r, &web.SimpleResponse{ResponseWriter: w}) - f := web.HTTPSRedirect(web.NewRedirectConfig()) - web.NewFilterChain([]web.Filter{f}).Next(ctx, web.Recursive) - assert.Equal(t, w.Result().StatusCode, http.StatusMovedPermanently) - assert.Equal(t, w.Result().Header.Get(web.HeaderLocation), "https://127.0.0.1:8080") -} diff --git a/web/request-id.go b/web/request-id.go deleted file mode 100644 index dbd8b5d7..00000000 --- a/web/request-id.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "github.com/google/uuid" -) - -type RequestIDConfig struct { - Header string - Generator func() string -} - -func NewRequestIDConfig() RequestIDConfig { - return RequestIDConfig{} -} - -func NewRequestIDFilter(config RequestIDConfig) Filter { - if config.Header == "" { - config.Header = HeaderXRequestID - } - if config.Generator == nil { - config.Generator = func() string { - return uuid.New().String() - } - } - return FuncFilter(func(ctx Context, chain FilterChain) { - reqID := ctx.Header(config.Header) - if reqID == "" { - reqID = config.Generator() - } - ctx.SetHeader(HeaderXRequestID, reqID) - chain.Next(ctx, Iterative) - }) -} diff --git a/web/request-id_test.go b/web/request-id_test.go deleted file mode 100644 index 7c441ce3..00000000 --- a/web/request-id_test.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestRequestIDFilter(t *testing.T) { - r, _ := http.NewRequest(http.MethodPost, "http://127.0.0.1:8080/", nil) - w := httptest.NewRecorder() - ctx := web.NewBaseContext("", nil, r, &web.SimpleResponse{ResponseWriter: w}) - f := web.NewRequestIDFilter(web.RequestIDConfig{ - Generator: func() string { return "0d9ad123-327f-bde5-14b4-8f93c36c3546" }, - }) - web.NewFilterChain([]web.Filter{f}).Next(ctx, web.Recursive) - assert.Equal(t, w.Result().Header.Get(web.HeaderXRequestID), "0d9ad123-327f-bde5-14b4-8f93c36c3546") -} diff --git a/web/rewrite.go b/web/rewrite.go deleted file mode 100644 index aec85e03..00000000 --- a/web/rewrite.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -func NewRewriteFilter() *Prefilter { - return FuncPrefilter(func(ctx Context, chain FilterChain) { - // https://github.com/labstack/echo/blob/master/middleware/rewrite.go - }) -} diff --git a/web/rewrite_test.go b/web/rewrite_test.go deleted file mode 100644 index 0a032a1e..00000000 --- a/web/rewrite_test.go +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test diff --git a/web/router.go b/web/router.go deleted file mode 100644 index 89d72961..00000000 --- a/web/router.go +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "net/http" - - "github.com/go-spring/spring-base/util" -) - -const ( - MethodGet = 0x0001 // "GET" - MethodHead = 0x0002 // "HEAD" - MethodPost = 0x0004 // "POST" - MethodPut = 0x0008 // "PUT" - MethodPatch = 0x0010 // "PATCH" - MethodDelete = 0x0020 // "DELETE" - MethodConnect = 0x0040 // "CONNECT" - MethodOptions = 0x0080 // "OPTIONS" - MethodTrace = 0x0100 // "TRACE" - MethodAny = 0xffff - MethodGetPost = MethodGet | MethodPost -) - -var httpMethods = map[uint32]string{ - MethodGet: http.MethodGet, - MethodHead: http.MethodHead, - MethodPost: http.MethodPost, - MethodPut: http.MethodPut, - MethodPatch: http.MethodPatch, - MethodDelete: http.MethodDelete, - MethodConnect: http.MethodConnect, - MethodOptions: http.MethodOptions, - MethodTrace: http.MethodTrace, -} - -// GetMethod 返回 method 对应的 HTTP 方法 -func GetMethod(method uint32) (r []string) { - for k, v := range httpMethods { - if method&k == k { - r = append(r, v) - } - } - return -} - -// Mapper 路由映射器 -type Mapper struct { - method uint32 // 请求方法 - path string // 路由地址 - handler Handler // 处理函数 - swagger Operation // 描述文档 -} - -// NewMapper Mapper 的构造函数 -func NewMapper(method uint32, path string, h Handler) *Mapper { - return &Mapper{method: method, path: path, handler: h} -} - -// Method 返回 Mapper 的方法 -func (m *Mapper) Method() uint32 { - return m.method -} - -// Path 返回 Mapper 的路径 -func (m *Mapper) Path() string { - return m.path -} - -// Handler 返回 Mapper 的处理函数 -func (m *Mapper) Handler() Handler { - return m.handler -} - -// Operation 设置与 Mapper 绑定的 Operation 对象 -func (m *Mapper) Operation(op Operation) { - m.swagger = op -} - -// Router 路由注册接口 -type Router interface { - - // Mappers 返回映射器列表 - Mappers() []*Mapper - - // AddMapper 添加一个 Mapper - AddMapper(m *Mapper) - - // HttpGet 注册 GET 方法处理函数 - HttpGet(path string, h http.HandlerFunc) *Mapper - - // HandleGet 注册 GET 方法处理函数 - HandleGet(path string, h Handler) *Mapper - - // GetMapping 注册 GET 方法处理函数 - GetMapping(path string, fn HandlerFunc) *Mapper - - // GetBinding 注册 GET 方法处理函数 - GetBinding(path string, fn interface{}) *Mapper - - // HttpPost 注册 POST 方法处理函数 - HttpPost(path string, h http.HandlerFunc) *Mapper - - // HandlePost 注册 POST 方法处理函数 - HandlePost(path string, h Handler) *Mapper - - // PostMapping 注册 POST 方法处理函数 - PostMapping(path string, fn HandlerFunc) *Mapper - - // PostBinding 注册 POST 方法处理函数 - PostBinding(path string, fn interface{}) *Mapper - - // HttpPut 注册 PUT 方法处理函数 - HttpPut(path string, h http.HandlerFunc) *Mapper - - // HandlePut 注册 PUT 方法处理函数 - HandlePut(path string, h Handler) *Mapper - - // PutMapping 注册 PUT 方法处理函数 - PutMapping(path string, fn HandlerFunc) *Mapper - - // PutBinding 注册 PUT 方法处理函数 - PutBinding(path string, fn interface{}) *Mapper - - // HttpDelete 注册 DELETE 方法处理函数 - HttpDelete(path string, h http.HandlerFunc) *Mapper - - // HandleDelete 注册 DELETE 方法处理函数 - HandleDelete(path string, h Handler) *Mapper - - // DeleteMapping 注册 DELETE 方法处理函数 - DeleteMapping(path string, fn HandlerFunc) *Mapper - - // DeleteBinding 注册 DELETE 方法处理函数 - DeleteBinding(path string, fn interface{}) *Mapper - - // HandleRequest 注册任意 HTTP 方法处理函数 - HandleRequest(method uint32, path string, h Handler) *Mapper - - // RequestMapping 注册任意 HTTP 方法处理函数 - RequestMapping(method uint32, path string, fn HandlerFunc) *Mapper - - // RequestBinding 注册任意 HTTP 方法处理函数 - RequestBinding(method uint32, path string, fn interface{}) *Mapper - - // File 定义单个文件资源 - File(path string, file string) *Mapper - - // Static 定义一组文件资源 - Static(prefix string, dir string) *Mapper - - // StaticFS 定义一组文件资源 - StaticFS(prefix string, fs http.FileSystem) *Mapper -} - -// router 路由注册接口的默认实现 -type router struct { - mappers []*Mapper -} - -// NewRouter router 的构造函数。 -func NewRouter() *router { - return &router{} -} - -// Mappers 返回映射器列表 -func (r *router) Mappers() []*Mapper { - return r.mappers -} - -// AddMapper 添加一个 Mapper -func (r *router) AddMapper(m *Mapper) { - r.mappers = append(r.mappers, m) -} - -func (r *router) request(method uint32, path string, h Handler) *Mapper { - m := NewMapper(method, path, h) - r.AddMapper(m) - return m -} - -// HttpGet 注册 GET 方法处理函数 -func (r *router) HttpGet(path string, h http.HandlerFunc) *Mapper { - return r.request(MethodGet, path, HTTP(h)) -} - -// HandleGet 注册 GET 方法处理函数 -func (r *router) HandleGet(path string, h Handler) *Mapper { - return r.request(MethodGet, path, h) -} - -// GetMapping 注册 GET 方法处理函数 -func (r *router) GetMapping(path string, fn HandlerFunc) *Mapper { - return r.request(MethodGet, path, FUNC(fn)) -} - -// GetBinding 注册 GET 方法处理函数 -func (r *router) GetBinding(path string, fn interface{}) *Mapper { - return r.request(MethodGet, path, BIND(fn)) -} - -// HttpPost 注册 POST 方法处理函数 -func (r *router) HttpPost(path string, h http.HandlerFunc) *Mapper { - return r.request(MethodPost, path, HTTP(h)) -} - -// HandlePost 注册 POST 方法处理函数 -func (r *router) HandlePost(path string, h Handler) *Mapper { - return r.request(MethodPost, path, h) -} - -// PostMapping 注册 POST 方法处理函数 -func (r *router) PostMapping(path string, fn HandlerFunc) *Mapper { - return r.request(MethodPost, path, FUNC(fn)) -} - -// PostBinding 注册 POST 方法处理函数 -func (r *router) PostBinding(path string, fn interface{}) *Mapper { - return r.request(MethodPost, path, BIND(fn)) -} - -// HttpPut 注册 PUT 方法处理函数 -func (r *router) HttpPut(path string, h http.HandlerFunc) *Mapper { - return r.request(MethodPut, path, HTTP(h)) -} - -// HandlePut 注册 PUT 方法处理函数 -func (r *router) HandlePut(path string, h Handler) *Mapper { - return r.request(MethodPut, path, h) -} - -// PutMapping 注册 PUT 方法处理函数 -func (r *router) PutMapping(path string, fn HandlerFunc) *Mapper { - return r.request(MethodPut, path, FUNC(fn)) -} - -// PutBinding 注册 PUT 方法处理函数 -func (r *router) PutBinding(path string, fn interface{}) *Mapper { - return r.request(MethodPut, path, BIND(fn)) -} - -// HttpDelete 注册 DELETE 方法处理函数 -func (r *router) HttpDelete(path string, h http.HandlerFunc) *Mapper { - return r.request(MethodDelete, path, HTTP(h)) -} - -// HandleDelete 注册 DELETE 方法处理函数 -func (r *router) HandleDelete(path string, h Handler) *Mapper { - return r.request(MethodDelete, path, h) -} - -// DeleteMapping 注册 DELETE 方法处理函数 -func (r *router) DeleteMapping(path string, fn HandlerFunc) *Mapper { - return r.request(MethodDelete, path, FUNC(fn)) -} - -// DeleteBinding 注册 DELETE 方法处理函数 -func (r *router) DeleteBinding(path string, fn interface{}) *Mapper { - return r.request(MethodDelete, path, BIND(fn)) -} - -// HandleRequest 注册任意 HTTP 方法处理函数 -func (r *router) HandleRequest(method uint32, path string, h Handler) *Mapper { - return r.request(method, path, h) -} - -// RequestMapping 注册任意 HTTP 方法处理函数 -func (r *router) RequestMapping(method uint32, path string, fn HandlerFunc) *Mapper { - return r.request(method, path, FUNC(fn)) -} - -// RequestBinding 注册任意 HTTP 方法处理函数 -func (r *router) RequestBinding(method uint32, path string, fn interface{}) *Mapper { - return r.request(method, path, BIND(fn)) -} - -// File 定义单个文件资源 -func (r *router) File(path string, file string) *Mapper { - return r.GetMapping(path, func(ctx Context) { - ctx.File(file) - }) -} - -// Static 定义一组文件资源 -func (r *router) Static(prefix string, dir string) *Mapper { - return r.StaticFS(prefix, http.Dir(dir)) -} - -// StaticFS 定义一组文件资源 -func (r *router) StaticFS(prefix string, fs http.FileSystem) *Mapper { - return r.HandleGet(prefix+"/*", &FileHandler{ - Prefix: prefix, - Server: http.FileServer(fs), - }) -} - -type FileHandler struct { - Prefix string - Server http.Handler -} - -func (f *FileHandler) Invoke(ctx Context) { - h := http.StripPrefix(f.Prefix, f.Server) - h.ServeHTTP(ctx.Response(), ctx.Request()) -} - -func (f *FileHandler) FileLine() (file string, line int, fnName string) { - return util.FileLine(f.Invoke) -} diff --git a/web/router_test.go b/web/router_test.go deleted file mode 100644 index a2ae80bd..00000000 --- a/web/router_test.go +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "net/http" - "testing" - - "github.com/go-spring/spring-core/web" -) - -// cacheMethods -var cacheMethods = map[uint32][]string{ - web.MethodGet: {http.MethodGet}, - web.MethodHead: {http.MethodHead}, - web.MethodPost: {http.MethodPost}, - web.MethodPut: {http.MethodPut}, - web.MethodPatch: {http.MethodPatch}, - web.MethodDelete: {http.MethodDelete}, - web.MethodConnect: {http.MethodConnect}, - web.MethodOptions: {http.MethodOptions}, - web.MethodTrace: {http.MethodTrace}, - web.MethodGetPost: {http.MethodGet, http.MethodPost}, - web.MethodAny: {http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace}, -} - -func GetMethodViaCache(method uint32) []string { - if r, ok := cacheMethods[method]; ok { - return r - } - return web.GetMethod(method) -} - -func BenchmarkGetMethod(b *testing.B) { - // 测试结论:使用缓存不一定能提高效率 - - b.Run("1", func(b *testing.B) { - web.GetMethod(web.MethodGet) - }) - - b.Run("cache-1", func(b *testing.B) { - GetMethodViaCache(web.MethodGet) - }) - - b.Run("2", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead) - }) - - b.Run("cache-2", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead) - }) - - b.Run("3", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost) - }) - - b.Run("cache-3", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost) - }) - - b.Run("4", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut) - }) - - b.Run("cache-4", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut) - }) - - b.Run("5", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch) - }) - - b.Run("cache-5", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch) - }) - - b.Run("6", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete) - }) - - b.Run("cache-6", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete) - }) - - b.Run("7", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect) - }) - - b.Run("cache-7", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect) - }) - - b.Run("8", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect | web.MethodOptions) - }) - - b.Run("cache-8", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect | web.MethodOptions) - }) - - b.Run("9", func(b *testing.B) { - web.GetMethod(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect | web.MethodOptions | web.MethodTrace) - }) - - b.Run("cache-9", func(b *testing.B) { - GetMethodViaCache(web.MethodGet | web.MethodHead | web.MethodPost | web.MethodPut | web.MethodPatch | web.MethodDelete | web.MethodConnect | web.MethodOptions | web.MethodTrace) - }) -} diff --git a/web/rpc-result.go b/web/rpc-result.go deleted file mode 100644 index 8da86b81..00000000 --- a/web/rpc-result.go +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "fmt" - "math" - "runtime" - - "github.com/go-spring/spring-base/util" -) - -var ( - ERROR = NewRpcError(-1, "ERROR") - SUCCESS = NewRpcSuccess(200, "SUCCESS") - DEFAULT = NewErrorCode(math.MaxInt32, "DEFAULT") -) - -// ErrorCode 错误码 -type ErrorCode struct { - Code int32 `json:"code"` // 错误码 - Msg string `json:"msg"` // 错误信息 -} - -// NewErrorCode ErrorCode 的构造函数 -func NewErrorCode(code int32, msg string) ErrorCode { - return ErrorCode{Code: code, Msg: msg} -} - -// RpcResult 定义 RPC 返回值 -type RpcResult struct { - ErrorCode - - Err string `json:"err,omitempty"` // 错误源 - Data interface{} `json:"data,omitempty"` // 返回值 -} - -// RpcSuccess 定义一个 RPC 成功值 -type RpcSuccess ErrorCode - -// NewRpcSuccess RpcSuccess 的构造函数 -func NewRpcSuccess(code int32, msg string) RpcSuccess { - return RpcSuccess(NewErrorCode(code, msg)) -} - -// Data 绑定一个值 -func (r RpcSuccess) Data(data interface{}) *RpcResult { - return &RpcResult{ErrorCode: ErrorCode(r), Data: data} -} - -// RpcError 定义一个 RPC 异常值 -type RpcError ErrorCode - -// NewRpcError RpcError 的构造函数 -func NewRpcError(code int32, msg string) RpcError { - return RpcError(NewErrorCode(code, msg)) -} - -// Error 绑定一个错误 -func (r RpcError) Error(err error) *RpcResult { - return r.error(1, err, nil) -} - -// ErrorWithData 绑定一个错误和一个值 -func (r RpcError) ErrorWithData(err error, data interface{}) *RpcResult { - return r.error(1, err, data) -} - -// WithFileLine 返回错误发生的文件行号,skip 是相对于当前函数的深度。 -func WithFileLine(err error, skip int) error { - _, file, line, _ := runtime.Caller(skip + 1) - return fmt.Errorf("%s:%d: %w", file, line, err) -} - -// error skip 是相对于当前函数的调用深度 -func (r RpcError) error(skip int, err error, data interface{}) *RpcResult { - str := WithFileLine(err, skip+1).Error() - return &RpcResult{ErrorCode: ErrorCode(r), Err: str, Data: data} -} - -// Panic 抛出一个异常值 -func (r RpcError) Panic(err error) *util.PanicCond { - return util.NewPanicCond(func() interface{} { - return r.error(2, err, nil) - }) -} - -// Panicf 抛出一段需要格式化的错误字符串 -func (r RpcError) Panicf(format string, a ...interface{}) *util.PanicCond { - return util.NewPanicCond(func() interface{} { - return r.error(2, fmt.Errorf(format, a...), nil) - }) -} - -// PanicImmediately 立即抛出一个异常值 -func (r RpcError) PanicImmediately(err error) { - panic(r.error(1, err, nil)) -} diff --git a/web/rpc-result_test.go b/web/rpc-result_test.go deleted file mode 100644 index 57eaf68c..00000000 --- a/web/rpc-result_test.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "errors" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestRpcError(t *testing.T) { - err := errors.New("this is an error") - - r1 := web.ERROR.Error(err) - assert.Equal(t, r1, &web.RpcResult{ - ErrorCode: web.ErrorCode(web.ERROR), - Err: "/Users/didi/GitHub/go-spring/go-spring/spring/spring-core/web/rpc-result_test.go:30: this is an error", - }) - - r2 := web.ERROR.ErrorWithData(err, "error_with_data") - assert.Equal(t, r2, &web.RpcResult{ - ErrorCode: web.ErrorCode(web.ERROR), - Err: "/Users/didi/GitHub/go-spring/go-spring/spring/spring-core/web/rpc-result_test.go:36: this is an error", - Data: "error_with_data", - }) - - func() { - defer func() { - assert.Equal(t, recover(), &web.RpcResult{ - ErrorCode: web.ErrorCode(web.ERROR), - Err: "/Users/didi/GitHub/go-spring/go-spring/spring/spring-core/web/rpc-result_test.go:50: this is an error", - }) - }() - web.ERROR.Panic(err).When(err != nil) - }() - - func() { - defer func() { - assert.Equal(t, recover(), &web.RpcResult{ - ErrorCode: web.ErrorCode(web.ERROR), - Err: "/Users/didi/GitHub/go-spring/go-spring/spring/spring-core/web/rpc-result_test.go:60: this is an error", - }) - }() - web.ERROR.Panicf(err.Error()).When(true) - }() - - func() { - defer func() { - assert.Equal(t, recover(), &web.RpcResult{ - ErrorCode: web.ErrorCode(web.ERROR), - Err: "/Users/didi/GitHub/go-spring/go-spring/spring/spring-core/web/rpc-result_test.go:70: this is an error", - }) - }() - web.ERROR.PanicImmediately(err) - }() -} diff --git a/web/rpc.go b/web/rpc.go deleted file mode 100644 index 1d06f751..00000000 --- a/web/rpc.go +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "context" - "errors" - "net/http" - "reflect" - - "github.com/go-spring/spring-base/knife" - "github.com/go-spring/spring-base/util" -) - -const ( - httpRequestKey = "::HttpRequest::" -) - -// bindHandler BIND 形式的 Web 处理接口 -type bindHandler struct { - fn interface{} - fnType reflect.Type - fnValue reflect.Value - bindType reflect.Type -} - -func (b *bindHandler) Invoke(ctx Context) { - err := knife.Store(ctx.Context(), httpRequestKey, ctx.Request()) - util.Panic(err).When(err != nil) - RPCInvoke(ctx, b.call) -} - -func (b *bindHandler) call(ctx Context) interface{} { - - // 反射创建需要绑定请求参数 - bindVal := reflect.New(b.bindType.Elem()) - err := ctx.Bind(bindVal.Interface()) - util.Panic(err).When(err != nil) - - // 执行处理函数,并返回结果 - ctxVal := reflect.ValueOf(ctx.Request().Context()) - in := []reflect.Value{ctxVal, bindVal} - return b.fnValue.Call(in)[0].Interface() -} - -func (b *bindHandler) FileLine() (file string, line int, fnName string) { - return util.FileLine(b.fn) -} - -func validBindFn(fnType reflect.Type) bool { - - // 必须是函数,必须有两个入参,必须有一个返回值 - if fnType.Kind() != reflect.Func || fnType.NumIn() != 2 || fnType.NumOut() != 1 { - return false - } - - // 第一个入参必须是 context.Context 类型 - if !util.IsContextType(fnType.In(0)) { - return false - } - - req := fnType.In(1) // 第二个入参必须是结构体指针 - return req.Kind() == reflect.Ptr && req.Elem().Kind() == reflect.Struct -} - -// BIND 转换成 BIND 形式的 Web 处理接口 -func BIND(fn interface{}) Handler { - if fnType := reflect.TypeOf(fn); validBindFn(fnType) { - return &bindHandler{ - fn: fn, - fnType: fnType, - fnValue: reflect.ValueOf(fn), - bindType: fnType.In(1), - } - } - panic(errors.New("fn should be func(context.Context, *struct})anything")) -} - -// GetHTTPRequest returns the *http.Request storing in the context.Context. -func GetHTTPRequest(ctx context.Context) *http.Request { - v, err := knife.Load(ctx, httpRequestKey) - util.Panic(err).When(err != nil) - r, _ := v.(*http.Request) - return r -} - -// RPCInvoke 可自定义的 rpc 执行函数。 -var RPCInvoke = func(ctx Context, fn func(Context) interface{}) { - ctx.JSON(fn(ctx)) -} diff --git a/web/server.go b/web/server.go deleted file mode 100644 index d8c61d0b..00000000 --- a/web/server.go +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Package web 为社区优秀的 Web 服务器提供一个抽象层,使得底层可以灵活切换。 -package web - -import ( - "context" - "fmt" - "net/http" - "reflect" - "time" - - "github.com/go-spring/spring-base/cast" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" -) - -// HandlerFunc 标准 Web 处理函数 -type HandlerFunc func(Context) - -// Handler 标准 Web 处理接口 -type Handler interface { - - // Invoke 响应函数 - Invoke(Context) - - // FileLine 获取用户函数的文件名、行号以及函数名称 - FileLine() (file string, line int, fnName string) -} - -// ServerConfig 定义 web 服务器配置 -type ServerConfig struct { - Prefix string `value:"${prefix:=}"` // 用于 WebStarter 选择路由匹配的 Server - Host string `value:"${host:=}"` // 监听 IP - Port int `value:"${port:=8080}"` // HTTP 端口 - EnableSSL bool `value:"${ssl.enable:=false}"` // 是否启用 HTTPS - KeyFile string `value:"${ssl.key:=}"` // SSL 秘钥 - CertFile string `value:"${ssl.cert:=}"` // SSL 证书 - BasePath string `value:"${base-path:=}"` // 当前 Server 的所有路由都具有这个路径前缀 - ReadTimeout int `value:"${read-timeout:=0}"` // 读取超时,毫秒 - WriteTimeout int `value:"${write-timeout:=0}"` // 写入超时,毫秒 -} - -// ErrorHandler 错误处理接口 -type ErrorHandler interface { - Invoke(ctx Context, err *HttpError) -} - -// Server web 服务器 -type Server interface { - Router - - // Config 获取 web 服务器配置 - Config() ServerConfig - - // Prefilters 返回前置过滤器列表 - Prefilters() []*Prefilter - - // AddPrefilter 添加前置过滤器 - AddPrefilter(filter ...*Prefilter) - - // Filters 返回过滤器列表 - Filters() []Filter - - // AddFilter 添加过滤器 - AddFilter(filter ...Filter) - - // AccessFilter 获取访问记录 Filter - AccessFilter() Filter - - // SetAccessFilter 设置访问记录 Filter - SetAccessFilter(filter Filter) - - // ErrorHandler 获取错误处理接口 - ErrorHandler() ErrorHandler - - // SetErrorHandler 设置错误处理接口 - SetErrorHandler(errHandler ErrorHandler) - - // Swagger 设置与服务器绑定的 Swagger 对象 - Swagger(swagger Swagger) - - // Start 启动 web 服务器 - Start() error - - // Stop 停止 web 服务器 - Stop(ctx context.Context) error -} - -type ServerHandler interface { - http.Handler - Start(s Server) error - RecoveryFilter(errHandler ErrorHandler) Filter -} - -type server struct { - router - - logger *log.Logger - config ServerConfig // 容器配置项 - server *http.Server - handler ServerHandler - - access Filter // 日志过滤器 - filters []Filter // 其他过滤器 - prefilters []*Prefilter // 前置过滤器 - errHandler ErrorHandler // 错误处理接口 - - swagger Swagger // Swagger根 -} - -// NewServer server 的构造函数 -func NewServer(config ServerConfig, handler ServerHandler) *server { - ret := &server{config: config, handler: handler} - ret.logger = log.GetLogger(util.TypeName(ret)) - return ret -} - -// Address 返回监听地址 -func (s *server) Address() string { - return fmt.Sprintf("%s:%d", s.config.Host, s.config.Port) -} - -// Config 获取 web 服务器配置 -func (s *server) Config() ServerConfig { - return s.config -} - -// Prefilters 返回前置过滤器列表 -func (s *server) Prefilters() []*Prefilter { - return s.prefilters -} - -// AddPrefilter 添加前置过滤器 -func (s *server) AddPrefilter(filter ...*Prefilter) { - s.prefilters = append(s.prefilters, filter...) -} - -// Filters 返回过滤器列表 -func (s *server) Filters() []Filter { - return s.filters -} - -// AddFilter 添加过滤器 -func (s *server) AddFilter(filter ...Filter) { - s.filters = append(s.filters, filter...) -} - -// AccessFilter 获取访问记录 Filter -func (s *server) AccessFilter() Filter { - if s.access != nil { - return s.access - } - return FuncFilter(func(ctx Context, chain FilterChain) { - w := &BufferedResponseWriter{ResponseWriter: ctx.Response().Get()} - ctx.Response().Set(w) - start := time.Now() - chain.Next(ctx, Recursive) - r := ctx.Request() - cost := time.Since(start) - s.logger.WithContext(ctx.Context()).Infof("%s %s %s %d %d %s", r.Method, r.RequestURI, cost, w.Size(), w.Status(), r.UserAgent()) - }) -} - -// SetAccessFilter 设置访问记录 Filter -func (s *server) SetAccessFilter(filter Filter) { - s.access = filter -} - -// ErrorHandler 获取错误处理接口 -func (s *server) ErrorHandler() ErrorHandler { - if s.errHandler != nil { - return s.errHandler - } - return FuncErrorHandler(func(ctx Context, err *HttpError) { - defer func() { - if r := recover(); r != nil { - s.logger.WithContext(ctx.Context()).Error(r) - } - }() - if err.Internal == nil { - ctx.SetStatus(err.Code) - ctx.String(err.Message) - return - } - switch v := err.Internal.(type) { - case string: - ctx.String(v) - default: - ctx.JSON(err.Internal) - } - }) -} - -// SetErrorHandler 设置错误处理接口 -func (s *server) SetErrorHandler(errHandler ErrorHandler) { - s.errHandler = errHandler -} - -// SwaggerHandler Swagger 处理器 -type SwaggerHandler func(router Router, doc string) - -// swaggerHandler Swagger 处理器 -var swaggerHandler SwaggerHandler - -// RegisterSwaggerHandler 注册 Swagger 处理器 -func RegisterSwaggerHandler(handler SwaggerHandler) { - swaggerHandler = handler -} - -// Swagger 设置与服务器绑定的 Swagger 对象 -func (s *server) Swagger(swagger Swagger) { - s.swagger = swagger -} - -// prepare 启动 web 服务器之前的准备工作 -func (s *server) prepare() error { - - // 处理 swagger 注册相关 - if s.swagger != nil && swaggerHandler != nil { - for _, mapper := range s.Mappers() { - if mapper.swagger == nil { - continue - } - if err := mapper.swagger.Process(); err != nil { - return err - } - for _, method := range GetMethod(mapper.Method()) { - s.swagger.AddPath(mapper.Path(), method, mapper.swagger) - } - } - swaggerHandler(&s.router, s.swagger.ReadDoc()) - } - - // 打印所有的路由信息 - for _, m := range s.Mappers() { - s.logger.Infof("%v :%d %s -> %s:%d %s", log.T(func() []interface{} { - method := GetMethod(m.method) - path := s.config.BasePath + m.path - file, line, fnName := m.handler.FileLine() - return []interface{}{method, s.config.Port, path, file, line, fnName} - })) - } - - return nil -} - -// Start 启动 web 服务器 -func (s *server) Start() (err error) { - if err = s.prepare(); err != nil { - return err - } - if err = s.handler.Start(s); err != nil { - return err - } - s.server = &http.Server{ - Handler: s, - Addr: s.Address(), - ReadTimeout: time.Duration(s.config.ReadTimeout) * time.Millisecond, - WriteTimeout: time.Duration(s.config.WriteTimeout) * time.Millisecond, - } - s.logger.Info("⇨ http server started on ", s.Address()) - if !s.config.EnableSSL { - err = s.server.ListenAndServe() - } else { - err = s.server.ListenAndServeTLS(s.config.CertFile, s.config.KeyFile) - } - s.logger.Infof("http server stopped on %s return %s", s.Address(), cast.ToString(err)) - return err -} - -// Stop 停止 web 服务器 -func (s *server) Stop(ctx context.Context) error { - return s.server.Shutdown(ctx) -} - -func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - prefilters := []Filter{ - s.AccessFilter(), - s.handler.RecoveryFilter(s.ErrorHandler()), - } - for _, f := range s.Prefilters() { - prefilters = append(prefilters, f) - } - prefilters = append(prefilters, HandlerFilter(WrapH(s.handler))) - ctx := NewBaseContext("", nil, r, &SimpleResponse{ResponseWriter: w}) - NewFilterChain(prefilters).Next(ctx, Recursive) -} - -/////////////////// Web Handlers ////////////////////// - -// fnHandler 封装 Web 处理函数 -type fnHandler HandlerFunc - -func (f fnHandler) Invoke(ctx Context) { f(ctx) } - -func (f fnHandler) FileLine() (file string, line int, fnName string) { - return util.FileLine(f) -} - -// FUNC 标准 Web 处理函数的辅助函数 -func FUNC(fn HandlerFunc) Handler { return fnHandler(fn) } - -// httpFuncHandler 标准 Http 处理函数 -type httpFuncHandler http.HandlerFunc - -func (h httpFuncHandler) Invoke(ctx Context) { - h(ctx.Response(), ctx.Request()) -} - -func (h httpFuncHandler) FileLine() (file string, line int, fnName string) { - return util.FileLine(h) -} - -// HTTP 标准 Http 处理函数的辅助函数 -func HTTP(fn http.HandlerFunc) Handler { - return httpFuncHandler(fn) -} - -// WrapF 标准 Http 处理函数的辅助函数 -func WrapF(fn http.HandlerFunc) Handler { - return httpFuncHandler(fn) -} - -// httpHandler 标准 Http 处理函数 -type httpHandler struct{ http.Handler } - -func (h httpHandler) Invoke(ctx Context) { - h.Handler.ServeHTTP(ctx.Response(), ctx.Request()) -} - -func (h httpHandler) FileLine() (file string, line int, fnName string) { - t := reflect.TypeOf(h.Handler) - m, _ := t.MethodByName("ServeHTTP") - return util.FileLine(m.Func.Interface()) -} - -// WrapH 标准 Http 处理函数的辅助函数 -func WrapH(h http.Handler) Handler { - return &httpHandler{h} -} diff --git a/web/server_test.go b/web/server_test.go deleted file mode 100644 index c79869a9..00000000 --- a/web/server_test.go +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" -) - -func init() { - config := ` - - - - - - - - - - - - ` - err := log.RefreshBuffer(config, ".xml") - util.Panic(err).When(err != nil) -} diff --git a/web/swagger.go b/web/swagger.go deleted file mode 100644 index 41d0a811..00000000 --- a/web/swagger.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -* Copyright 2012-2019 the original author or authors. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. - */ - -package web - -// Operation 与路由绑定的 API 描述文档 -type Operation interface { - - // Process 完成处理参数绑定等过程 - Process() error -} - -// Swagger 与服务器绑定的 API 描述文档 -type Swagger interface { - - // ReadDoc 读取标准格式的描述文档 - ReadDoc() string - - // AddPath 添加与服务器绑定的路由节点 - AddPath(path string, method string, op Operation) -} diff --git a/web/url.go b/web/url.go deleted file mode 100644 index 1129e423..00000000 --- a/web/url.go +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web - -import ( - "errors" - "strings" -) - -// 路由风格有 echo、gin 和 {} 三种, -// /a/:b/c/:d/* 这种是 echo 风格; -// /a/:b/c/:d/*e 这种是 gin 风格; -// /a/{b}/c/{e:*} 这种是 {} 风格; -// /a/{b}/c/{*:e} 这也是 {} 风格; -// /a/{b}/c/{*} 这种也是 {} 风格。 - -type PathStyleEnum int - -const ( - EchoPathStyle = PathStyleEnum(0) - GinPathStyle = PathStyleEnum(1) - JavaPathStyle = PathStyleEnum(2) -) - -// DefaultWildcardName 默认通配符的名称 -const DefaultWildcardName = "@_@" - -// pathStyle URL 地址风格 -type pathStyle interface { - Path() string - Wildcard() string - addKnownPath(path string) - addNamedPath(path string) - addWildCard(name string) -} - -type basePathStyle struct { - strings.Builder - wildcard string // 通配符的名称 -} - -func (p *basePathStyle) Path() string { - return p.String() -} - -func (p *basePathStyle) Wildcard() string { - return p.wildcard -} - -// echoPathStyle Echo 地址风格 -type echoPathStyle struct { - basePathStyle -} - -func (p *echoPathStyle) addKnownPath(path string) { - p.WriteString("/" + path) -} - -func (p *echoPathStyle) addNamedPath(path string) { - p.WriteString("/:" + path) -} - -func (p *echoPathStyle) addWildCard(name string) { - p.WriteString("/*") - p.wildcard = name -} - -// ginPathStyle Gin 地址风格 -type ginPathStyle struct { - basePathStyle -} - -func (p *ginPathStyle) addKnownPath(path string) { - p.WriteString("/" + path) -} - -func (p *ginPathStyle) addNamedPath(path string) { - p.WriteString("/:" + path) -} - -func (p *ginPathStyle) addWildCard(name string) { - if name == "" { // gin 的路由需要指定一个名称 - name = DefaultWildcardName - } - p.WriteString("/*" + name) - p.wildcard = name -} - -// javaPathStyle {} 地址风格 -type javaPathStyle struct { - basePathStyle -} - -func (p *javaPathStyle) addKnownPath(path string) { - p.WriteString("/" + path) -} - -func (p *javaPathStyle) addNamedPath(path string) { - p.WriteString("/{" + path + "}") -} - -func (p *javaPathStyle) addWildCard(name string) { - if name != "" { - p.WriteString("/{*:" + name + "}") - } else { - p.WriteString("/{*}") - } - p.wildcard = name -} - -// ToPathStyle 将 URL 转换为指定风格的表示形式 -func ToPathStyle(path string, style PathStyleEnum) (newPath string, wildcard string) { - - var p pathStyle - switch style { - case EchoPathStyle: - p = &echoPathStyle{} - case GinPathStyle: - p = &ginPathStyle{} - case JavaPathStyle: - p = &javaPathStyle{} - default: - panic(errors.New("error path style")) - } - - // 去掉开始的 / 字符,后面好计算 - if path[0] == '/' { - path = path[1:] - } - - for _, s := range strings.Split(path, "/") { - - // 尾部的 '/' 特殊处理 - if len(s) == 0 { - p.addKnownPath(s) - continue - } - - switch s[0] { - case '{': - if s[len(s)-1] != '}' { - panic(errors.New("error url path")) - } - if ss := strings.Split(s[1:len(s)-1], ":"); len(ss) > 1 { - if ss[0] == "*" { - p.addWildCard(ss[1]) - } else if ss[1] == "*" { - p.addWildCard(ss[0]) - } else { - panic(errors.New("error url path")) - } - } else if s[1] == '*' { - p.addWildCard(s[2 : len(s)-1]) - } else { - p.addNamedPath(s[1 : len(s)-1]) - } - case '*': - if s == "*" { - p.addWildCard("") - } else { - p.addWildCard(s[1:]) - } - case ':': - p.addNamedPath(s[1:]) - default: - p.addKnownPath(s) - } - } - return p.Path(), p.Wildcard() -} diff --git a/web/url_test.go b/web/url_test.go deleted file mode 100644 index a97c5277..00000000 --- a/web/url_test.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package web_test - -import ( - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/web" -) - -func TestToPathStyle(t *testing.T) { - - t.Run("/:a", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/:a", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/:a", web.GinPathStyle) - assert.Equal(t, newPath, "/:a") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/:a", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/{a}", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/{a}", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/{a}", web.GinPathStyle) - assert.Equal(t, newPath, "/:a") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/{a}", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/:a/b/:c", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/:a/b/:c", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/:a/b/:c", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/:a/b/:c", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/{a}/b/{c}", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/{a}/b/{c}", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/:a/b/:c/*", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/:a/b/:c/*", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/:a/b/:c/*", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*@_@") - assert.Equal(t, wildcard, "@_@") - newPath, wildcard = web.ToPathStyle("/:a/b/:c/*", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}/{*}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/{a}/b/{c}/{*}", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/{a}/b/{c}/{*}", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*") - assert.Equal(t, wildcard, "") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{*}", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*@_@") - assert.Equal(t, wildcard, "@_@") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{*}", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}/{*}") - assert.Equal(t, wildcard, "") - }) - - t.Run("/:a/b/:c/*e", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/:a/b/:c/*e", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/:a/b/:c/*e", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*e") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/:a/b/:c/*e", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}/{*:e}") - assert.Equal(t, wildcard, "e") - }) - - t.Run("/{a}/b/{c}/{*:e}", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/{a}/b/{c}/{*:e}", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{*:e}", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*e") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{*:e}", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}/{*:e}") - assert.Equal(t, wildcard, "e") - }) - - t.Run("/{a}/b/{c}/{e:*}", func(t *testing.T) { - newPath, wildcard := web.ToPathStyle("/{a}/b/{c}/{e:*}", web.EchoPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{e:*}", web.GinPathStyle) - assert.Equal(t, newPath, "/:a/b/:c/*e") - assert.Equal(t, wildcard, "e") - newPath, wildcard = web.ToPathStyle("/{a}/b/{c}/{e:*}", web.JavaPathStyle) - assert.Equal(t, newPath, "/{a}/b/{c}/{*:e}") - assert.Equal(t, wildcard, "e") - }) -} From 2936cf923cda88f71ad511b85ca93889e264ea35 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 28 Dec 2024 21:37:02 +0800 Subject: [PATCH 02/75] code refactor --- conf/bind.go | 2 +- conf/bind_test.go | 10 ++++----- conf/conf.go | 16 +++++++-------- conf/conf_test.go | 32 ++++++++++++++--------------- conf/reader/json/json.go | 31 ++++++++++++++++++++++++++++ conf/reader/json/json_test.go | 17 +++++++++++++++ conf/{ => reader}/prop/prop.go | 6 +----- conf/{ => reader}/prop/prop_test.go | 2 +- conf/{ => reader}/toml/toml.go | 0 conf/{ => reader}/toml/toml_test.go | 2 +- conf/{ => reader}/yaml/yaml.go | 0 conf/{ => reader}/yaml/yaml_test.go | 2 +- 12 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 conf/reader/json/json.go create mode 100644 conf/reader/json/json_test.go rename conf/{ => reader}/prop/prop.go (92%) rename conf/{ => reader}/prop/prop_test.go (98%) rename conf/{ => reader}/toml/toml.go (100%) rename conf/{ => reader}/toml/toml_test.go (98%) rename conf/{ => reader}/yaml/yaml.go (100%) rename conf/{ => reader}/yaml/yaml_test.go (98%) diff --git a/conf/bind.go b/conf/bind.go index 70f3396c..c1619980 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -273,7 +273,7 @@ func getSlice(p *Properties, et reflect.Type, param BindParam) (*Properties, err } } - p = New() + p = NewProperties() for i, s := range arrVal { k := fmt.Sprintf("%s[%d]", param.Key, i) if err = p.Set(k, s); err != nil { diff --git a/conf/bind_test.go b/conf/bind_test.go index bb4bd70f..369c6336 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -81,7 +81,7 @@ type NestedDbMapConfig struct { func TestProperties_Bind(t *testing.T) { t.Run("default", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() v := &struct { S struct { V int `value:"${:=3}"` @@ -140,7 +140,7 @@ func TestProperties_Bind(t *testing.T) { t.Run("simple map bind", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("a.b1", "b1") assert.Nil(t, err) err = p.Set("a.b2", "b2") @@ -190,19 +190,19 @@ func TestProperties_Bind(t *testing.T) { }) t.Run("ignore interface", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Bind(&struct{ fmt.Stringer }{}) assert.Nil(t, err) }) t.Run("ignore pointer", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Bind(list.New()) assert.Error(t, err, ".*bind.go:.* bind List error; .*bind.go:.* bind List.len error; .*bind.go:.* resolve property \"len\" error; property \"len\" not exist") }) t.Run("", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Bytes([]byte(`m:`), ".yaml") if err != nil { t.Fatal(err) diff --git a/conf/conf.go b/conf/conf.go index c68e5550..92ecb6e0 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -32,9 +32,9 @@ import ( "github.com/go-spring/spring-base/cast" "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf/internal" - "github.com/go-spring/spring-core/conf/prop" - "github.com/go-spring/spring-core/conf/toml" - "github.com/go-spring/spring-core/conf/yaml" + "github.com/go-spring/spring-core/conf/reader/prop" + "github.com/go-spring/spring-core/conf/reader/toml" + "github.com/go-spring/spring-core/conf/reader/yaml" ) // Splitter splits string into []string by some characters. @@ -113,8 +113,8 @@ type Properties struct { storage *internal.Storage } -// New creates empty *Properties. -func New() *Properties { +// NewProperties creates empty *Properties. +func NewProperties() *Properties { return &Properties{ storage: internal.NewStorage(), } @@ -122,7 +122,7 @@ func New() *Properties { // Map creates *Properties from map. func Map(m map[string]interface{}) (*Properties, error) { - p := New() + p := NewProperties() var keys []string for k := range m { keys = append(keys, k) @@ -138,7 +138,7 @@ func Map(m map[string]interface{}) (*Properties, error) { // Load creates *Properties from file. func Load(file string) (*Properties, error) { - p := New() + p := NewProperties() if err := p.Load(file); err != nil { return nil, err } @@ -165,7 +165,7 @@ func Read(r io.Reader, ext string) (*Properties, error) { // Bytes creates *Properties from []byte, ext is the file name extension. func Bytes(b []byte, ext string) (*Properties, error) { - p := New() + p := NewProperties() if err := p.Bytes(b, ext); err != nil { return nil, err } diff --git a/conf/conf_test.go b/conf/conf_test.go index 938c055f..0f447988 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -31,7 +31,7 @@ import ( func TestProperties_Load(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Load("testdata/config/application.yaml") assert.Nil(t, err) err = p.Load("testdata/config/application.properties") @@ -49,7 +49,7 @@ func TestProperties_Get(t *testing.T) { t.Run("base", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("a.b.c", "3") assert.Nil(t, err) @@ -202,13 +202,13 @@ func TestProperties_Ref(t *testing.T) { var httpLog struct{ fileLog } t.Run("not config", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Bind(&httpLog) assert.Error(t, err, "property \\\"app.dir\\\" not exist") }) t.Run("config", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() appDir := "/home/log" err := p.Set("app.dir", appDir) @@ -230,7 +230,7 @@ func TestProperties_Ref(t *testing.T) { }) t.Run("empty key", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() var s struct { KeyIsEmpty string `value:"${:=kie}"` } @@ -241,7 +241,7 @@ func TestProperties_Ref(t *testing.T) { } func TestBindSlice(t *testing.T) { - p := conf.New() + p := conf.NewProperties() p.Set("a", []string{"1", "2"}) var ss []string err := p.Bind(&ss, conf.Key("a")) @@ -255,19 +255,19 @@ func TestBindMap(t *testing.T) { t.Run("", func(t *testing.T) { var r [3]map[string]string - err := conf.New().Bind(&r) + err := conf.NewProperties().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind \\[3]map\\[string]string error; target should be value type") }) t.Run("", func(t *testing.T) { var r []map[string]string - err := conf.New().Bind(&r) + err := conf.NewProperties().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind \\[]map\\[string]string error; target should be value type") }) t.Run("", func(t *testing.T) { var r map[string]map[string]string - err := conf.New().Bind(&r) + err := conf.NewProperties().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind map\\[string]map\\[string]string error; target should be value type") }) @@ -362,7 +362,7 @@ func TestBindMap(t *testing.T) { } func TestResolve(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("name", "Jim") assert.Nil(t, err) _, err = p.Resolve("my name is ${name") @@ -399,7 +399,7 @@ func TestProperties_Set(t *testing.T) { t.Run("map nil", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("m", nil) assert.Nil(t, err) assert.True(t, p.Has("m")) @@ -417,7 +417,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("map empty", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("m", map[string]string{}) assert.Nil(t, err) assert.True(t, p.Has("m")) @@ -428,7 +428,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list nil", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("a", nil) assert.Nil(t, err) assert.True(t, p.Has("a")) @@ -439,7 +439,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list empty", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("a", []string{}) assert.Nil(t, err) assert.True(t, p.Has("a")) @@ -450,7 +450,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list", func(t *testing.T) { - p := conf.New() + p := conf.NewProperties() err := p.Set("a", []string{"a", "aa", "aaa"}) assert.Nil(t, err) err = p.Set("b", []int{1, 11, 111}) @@ -495,7 +495,7 @@ func TestSplitter(t *testing.T) { conf.RegisterConverter(PointConverter) conf.RegisterSplitter("PointSplitter", PointSplitter) var points []image.Point - err := conf.New().Bind(&points, conf.Tag("${:=(1,2)(3,4)}||PointSplitter")) + err := conf.NewProperties().Bind(&points, conf.Tag("${:=(1,2)(3,4)}||PointSplitter")) assert.Nil(t, err) assert.Equal(t, points, []image.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}) } diff --git a/conf/reader/json/json.go b/conf/reader/json/json.go new file mode 100644 index 00000000..1f895bec --- /dev/null +++ b/conf/reader/json/json.go @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package json + +import ( + "encoding/json" +) + +// Read parses []byte in the json format into map. +func Read(b []byte) (map[string]interface{}, error) { + var ret map[string]interface{} + err := json.Unmarshal(b, &ret) + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/conf/reader/json/json_test.go b/conf/reader/json/json_test.go new file mode 100644 index 00000000..0875fe78 --- /dev/null +++ b/conf/reader/json/json_test.go @@ -0,0 +1,17 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package json_test diff --git a/conf/prop/prop.go b/conf/reader/prop/prop.go similarity index 92% rename from conf/prop/prop.go rename to conf/reader/prop/prop.go index b536a10e..9e345004 100644 --- a/conf/prop/prop.go +++ b/conf/reader/prop/prop.go @@ -23,11 +23,7 @@ func Read(b []byte) (map[string]interface{}, error) { p := properties.NewProperties() p.DisableExpansion = true - - err := p.Load(b, properties.UTF8) - if err != nil { - return nil, err - } + _ = p.Load(b, properties.UTF8) // always no error ret := make(map[string]interface{}) for k, v := range p.Map() { diff --git a/conf/prop/prop_test.go b/conf/reader/prop/prop_test.go similarity index 98% rename from conf/prop/prop_test.go rename to conf/reader/prop/prop_test.go index e8567341..b64f5485 100644 --- a/conf/prop/prop_test.go +++ b/conf/reader/prop/prop_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf/prop" + "github.com/go-spring/spring-core/conf/reader/prop" ) func TestRead(t *testing.T) { diff --git a/conf/toml/toml.go b/conf/reader/toml/toml.go similarity index 100% rename from conf/toml/toml.go rename to conf/reader/toml/toml.go diff --git a/conf/toml/toml_test.go b/conf/reader/toml/toml_test.go similarity index 98% rename from conf/toml/toml_test.go rename to conf/reader/toml/toml_test.go index 3dff59d2..df41df67 100644 --- a/conf/toml/toml_test.go +++ b/conf/reader/toml/toml_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf/toml" + "github.com/go-spring/spring-core/conf/reader/toml" ) func TestRead(t *testing.T) { diff --git a/conf/yaml/yaml.go b/conf/reader/yaml/yaml.go similarity index 100% rename from conf/yaml/yaml.go rename to conf/reader/yaml/yaml.go diff --git a/conf/yaml/yaml_test.go b/conf/reader/yaml/yaml_test.go similarity index 98% rename from conf/yaml/yaml_test.go rename to conf/reader/yaml/yaml_test.go index b6d376d3..ad5464c6 100644 --- a/conf/yaml/yaml_test.go +++ b/conf/reader/yaml/yaml_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf/yaml" + "github.com/go-spring/spring-core/conf/reader/yaml" ) func TestRead(t *testing.T) { From 24ad672e5133707d885c1820e02332e6fcdc1e80 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 28 Dec 2024 22:06:49 +0800 Subject: [PATCH 03/75] add sysconf and code refactor --- conf/bind.go | 205 ++++++++++++++++------------- conf/bind_test.go | 7 - conf/conf.go | 271 ++++++++++++++++++++------------------- conf/conf_test.go | 32 ++--- conf/flat.go | 59 +++++++++ conf/internal/storage.go | 11 +- conf/util.go | 75 +++++++++++ expr/expr.go | 28 ++-- go.mod | 1 + go.sum | 12 ++ gs/sysconf/sysconf.go | 67 ++++++++++ util/fileline.go | 45 +++++++ 12 files changed, 556 insertions(+), 257 deletions(-) create mode 100644 conf/flat.go create mode 100644 conf/util.go create mode 100644 gs/sysconf/sysconf.go create mode 100644 util/fileline.go diff --git a/conf/bind.go b/conf/bind.go index c1619980..97c5bfc1 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -23,17 +23,31 @@ import ( "strconv" "strings" - "github.com/go-spring/spring-base/code" - "github.com/go-spring/spring-base/util" + "github.com/go-spring/spring-core/util" ) var ( - errNotExist = errors.New("not exist") - errInvalidSyntax = errors.New("invalid syntax") + ErrNotExist = errors.New("not exist") + ErrInvalidSyntax = errors.New("invalid syntax") ) +var ( + bindValidator ValidatorInterface +) + +// ValidatorInterface is the interface for validating a field. +type ValidatorInterface interface { + Name() string + Field(tag string, i interface{}) error +} + +// SetValidator sets the validator. +func SetValidator(i ValidatorInterface) { + bindValidator = i +} + // ParsedTag a value tag includes at most three parts: required key, optional -// default value, and optional splitter, the syntax is ${key:=value}||splitter. +// default value, and optional splitter, the syntax is ${key:=value}>>splitter. type ParsedTag struct { Key string // short property key Def string // default value @@ -41,24 +55,37 @@ type ParsedTag struct { Splitter string // splitter's name } +func (tag ParsedTag) String() string { + var sb strings.Builder + sb.WriteString("${") + sb.WriteString(tag.Key) + if tag.HasDef { + sb.WriteString(":=") + sb.WriteString(tag.Def) + } + sb.WriteString("}") + if tag.Splitter != "" { + sb.WriteString(">>") + sb.WriteString(tag.Splitter) + } + return sb.String() +} + // ParseTag parses a value tag, returns its key, and default value, and splitter. func ParseTag(tag string) (ret ParsedTag, err error) { - i := strings.LastIndex(tag, "||") + i := strings.LastIndex(tag, ">>") if i == 0 { - err = errInvalidSyntax - err = util.Wrapf(err, code.FileLine(), "parse tag %q error", tag) + err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax) return } j := strings.LastIndex(tag, "}") if j <= 0 { - err = errInvalidSyntax - err = util.Wrapf(err, code.FileLine(), "parse tag %q error", tag) + err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax) return } k := strings.Index(tag, "${") if k < 0 { - err = errInvalidSyntax - err = util.Wrapf(err, code.FileLine(), "parse tag %q error", tag) + err = fmt.Errorf("parse tag '%s' error: %w", tag, ErrInvalidSyntax) return } if i > j { @@ -74,13 +101,13 @@ func ParseTag(tag string) (ret ParsedTag, err error) { } type BindParam struct { - Key string // full property key - Path string // binding path - Tag ParsedTag // parsed tag - Validate string + Key string // full key + Path string // full path + Tag ParsedTag // parsed tag + Validate reflect.StructTag // full field tag } -func (param *BindParam) BindTag(tag string, validate string) error { +func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error { parsedTag, err := ParseTag(tag) if err != nil { return err @@ -103,34 +130,47 @@ func (param *BindParam) BindTag(tag string, validate string) error { type Filter func(i interface{}, param BindParam) (bool, error) // BindValue binds properties to a value. -func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { - if !util.IsValueType(t) { + if !IsValueType(t) { err := errors.New("target should be value type") - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } + defer func() { + if RetErr == nil { + tag, ok := param.Validate.Lookup(bindValidator.Name()) + if ok && len(tag) > 0 { + err := bindValidator.Field(tag, v.Interface()) + if err != nil { + RetErr = err + } + } + } + }() + switch v.Kind() { case reflect.Map: return bindMap(p, v, t, param, filter) - case reflect.Array: - err := errors.New("use slice instead of array") - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) case reflect.Slice: return bindSlice(p, v, t, param, filter) + case reflect.Array: + err := errors.New("use slice instead of array") + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + default: // for linter } fn := converters[t] if fn == nil && v.Kind() == reflect.Struct { if err := bindStruct(p, v, t, param, filter); err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } return nil } val, err := resolve(p, param) if err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } if fn != nil { @@ -138,7 +178,7 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, out := fnValue.Call([]reflect.Value{reflect.ValueOf(val)}) if !out[1].IsNil() { err = out[1].Interface().(error) - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } v.Set(out[0]) return nil @@ -148,53 +188,39 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var u uint64 if u, err = strconv.ParseUint(val, 0, 0); err == nil { - // if err = validate.Field(u, param.Validate); err != nil { - // return err - // } v.SetUint(u) return nil } - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 if i, err = strconv.ParseInt(val, 0, 0); err == nil { - // if err = validate.Field(i, param.Validate); err != nil { - // return err - // } v.SetInt(i) return nil } - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) case reflect.Float32, reflect.Float64: var f float64 if f, err = strconv.ParseFloat(val, 64); err == nil { - // if err = validate.Field(f, param.Validate); err != nil { - // return err - // } v.SetFloat(f) return nil } - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) case reflect.Bool: var b bool if b, err = strconv.ParseBool(val); err == nil { - // if err = validate.Field(b, param.Validate); err != nil { - // return err - // } v.SetBool(b) return nil } - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) case reflect.String: - // if err = validate.Field(val, param.Validate); err != nil { - // return err - // } v.SetString(val) return nil + default: // for linter } err = fmt.Errorf("unsupported bind type %q", t.String()) - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } // bindSlice binds properties to a slice value. @@ -203,7 +229,7 @@ func bindSlice(p *Properties, v reflect.Value, t reflect.Type, param BindParam, et := t.Elem() p, err := getSlice(p, et, param) if err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } slice := reflect.MakeSlice(t, 0, 0) @@ -220,11 +246,11 @@ func bindSlice(p *Properties, v reflect.Value, t reflect.Type, param BindParam, Path: fmt.Sprintf("%s[%d]", param.Path, i), } err = BindValue(p, e, et, subParam, filter) - if errors.Is(err, errNotExist) { + if errors.Is(err, ErrNotExist) { break } if err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } slice = reflect.Append(slice, e) } @@ -233,25 +259,25 @@ func bindSlice(p *Properties, v reflect.Value, t reflect.Type, param BindParam, func getSlice(p *Properties, et reflect.Type, param BindParam) (*Properties, error) { - // properties defined as list. + // properties that defined as list. if p.Has(param.Key + "[0]") { return p, nil } - // properties defined as string and needs to split into []string. + // properties that defined as string and needs to split into []string. var strVal string { if p.Has(param.Key) { strVal = p.Get(param.Key) } else { if !param.Tag.HasDef { - return nil, util.Errorf(code.FileLine(), "property %q %w", param.Key, errNotExist) + return nil, fmt.Errorf("%s: property %q %w", util.FileLine(), param.Key, ErrNotExist) } if param.Tag.Def == "" { return nil, nil } - if !util.IsPrimitiveValueType(et) && converters[et] == nil { - return nil, util.Error(code.FileLine(), "slice can't have a non empty default value") + if !IsPrimitiveValueType(et) && converters[et] == nil { + return nil, fmt.Errorf("%s: can't find converter for %s", util.FileLine(), et.String()) } strVal = param.Tag.Def } @@ -267,28 +293,31 @@ func getSlice(p *Properties, et reflect.Type, param BindParam) (*Properties, err if s := param.Tag.Splitter; s == "" { arrVal = strings.Split(strVal, ",") - } else if fn := splitters[s]; fn != nil { + for i := range arrVal { + arrVal[i] = strings.TrimSpace(arrVal[i]) + } + } else if fn, ok := splitters[s]; ok && fn != nil { if arrVal, err = fn(strVal); err != nil { - return nil, err + return nil, fmt.Errorf("%s: split error: %w, value: %q", util.FileLine(), err, strVal) } + } else { + return nil, fmt.Errorf("%s: unknown splitter %q", util.FileLine(), s) } p = NewProperties() for i, s := range arrVal { k := fmt.Sprintf("%s[%d]", param.Key, i) - if err = p.Set(k, s); err != nil { - return nil, err - } + _ = p.store(k, s) } return p, nil } // bindMap binds properties to a map value. -func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (err error) { +func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { - err := errors.New("map can't have a non empty default value") - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + err := errors.New("map can't have a non-empty default value") + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } et := t.Elem() @@ -297,7 +326,7 @@ func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fi keys, err := p.storage.SubKeys(param.Key) if err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } for _, key := range keys { @@ -312,7 +341,7 @@ func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fi } err = BindValue(p, e, et, subParam, filter) if err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } ret.SetMapIndex(reflect.ValueOf(key), e) } @@ -323,8 +352,8 @@ func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fi func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { - err := errors.New("struct can't have a non empty default value") - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + err := errors.New("struct can't have a non-empty default value") + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } for i := 0; i < t.NumField(); i++ { @@ -332,10 +361,7 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fv := v.Field(i) if !fv.CanInterface() { - fv = util.PatchValue(fv) - if !fv.CanInterface() { - continue - } + continue } subParam := BindParam{ @@ -344,9 +370,8 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, } if tag, ok := ft.Tag.Lookup("value"); ok { - // validateTag, _ := ft.Tag.Lookup(validate.TagName()) - if err := subParam.BindTag(tag, ""); err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + if err := subParam.BindTag(tag, ft.Tag); err != nil { + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } if filter != nil { ret, err := filter(fv.Addr().Interface(), subParam) @@ -358,7 +383,7 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, } } if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } continue } @@ -369,19 +394,21 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, continue } if err := bindStruct(p, fv, ft.Type, subParam, filter); err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } continue } - if util.IsValueType(ft.Type) { + if IsValueType(ft.Type) { if subParam.Key == "" { subParam.Key = ft.Name } else { subParam.Key = subParam.Key + "." + ft.Name } + subParam.Key = strings.ToLower(subParam.Key) + subParam.Key = strings.ReplaceAll(subParam.Key, "_", ".") if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return util.Wrapf(err, code.FileLine(), "bind %s error", param.Path) + return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } } } @@ -390,15 +417,18 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, // resolve returns property references processed property value. func resolve(p *Properties, param BindParam) (string, error) { - val := p.storage.Get(param.Key) - if val != "" { + if val, ok := p.storage.Get(param.Key); ok { return resolveString(p, val) } + if p.storage.Has(param.Key) { + err := fmt.Errorf("property %q isn't simple value", param.Key) + return "", fmt.Errorf("%s: resolve property %q error, %w", util.FileLine(), param.Key, err) + } if param.Tag.HasDef { return resolveString(p, param.Tag.Def) } - err := fmt.Errorf("property %q %w", param.Key, errNotExist) - return "", util.Wrapf(err, code.FileLine(), "resolve property %q error", param.Key) + err := fmt.Errorf("property %q %w", param.Key, ErrNotExist) + return "", fmt.Errorf("%s: resolve property %q error, %w", util.FileLine(), param.Key, err) } // resolveString returns property references processed string. @@ -435,24 +465,21 @@ func resolveString(p *Properties, s string) (string, error) { } if end < 0 || count > 0 { - err := errInvalidSyntax - return "", util.Wrapf(err, code.FileLine(), "resolve string %q error", s) + err := ErrInvalidSyntax + return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) } var param BindParam - err := param.BindTag(s[start:end+1], "") - if err != nil { - return "", util.Wrapf(err, code.FileLine(), "resolve string %q error", s) - } + _ = param.BindTag(s[start:end+1], "") s1, err := resolve(p, param) if err != nil { - return "", util.Wrapf(err, code.FileLine(), "resolve string %q error", s) + return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) } s2, err := resolveString(p, s[end+1:]) if err != nil { - return "", util.Wrapf(err, code.FileLine(), "resolve string %q error", s) + return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) } return s[:start] + s1 + s2, nil diff --git a/conf/bind_test.go b/conf/bind_test.go index 369c6336..857ea509 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -17,7 +17,6 @@ package conf_test import ( - "container/list" "fmt" "testing" @@ -195,12 +194,6 @@ func TestProperties_Bind(t *testing.T) { assert.Nil(t, err) }) - t.Run("ignore pointer", func(t *testing.T) { - p := conf.NewProperties() - err := p.Bind(list.New()) - assert.Error(t, err, ".*bind.go:.* bind List error; .*bind.go:.* bind List.len error; .*bind.go:.* resolve property \"len\" error; property \"len\" not exist") - }) - t.Run("", func(t *testing.T) { p := conf.NewProperties() err := p.Bytes([]byte(`m:`), ".yaml") diff --git a/conf/conf.go b/conf/conf.go index 92ecb6e0..32a1fc1d 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -21,60 +21,49 @@ package conf import ( "errors" "fmt" - "io" - "io/ioutil" + "os" "path/filepath" "reflect" "sort" "strings" + "sync/atomic" "time" - "github.com/go-spring/spring-base/cast" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf/internal" + "github.com/go-spring/spring-core/conf/reader/json" "github.com/go-spring/spring-core/conf/reader/prop" "github.com/go-spring/spring-core/conf/reader/toml" "github.com/go-spring/spring-core/conf/reader/yaml" + "github.com/go-spring/spring-core/expr" + "github.com/spf13/cast" ) -// Splitter splits string into []string by some characters. -type Splitter func(string) ([]string, error) - -// Reader parses []byte into nested map[string]interface{}. -type Reader func(b []byte) (map[string]interface{}, error) - var ( readers = map[string]Reader{} splitters = map[string]Splitter{} - converters = map[reflect.Type]util.Converter{} + converters = map[reflect.Type]Converter{} ) func init() { + SetValidator(&expr.Validator{}) + RegisterReader(json.Read, ".json") RegisterReader(prop.Read, ".properties") RegisterReader(yaml.Read, ".yaml", ".yml") RegisterReader(toml.Read, ".toml", ".tml") - // converts string into time.Time. The string value may have its own - // time format defined after >> splitter, otherwise it uses a default - // time format `2006-01-02 15:04:05 -0700`. RegisterConverter(func(s string) (time.Time, error) { - s = strings.TrimSpace(s) - format := "2006-01-02 15:04:05 -0700" - if ss := strings.Split(s, ">>"); len(ss) == 2 { - format = strings.TrimSpace(ss[1]) - s = strings.TrimSpace(ss[0]) - } - return cast.ToTimeE(s, format) + return cast.ToTimeE(strings.TrimSpace(s)) }) - // converts string into time.Duration. The string should have its own - // time unit such as "ns", "ms", "s", "m", etc. RegisterConverter(func(s string) (time.Duration, error) { - return cast.ToDurationE(s) + return time.ParseDuration(strings.TrimSpace(s)) }) } +// Reader parses []byte into nested map[string]interface{}. +type Reader func(b []byte) (map[string]interface{}, error) + // RegisterReader registers its Reader for some kind of file extension. func RegisterReader(r Reader, ext ...string) { for _, s := range ext { @@ -82,21 +71,30 @@ func RegisterReader(r Reader, ext ...string) { } } +// Splitter splits string into []string by some characters. +type Splitter func(string) ([]string, error) + // RegisterSplitter registers a Splitter and named it. func RegisterSplitter(name string, fn Splitter) { splitters[name] = fn } +// Converter converts string value into user-defined value. It should be function +// type, and its prototype is func(string)(type,error). +type Converter interface{} + // RegisterConverter registers its converter for non-primitive type such as // time.Time, time.Duration, or other user-defined value type. -func RegisterConverter(fn util.Converter) { +func RegisterConverter(fn Converter) { t := reflect.TypeOf(fn) - if !util.IsConverter(t) { - panic(errors.New("converter should be func(string)(type,error)")) + if !IsConverter(t) { + panic(errors.New("converter is func(string)(type,error)")) } converters[t.Out(0)] = fn } +var _ ReadOnlyProperties = (*Properties)(nil) + // Properties stores the data with map[string]string and the keys are case-sensitive, // you can get one of them by its key, or bind some of them to a value. // There are too many formats of configuration files, and too many conflicts between @@ -147,58 +145,47 @@ func Load(file string) (*Properties, error) { // Load loads properties from file. func (p *Properties) Load(file string) error { - b, err := ioutil.ReadFile(file) + b, err := os.ReadFile(file) if err != nil { return err } return p.Bytes(b, filepath.Ext(file)) } -// Read creates *Properties from io.Reader, ext is the file name extension. -func Read(r io.Reader, ext string) (*Properties, error) { - b, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - return Bytes(b, ext) -} - -// Bytes creates *Properties from []byte, ext is the file name extension. -func Bytes(b []byte, ext string) (*Properties, error) { - p := NewProperties() - if err := p.Bytes(b, ext); err != nil { - return nil, err - } - return p, nil -} - // Bytes loads properties from []byte, ext is the file name extension. func (p *Properties) Bytes(b []byte, ext string) error { r, ok := readers[ext] if !ok { - return fmt.Errorf("unsupported file type %s", ext) + return fmt.Errorf("unsupported file type %q", ext) } m, err := r(b) if err != nil { return err } - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - if err = p.Set(k, m[k]); err != nil { + return p.Merge(m) +} + +// Merge flattens the map and sets all keys and values. +func (p *Properties) Merge(m map[string]interface{}) error { + s := FlattenMap(m) + return p.merge(s) +} + +func (p *Properties) merge(m map[string]string) error { + for key, val := range m { + if err := p.store(key, val); err != nil { return err } } return nil } -func (p *Properties) Copy() *Properties { - return &Properties{ - storage: p.storage.Copy(), - } +func (p *Properties) store(key, val string) error { + return p.storage.Set(key, val) +} + +func (p *Properties) Data() map[string]string { + return p.storage.Data() } // Keys returns all sorted keys. @@ -226,8 +213,8 @@ func Def(v string) GetOption { // Get returns key's value, using Def to return a default value. func (p *Properties) Get(key string, opts ...GetOption) string { - val := p.storage.Get(key) - if val != "" { + val, ok := p.storage.Get(key) + if ok { return val } arg := getArg{} @@ -237,40 +224,6 @@ func (p *Properties) Get(key string, opts ...GetOption) string { return arg.def } -func Flatten(key string, val interface{}, result map[string]string) error { - switch v := reflect.ValueOf(val); v.Kind() { - case reflect.Map: - if v.Len() == 0 { - result[key] = "" - return nil - } - for _, k := range v.MapKeys() { - mapKey := cast.ToString(k.Interface()) - mapValue := v.MapIndex(k).Interface() - err := Flatten(key+"."+mapKey, mapValue, result) - if err != nil { - return err - } - } - case reflect.Array, reflect.Slice: - if v.Len() == 0 { - result[key] = "" - return nil - } - for i := 0; i < v.Len(); i++ { - subKey := fmt.Sprintf("%s[%d]", key, i) - subValue := v.Index(i).Interface() - err := Flatten(subKey, subValue, result) - if err != nil { - return err - } - } - default: - result[key] = cast.ToString(val) - } - return nil -} - // Set sets key's value to be a primitive type as int or string, // or a slice or map nested with primitive type elements. One thing // you should know is Set actions as overlap but not replace, that @@ -279,25 +232,11 @@ func Flatten(key string, val interface{}, result map[string]string) error { // prefix path. func (p *Properties) Set(key string, val interface{}) error { if key == "" { - return nil + return errors.New("key is empty") } m := make(map[string]string) - err := Flatten(key, val, m) - if err != nil { - return err - } - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - err = p.storage.Set(k, m[k]) - if err != nil { - return err - } - } - return nil + FlattenValue(key, val, m) + return p.merge(m) } // Resolve resolves string value that contains references to other @@ -306,33 +245,53 @@ func (p *Properties) Resolve(s string) (string, error) { return resolveString(p, s) } -type bindArg struct { +type BindArg interface { + getParam() (BindParam, error) +} + +type paramArg struct { + param BindParam +} + +func (tag paramArg) getParam() (BindParam, error) { + return tag.param, nil +} + +type tagArg struct { tag string } -type BindOption func(arg *bindArg) +func (tag tagArg) getParam() (BindParam, error) { + var param BindParam + err := param.BindTag(tag.tag, "") + if err != nil { + return BindParam{}, err + } + return param, nil +} // Key binds properties using one key. -func Key(key string) BindOption { - return func(arg *bindArg) { - arg.tag = "${" + key + "}" - } +func Key(key string) BindArg { + return tagArg{tag: "${" + key + "}"} } // Tag binds properties using one tag. -func Tag(tag string) BindOption { - return func(arg *bindArg) { - arg.tag = tag - } +func Tag(tag string) BindArg { + return tagArg{tag: tag} +} + +// Param binds properties using BindParam. +func Param(param BindParam) BindArg { + return paramArg{param: param} } // Bind binds properties to a value, the bind value can be primitive type, // map, slice, struct. When binding to struct, the tag 'value' indicates // which properties should be bind. The 'value' tags are defined by -// value:"${a:=b|splitter}", 'a' is the key, 'b' is the default value, +// value:"${a:=b}>>splitter", 'a' is the key, 'b' is the default value, // 'splitter' is the Splitter's name when you want split string value // into []string value. -func (p *Properties) Bind(i interface{}, opts ...BindOption) error { +func (p *Properties) Bind(i interface{}, args ...BindArg) error { var v reflect.Value { @@ -342,15 +301,14 @@ func (p *Properties) Bind(i interface{}, opts ...BindOption) error { default: v = reflect.ValueOf(i) if v.Kind() != reflect.Ptr { - return errors.New("i should be a ptr") + return errors.New("should be a ptr") } v = v.Elem() } } - arg := bindArg{tag: "${ROOT}"} - for _, opt := range opts { - opt(&arg) + if len(args) == 0 { + args = []BindArg{tagArg{tag: "${ROOT}"}} } t := v.Type() @@ -359,12 +317,63 @@ func (p *Properties) Bind(i interface{}, opts ...BindOption) error { typeName = t.String() } - param := BindParam{ - Path: typeName, - } - err := param.BindTag(arg.tag, "") + param, err := args[0].getParam() if err != nil { return err } + param.Path = typeName return BindValue(p, v, t, param, nil) } + +// CopyTo copies properties into another by override. +func (p *Properties) CopyTo(out *Properties) error { + return out.merge(p.storage.RawData()) +} + +// ReadOnlyProperties is the interface for read-only properties. +type ReadOnlyProperties interface { + + // Data returns key-value pairs of the properties. + Data() map[string]string + + // Keys returns keys of the properties. + Keys() []string + + // Has returns whether the key exists. + Has(key string) bool + + // Get returns key's value, using Def to return a default value. + Get(key string, opts ...GetOption) string + + // Resolve resolves string that contains references. + Resolve(s string) (string, error) + + // Bind binds properties into a value. + Bind(i interface{}, args ...BindArg) error + + // CopyTo copies properties into another by override. + CopyTo(out *Properties) error +} + +// AtomicProperties is a thread-safe version of Properties. +type AtomicProperties struct { + v atomic.Pointer[Properties] +} + +// NewAtomicProperties creates a new atomic properties. +func NewAtomicProperties() *AtomicProperties { + return new(AtomicProperties) +} + +// Load loads as read-only properties. +func (p *AtomicProperties) Load() ReadOnlyProperties { + if s := p.v.Load(); s != nil { + return s + } + return nil +} + +// Store stores a new properties. +func (p *AtomicProperties) Store(v *Properties) { + p.v.Store(v) +} diff --git a/conf/conf_test.go b/conf/conf_test.go index 0f447988..f5fc8771 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -75,7 +75,7 @@ func TestProperties_Get(t *testing.T) { assert.Nil(t, err) err = p.Set("StringSlice", []string{"3", "4"}) assert.Nil(t, err) - err = p.Set("Time", "2020-02-04 20:02:04 >> 2006-01-02 15:04:05") + err = p.Set("Time", "2020-02-04 20:02:04") assert.Nil(t, err) err = p.Set("MapStringInterface", []interface{}{ map[interface{}]interface{}{ @@ -150,10 +150,6 @@ func TestProperties_Get(t *testing.T) { assert.Nil(t, err) assert.Equal(t, ti, time.Date(2020, 02, 04, 20, 02, 04, 0, time.UTC)) - err = p.Bind(&ti, conf.Key("Duration")) - assert.Nil(t, err) - assert.Equal(t, ti, time.Date(1970, 01, 01, 00, 00, 03, 0, time.UTC).Local()) - var ss2 []string err = p.Bind(&ss2, conf.Key("StringSlice")) assert.Nil(t, err) @@ -191,15 +187,15 @@ func TestProperties_Get(t *testing.T) { func TestProperties_Ref(t *testing.T) { - type fileLog struct { + type FileLog struct { Dir string `value:"${dir:=${app.dir}}"` NestedDir string `value:"${nested.dir:=${nested.app.dir:=./log}}"` NestedEmptyDir string `value:"${nested.dir:=${nested.app.dir:=}}"` NestedNestedDir string `value:"${nested.dir:=${nested.app.dir:=${nested.nested.app.dir:=./log}}}"` } - var mqLog struct{ fileLog } - var httpLog struct{ fileLog } + var mqLog struct{ FileLog } + var httpLog struct{ FileLog } t.Run("not config", func(t *testing.T) { p := conf.NewProperties() @@ -256,19 +252,19 @@ func TestBindMap(t *testing.T) { t.Run("", func(t *testing.T) { var r [3]map[string]string err := conf.NewProperties().Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind \\[3]map\\[string]string error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind \\[3]map\\[string]string error, target should be value type") }) t.Run("", func(t *testing.T) { var r []map[string]string err := conf.NewProperties().Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind \\[]map\\[string]string error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind \\[]map\\[string]string error, target should be value type") }) t.Run("", func(t *testing.T) { var r map[string]map[string]string err := conf.NewProperties().Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind map\\[string]map\\[string]string error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind map\\[string]map\\[string]string error, target should be value type") }) m := map[string]interface{}{ @@ -288,7 +284,7 @@ func TestBindMap(t *testing.T) { p, err := conf.Map(m) assert.Nil(t, err) err = p.Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error, target should be value type") }) t.Run("", func(t *testing.T) { @@ -299,7 +295,7 @@ func TestBindMap(t *testing.T) { p, err := conf.Map(m) assert.Nil(t, err) err = p.Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error, target should be value type") }) t.Run("", func(t *testing.T) { @@ -310,7 +306,7 @@ func TestBindMap(t *testing.T) { p, err := conf.Map(m) assert.Nil(t, err) err = p.Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error; target should be value type") + assert.Error(t, err, ".*bind.go:.* bind map\\[string]conf_test.S.M error, target should be value type") }) t.Run("", func(t *testing.T) { @@ -331,7 +327,7 @@ func TestBindMap(t *testing.T) { assert.Nil(t, err) var r map[string]string err = p.Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind map\\[string]string error; .*bind.go:.* resolve property \"a\" error; property \"a\" not exist") + assert.Error(t, err, ".*bind.go:.* bind map\\[string]string error, .*bind.go:.* resolve property \"a\" error, property \"a\" isn't simple value") }) t.Run("", func(t *testing.T) { @@ -345,7 +341,7 @@ func TestBindMap(t *testing.T) { }) assert.Nil(t, err) err = p.Bind(&r) - assert.Error(t, err, ".*bind.go:.* bind S error; .*bind.go:.* bind S.A error; property 'a' is value") + assert.Error(t, err, ".*bind.go:.* bind S error, .*bind.go:.* bind S.A error, property 'a' is value") }) t.Run("", func(t *testing.T) { @@ -366,7 +362,7 @@ func TestResolve(t *testing.T) { err := p.Set("name", "Jim") assert.Nil(t, err) _, err = p.Resolve("my name is ${name") - assert.Error(t, err, ".*bind.go:.* resolve string \"my name is \\${name\" error; invalid syntax") + assert.Error(t, err, ".*bind.go:.* resolve string \"my name is \\${name\" error, invalid syntax") str, _ := p.Resolve("my name is ${name}") assert.Equal(t, str, "my name is Jim") str, _ = p.Resolve("my name is ${name}${name}") @@ -495,7 +491,7 @@ func TestSplitter(t *testing.T) { conf.RegisterConverter(PointConverter) conf.RegisterSplitter("PointSplitter", PointSplitter) var points []image.Point - err := conf.NewProperties().Bind(&points, conf.Tag("${:=(1,2)(3,4)}||PointSplitter")) + err := conf.NewProperties().Bind(&points, conf.Tag("${:=(1,2)(3,4)}>>PointSplitter")) assert.Nil(t, err) assert.Equal(t, points, []image.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}) } diff --git a/conf/flat.go b/conf/flat.go new file mode 100644 index 00000000..8ca2e45c --- /dev/null +++ b/conf/flat.go @@ -0,0 +1,59 @@ +// Copyright 2024 github.com/lvan100 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package conf + +import ( + "fmt" + "reflect" + + "github.com/spf13/cast" +) + +// FlattenMap can expand the nested array, slice and map. +func FlattenMap(m map[string]interface{}) map[string]string { + result := make(map[string]string) + for key, val := range m { + FlattenValue(key, val, result) + } + return result +} + +// FlattenValue can expand the nested array, slice and map. +func FlattenValue(key string, val interface{}, result map[string]string) { + switch v := reflect.ValueOf(val); v.Kind() { + case reflect.Map: + if v.Len() == 0 { + result[key] = "" + return + } + for _, k := range v.MapKeys() { + mapKey := cast.ToString(k.Interface()) + mapValue := v.MapIndex(k).Interface() + FlattenValue(key+"."+mapKey, mapValue, result) + } + case reflect.Array, reflect.Slice: + if v.Len() == 0 { + result[key] = "" + return + } + for i := 0; i < v.Len(); i++ { + subKey := fmt.Sprintf("%s[%d]", key, i) + subValue := v.Index(i).Interface() + FlattenValue(subKey, subValue, result) + } + default: + result[key] = cast.ToString(val) + } +} diff --git a/conf/internal/storage.go b/conf/internal/storage.go index 2a8e466b..eac55b22 100644 --- a/conf/internal/storage.go +++ b/conf/internal/storage.go @@ -79,6 +79,11 @@ func (s *Storage) Copy() *Storage { } } +// RawData returns the raw data of the storage. +func (s *Storage) RawData() map[string]string { + return s.data +} + // Data returns key-value pairs of the properties. func (s *Storage) Data() map[string]string { if len(s.data) == 0 { @@ -154,9 +159,9 @@ func (s *Storage) Has(key string) bool { } // Get returns the key's value. -func (s *Storage) Get(key string) string { - val, _ := s.data[key] - return val +func (s *Storage) Get(key string) (string, bool) { + val, ok := s.data[key] + return val, ok } // Set stores the key and its value. diff --git a/conf/util.go b/conf/util.go new file mode 100644 index 00000000..7795d63f --- /dev/null +++ b/conf/util.go @@ -0,0 +1,75 @@ +// Copyright 2024 github.com/lvan100 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package conf + +import ( + "reflect" +) + +// errorType the reflection type of error. +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +// IsFuncType returns whether `t` is func type. +func IsFuncType(t reflect.Type) bool { + return t.Kind() == reflect.Func +} + +// IsErrorType returns whether `t` is error type. +func IsErrorType(t reflect.Type) bool { + return t == errorType || t.Implements(errorType) +} + +// IsConverter returns whether `t` is a converter type. +func IsConverter(t reflect.Type) bool { + return IsFuncType(t) && + t.NumIn() == 1 && + t.In(0).Kind() == reflect.String && + t.NumOut() == 2 && + (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) +} + +// IsValueType returns whether the input type is the primitive value type and their +// composite type including array, slice, map and struct, such as []int, [3]string, +// []string, map[int]int, map[string]string, etc. +func IsValueType(t reflect.Type) bool { + fn := func(t reflect.Type) bool { + return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct + } + switch t.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return fn(t.Elem()) + default: + return fn(t) + } +} + +// IsPrimitiveValueType returns whether `t` is the primitive value type which only is +// int, unit, float, bool, string and complex. +func IsPrimitiveValueType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Bool: + return true + default: + return false + } +} diff --git a/expr/expr.go b/expr/expr.go index 6ec95768..2208cb1b 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -17,20 +17,30 @@ package expr import ( + "fmt" + "github.com/expr-lang/expr" - "github.com/go-spring/spring-base/code" - "github.com/go-spring/spring-base/util" ) -// Eval returns the value for the expression expr. -func Eval(input string, val interface{}) (bool, error) { - r, err := expr.Eval(input, map[string]interface{}{"$": val}) +type Validator struct{} + +// Name returns the name of the validator. +func (d *Validator) Name() string { + return "expr" +} + +// Field validates the field with the given tag and value. +func (d *Validator) Field(tag string, i interface{}) error { + r, err := expr.Eval(tag, map[string]interface{}{"$": i}) if err != nil { - return false, util.Wrapf(err, code.FileLine(), "eval %q returns error", input) + return fmt.Errorf("eval %q returns error, %w", tag, err) } - b, ok := r.(bool) + ret, ok := r.(bool) if !ok { - return false, util.Wrapf(err, code.FileLine(), "eval %q doesn't return bool", input) + return fmt.Errorf("eval %q doesn't return bool value", tag) + } + if !ret { + return fmt.Errorf("validate failed on %q for value %v", tag, i) } - return b, nil + return nil } diff --git a/go.mod b/go.mod index ceb46c06..3c6a9467 100644 --- a/go.mod +++ b/go.mod @@ -8,5 +8,6 @@ require ( github.com/golang/mock v1.6.0 github.com/magiconair/properties v1.8.9 github.com/pelletier/go-toml v1.9.5 + github.com/spf13/cast v1.7.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 3e698184..6c51b515 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,25 @@ github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/go-spring/spring-base v1.1.3 h1:oyPwSend8UFIYSk8X6x4PaRu3BrbLWK7rYc+htnqLWA= github.com/go-spring/spring-base v1.1.3/go.mod h1:tdngm+6agA34HQ5YADitIGaQ04e1pmxuR5cd6Eaobmw= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/gs/sysconf/sysconf.go b/gs/sysconf/sysconf.go new file mode 100644 index 00000000..1835fd64 --- /dev/null +++ b/gs/sysconf/sysconf.go @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sysconf + +import ( + "sync" + + "github.com/go-spring/spring-core/conf" +) + +var ( + prop = conf.NewProperties() + lock sync.Mutex +) + +// Get returns the property of the key. +func Get(key string, opts ...conf.GetOption) string { + lock.Lock() + defer lock.Unlock() + return prop.Get(key, opts...) +} + +// Set sets the property of the key. +func Set(key string, val interface{}) error { + lock.Lock() + defer lock.Unlock() + return prop.Set(key, val) +} + +// Unset removes the property. +func Unset(key string) { + lock.Lock() + defer lock.Unlock() +} + +// Clear clears all properties. +func Clear() { + lock.Lock() + defer lock.Unlock() + prop = conf.NewProperties() +} + +// Clone copies all properties into another properties. +func Clone() *conf.Properties { + lock.Lock() + m := prop.Data() + lock.Unlock() + p := conf.NewProperties() + for k, v := range m { + _ = p.Set(k, v) + } + return p +} diff --git a/util/fileline.go b/util/fileline.go new file mode 100644 index 00000000..15eddc3d --- /dev/null +++ b/util/fileline.go @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util + +import ( + "fmt" + "runtime" + "sync" +) + +var frameMap sync.Map + +func fileLine() (string, int) { + rpc := make([]uintptr, 1) + runtime.Callers(3, rpc[:]) + pc := rpc[0] + if v, ok := frameMap.Load(pc); ok { + e := v.(*runtime.Frame) + return e.File, e.Line + } + frame, _ := runtime.CallersFrames(rpc).Next() + frameMap.Store(pc, &frame) + return frame.File, frame.Line +} + +// FileLine returns the file name and line of the call point. +// In reality FileLine here costs less time than debug.Stack. +func FileLine() string { + file, line := fileLine() + return fmt.Sprintf("%s:%d", file, line) +} From 34fca46394e411f8aef09e552ef37f9428172ace Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 28 Dec 2024 22:16:11 +0800 Subject: [PATCH 04/75] code refactor --- conf/internal/path.go | 2 +- conf/internal/storage.go | 177 +++++++++++++--------------------- conf/internal/storage_test.go | 65 ++++++------- 3 files changed, 95 insertions(+), 149 deletions(-) diff --git a/conf/internal/path.go b/conf/internal/path.go index 2896b0e6..ff8275d4 100644 --- a/conf/internal/path.go +++ b/conf/internal/path.go @@ -34,7 +34,7 @@ type Path struct { Elem string } -// JoinPath joins path elements into a single path. +// JoinPath joins all path elements into a single path. func JoinPath(path []Path) string { var s strings.Builder for i, p := range path { diff --git a/conf/internal/storage.go b/conf/internal/storage.go index eac55b22..f66313c2 100644 --- a/conf/internal/storage.go +++ b/conf/internal/storage.go @@ -17,9 +17,9 @@ package internal import ( + "cmp" "fmt" "sort" - "strings" ) type nodeType int @@ -36,31 +36,12 @@ type treeNode struct { data interface{} } -// Copy returns a new copy of the *treeNode object. -func (t *treeNode) Copy() *treeNode { - r := &treeNode{ - node: t.node, - } - switch m := t.data.(type) { - case map[string]*treeNode: - c := make(map[string]*treeNode) - for k, v := range m { - c[k] = v.Copy() - } - r.data = c - default: - r.data = t.data - } - return r -} - -// Storage stores data in the properties format. +// Storage is a key-value store that verifies the format of the key. type Storage struct { tree *treeNode data map[string]string } -// NewStorage returns a new *Storage object. func NewStorage() *Storage { return &Storage{ tree: &treeNode{ @@ -71,24 +52,13 @@ func NewStorage() *Storage { } } -// Copy returns a new copy of the *Storage object. -func (s *Storage) Copy() *Storage { - return &Storage{ - tree: s.tree.Copy(), - data: s.Data(), - } -} - // RawData returns the raw data of the storage. func (s *Storage) RawData() map[string]string { return s.data } -// Data returns key-value pairs of the properties. +// Data returns the copied data of the storage. func (s *Storage) Data() map[string]string { - if len(s.data) == 0 { - return nil - } m := make(map[string]string) for k, v := range s.data { m[k] = v @@ -96,20 +66,12 @@ func (s *Storage) Data() map[string]string { return m } -// Keys returns keys of the properties. +// Keys returns the sorted keys of the storage. func (s *Storage) Keys() []string { - if len(s.data) == 0 { - return nil - } - keys := make([]string, 0, len(s.data)) - for k := range s.data { - keys = append(keys, k) - } - sort.Strings(keys) - return keys + return OrderedMapKeys(s.data) } -// SubKeys returns the sub keys of the key item. +// SubKeys returns the sorted sub keys of the key. func (s *Storage) SubKeys(key string) ([]string, error) { path, err := SplitPath(key) if err != nil { @@ -119,21 +81,22 @@ func (s *Storage) SubKeys(key string) ([]string, error) { for i, pathNode := range path { m := tree.data.(map[string]*treeNode) v, ok := m[pathNode.Elem] - if !ok || v.node == nodeTypeNil { + if !ok { return nil, nil } switch v.node { + case nodeTypeNil: + return nil, nil case nodeTypeValue: return nil, fmt.Errorf("property '%s' is value", JoinPath(path[:i+1])) case nodeTypeArray, nodeTypeMap: tree = v + default: + return nil, fmt.Errorf("invalid node type %d", v.node) } } - var keys []string - for k := range tree.data.(map[string]*treeNode) { - keys = append(keys, k) - } - sort.Strings(keys) + m := tree.data.(map[string]*treeNode) + keys := OrderedMapKeys(m) return keys, nil } @@ -146,6 +109,17 @@ func (s *Storage) Has(key string) bool { tree := s.tree for i, node := range path { m := tree.data.(map[string]*treeNode) + switch tree.node { + case nodeTypeArray: + if node.Type != PathTypeIndex { + return false + } + case nodeTypeMap: + if node.Type != PathTypeKey { + return false + } + default: // for linter + } v, ok := m[node.Elem] if !ok { return false @@ -158,77 +132,68 @@ func (s *Storage) Has(key string) bool { return true } -// Get returns the key's value. +// Get returns the value of the key, and false if the key does not exist. func (s *Storage) Get(key string) (string, bool) { val, ok := s.data[key] return val, ok } -// Set stores the key and its value. +// Set stores the value of the key. func (s *Storage) Set(key, val string) error { - val = strings.TrimSpace(val) - err := s.buildTree(key, val) + tree, err := s.merge(key, val) if err != nil { return err } - path, _ := SplitPath(key) - for i := range path { - k := JoinPath(path[:i+1]) - if _, ok := s.data[k]; ok { - delete(s.data, k) - } + switch tree.node { + case nodeTypeNil, nodeTypeValue: + s.data[key] = val + default: + return fmt.Errorf("invalid node type %d", tree.node) } - s.data[key] = val return nil } -func (s *Storage) buildTree(key, val string) error { +func (s *Storage) merge(key, val string) (*treeNode, error) { path, err := SplitPath(key) if err != nil { - return err + return nil, err } if path[0].Type == PathTypeIndex { - return fmt.Errorf("invalid key '%s'", key) + return nil, fmt.Errorf("invalid key '%s'", key) } tree := s.tree for i, pathNode := range path { if tree.node == nodeTypeMap { if pathNode.Type != PathTypeKey { - return fmt.Errorf("property '%s' is a map but '%s' wants other type", JoinPath(path[:i]), key) + return nil, fmt.Errorf("property '%s' is a map but '%s' wants other type", JoinPath(path[:i]), key) } } m := tree.data.(map[string]*treeNode) v, ok := m[pathNode.Elem] + if v != nil && v.node == nodeTypeNil { + delete(s.data, JoinPath(path[:i+1])) + } if !ok || v.node == nodeTypeNil { if i < len(path)-1 { n := &treeNode{ data: make(map[string]*treeNode), } - if pathNode.Type == PathTypeKey { - if path[i+1].Type == PathTypeIndex { - n.node = nodeTypeArray - } else { - n.node = nodeTypeMap - } - } else if pathNode.Type == PathTypeIndex { + if path[i+1].Type == PathTypeIndex { n.node = nodeTypeArray + } else { + n.node = nodeTypeMap } m[pathNode.Elem] = n tree = n continue } if val == "" { - m[pathNode.Elem] = &treeNode{ - node: nodeTypeNil, - data: nodeTypeNil, - } - continue - } - m[pathNode.Elem] = &treeNode{ - node: nodeTypeValue, - data: nodeTypeValue, + tree = &treeNode{node: nodeTypeNil} + } else { + tree = &treeNode{node: nodeTypeValue} } - continue + m[pathNode.Elem] = tree + break // break for 100% test } switch v.node { case nodeTypeMap: @@ -237,15 +202,13 @@ func (s *Storage) buildTree(key, val string) error { continue } if val == "" { - s.remove(key, v) - v.data = make(map[string]*treeNode) - return nil + return v, nil } - return fmt.Errorf("property '%s' is a map but '%s' wants other type", JoinPath(path[:i+1]), key) + return nil, fmt.Errorf("property '%s' is a map but '%s' wants other type", JoinPath(path[:i+1]), key) case nodeTypeArray: if pathNode.Type != PathTypeIndex { if i < len(path)-1 && path[i+1].Type != PathTypeIndex { - return fmt.Errorf("property '%s' is an array but '%s' wants other type", JoinPath(path[:i+1]), key) + return nil, fmt.Errorf("property '%s' is an array but '%s' wants other type", JoinPath(path[:i+1]), key) } } if i < len(path)-1 { @@ -253,37 +216,29 @@ func (s *Storage) buildTree(key, val string) error { continue } if val == "" { - s.remove(key, v) - v.data = make(map[string]*treeNode) - return nil + return v, nil } - return fmt.Errorf("property '%s' is an array but '%s' wants other type", JoinPath(path[:i+1]), key) + return nil, fmt.Errorf("property '%s' is an array but '%s' wants other type", JoinPath(path[:i+1]), key) case nodeTypeValue: if i == len(path)-1 { - if val == "" { - s.remove(key, v) - } - return nil + return v, nil } - return fmt.Errorf("property '%s' is a value but '%s' wants other type", JoinPath(path[:i+1]), key) + return nil, fmt.Errorf("property '%s' is a value but '%s' wants other type", JoinPath(path[:i+1]), key) + default: + return nil, fmt.Errorf("invalid node type %d", v.node) } } - return nil + return tree, nil } -func (s *Storage) remove(key string, tree *treeNode) { - switch tree.node { - case nodeTypeValue: - delete(s.data, key) - case nodeTypeMap: - m := tree.data.(map[string]*treeNode) - for k, v := range m { - s.remove(key+"."+k, v) - } - case nodeTypeArray: - m := tree.data.(map[string]*treeNode) - for k, v := range m { - s.remove(key+"["+k+"]", v) - } +// OrderedMapKeys returns the keys of the map m in sorted order. +func OrderedMapKeys[M ~map[K]V, K cmp.Ordered, V any](m M) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) } + sort.Slice(r, func(i, j int) bool { + return r[i] < r[j] + }) + return r } diff --git a/conf/internal/storage_test.go b/conf/internal/storage_test.go index 80f13d1b..d686dd99 100644 --- a/conf/internal/storage_test.go +++ b/conf/internal/storage_test.go @@ -29,8 +29,8 @@ func TestStorage(t *testing.T) { { s = internal.NewStorage() - assert.Nil(t, s.Data()) - assert.Nil(t, s.Keys()) + assert.Equal(t, s.Data(), map[string]string{}) + assert.Equal(t, s.Keys(), []string{}) subKeys, err := s.SubKeys("m") assert.Nil(t, err) @@ -55,7 +55,7 @@ func TestStorage(t *testing.T) { err := s.Set("a", "b") assert.Nil(t, err) assert.True(t, s.Has("a")) - assert.Equal(t, s.Get("a"), "b") + // assert.Equal(t, s.Get("a"), "b") err = s.Set("a[0]", "x") assert.Error(t, err, "property 'a' is a value but 'a\\[0]' wants other type") err = s.Set("a.y", "x") @@ -68,11 +68,11 @@ func TestStorage(t *testing.T) { err = s.Set("a", "c") assert.Nil(t, err) assert.True(t, s.Has("a")) - assert.Equal(t, s.Get("a"), "c") + // assert.Equal(t, s.Get("a"), "c") err = s.Set("a", "") assert.Nil(t, err) assert.True(t, s.Has("a")) - assert.Equal(t, s.Get("a"), "") + // assert.Equal(t, s.Get("a"), "") err = s.Set("a[0]", "x") assert.Error(t, err, "property 'a' is a value but 'a\\[0]' wants other type") err = s.Set("a.y", "x") @@ -82,15 +82,12 @@ func TestStorage(t *testing.T) { err = s.Set("a", "c") assert.Nil(t, err) assert.True(t, s.Has("a")) - assert.Equal(t, s.Get("a"), "c") + // assert.Equal(t, s.Get("a"), "c") err = s.Set("a[0]", "x") assert.Error(t, err, "property 'a' is a value but 'a\\[0]' wants other type") err = s.Set("a.y", "x") assert.Error(t, err, "property 'a' is a value but 'a\\.y' wants other type") assert.Equal(t, s.Keys(), []string{"a"}) - - s1 := s.Copy() - assert.Equal(t, s1.Keys(), []string{"a"}) } // 初始值是嵌套的 KV 值 @@ -101,7 +98,7 @@ func TestStorage(t *testing.T) { assert.Nil(t, err) assert.True(t, s.Has("m")) assert.True(t, s.Has("m.x")) - assert.Equal(t, s.Get("m.x"), "y") + // assert.Equal(t, s.Get("m.x"), "y") err = s.Set("m", "w") assert.Error(t, err, "property 'm' is a map but 'm' wants other type") err = s.Set("m[0]", "f") @@ -116,42 +113,39 @@ func TestStorage(t *testing.T) { assert.Nil(t, err) assert.True(t, s.Has("m")) assert.True(t, s.Has("m.x")) - assert.Equal(t, s.Get("m.x"), "z") + // assert.Equal(t, s.Get("m.x"), "z") err = s.Set("m", "") - assert.Nil(t, err) + assert.Error(t, err, "invalid node type 2") assert.True(t, s.Has("m")) - assert.False(t, s.Has("m.x")) + assert.True(t, s.Has("m.x")) err = s.Set("m", "w") assert.Error(t, err, "property 'm' is a map but 'm' wants other type") err = s.Set("m[0]", "f") assert.Error(t, err, "property 'm' is a map but 'm\\[0]' wants other type") - assert.Equal(t, s.Keys(), []string{"m"}) + assert.Equal(t, s.Keys(), []string{"m.x"}) subKeys, err = s.SubKeys("m") assert.Nil(t, err) - assert.Nil(t, subKeys) + assert.Equal(t, subKeys, []string{"x"}) err = s.Set("m.t", "q") assert.Nil(t, err) assert.True(t, s.Has("m")) - assert.False(t, s.Has("m.x")) + assert.True(t, s.Has("m.x")) assert.True(t, s.Has("m.t")) - assert.Equal(t, s.Get("m.x"), "") - assert.Equal(t, s.Get("m.t"), "q") + // assert.Equal(t, s.Get("m.x"), "") + // assert.Equal(t, s.Get("m.t"), "q") err = s.Set("m", "w") assert.Error(t, err, "property 'm' is a map but 'm' wants other type") err = s.Set("m[0]", "f") assert.Error(t, err, "property 'm' is a map but 'm\\[0]' wants other type") err = s.Set("m.t[0]", "f") assert.Error(t, err, "property 'm.t' is a value but 'm.t\\[0]' wants other type") - assert.Equal(t, s.Keys(), []string{"m.t"}) + assert.Equal(t, s.Keys(), []string{"m.t", "m.x"}) subKeys, err = s.SubKeys("m") assert.Nil(t, err) - assert.Equal(t, subKeys, []string{"t"}) - - s1 := s.Copy() - assert.Equal(t, s1.Keys(), []string{"m.t"}) + assert.Equal(t, subKeys, []string{"t", "x"}) } // 初始值是数组 KV 值 @@ -162,7 +156,7 @@ func TestStorage(t *testing.T) { assert.Nil(t, err) assert.True(t, s.Has("s")) assert.True(t, s.Has("s[0]")) - assert.Equal(t, s.Get("s[0]"), "p") + // assert.Equal(t, s.Get("s[0]"), "p") err = s.Set("s", "w") assert.Error(t, err, "property 's' is an array but 's' wants other type") err = s.Set("s.x", "f") @@ -177,40 +171,37 @@ func TestStorage(t *testing.T) { assert.Nil(t, err) assert.True(t, s.Has("s")) assert.True(t, s.Has("s[0]")) - assert.Equal(t, s.Get("s[0]"), "q") + // assert.Equal(t, s.Get("s[0]"), "q") err = s.Set("s", "") - assert.Nil(t, err) + assert.Error(t, err, "invalid node type 3") assert.True(t, s.Has("s")) - assert.False(t, s.Has("s[0]")) + assert.True(t, s.Has("s[0]")) err = s.Set("s", "w") assert.Error(t, err, "property 's' is an array but 's' wants other type") err = s.Set("s.x", "f") assert.Error(t, err, "property 's' is an array but 's\\.x' wants other type") - assert.Equal(t, s.Keys(), []string{"s"}) + assert.Equal(t, s.Keys(), []string{"s[0]"}) subKeys, err = s.SubKeys("s") assert.Nil(t, err) - assert.Nil(t, subKeys) + assert.Equal(t, subKeys, []string{"0"}) err = s.Set("s[1]", "o") assert.Nil(t, err) assert.True(t, s.Has("s")) - assert.False(t, s.Has("s[0]")) + assert.True(t, s.Has("s[0]")) assert.True(t, s.Has("s[1]")) - assert.Equal(t, s.Get("s[0]"), "") - assert.Equal(t, s.Get("s[1]"), "o") + // assert.Equal(t, s.Get("s[0]"), "") + // assert.Equal(t, s.Get("s[1]"), "o") err = s.Set("s", "w") assert.Error(t, err, "property 's' is an array but 's' wants other type") err = s.Set("s.x", "f") assert.Error(t, err, "property 's' is an array but 's\\.x' wants other type") - assert.Equal(t, s.Keys(), []string{"s[1]"}) + assert.Equal(t, s.Keys(), []string{"s[0]", "s[1]"}) subKeys, err = s.SubKeys("s") assert.Nil(t, err) - assert.Equal(t, subKeys, []string{"1"}) - - s1 := s.Copy() - assert.Equal(t, s1.Keys(), []string{"s[1]"}) + assert.Equal(t, subKeys, []string{"0", "1"}) } { From 6027761a69407c20789fe87a62b1405ad2f15772 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 09:06:35 +0800 Subject: [PATCH 05/75] add appconf --- gs/appconf/args.go | 66 +++++++++++ gs/appconf/conf.go | 246 ++++++++++++++++++++++++++++++++++++++++ gs/appconf/envs.go | 160 ++++++++++++++++++++++++++ gs/appconf/envs_test.go | 28 +++++ 4 files changed, 500 insertions(+) create mode 100644 gs/appconf/args.go create mode 100644 gs/appconf/conf.go create mode 100644 gs/appconf/envs.go create mode 100644 gs/appconf/envs_test.go diff --git a/gs/appconf/args.go b/gs/appconf/args.go new file mode 100644 index 00000000..275f6782 --- /dev/null +++ b/gs/appconf/args.go @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appconf + +import ( + "fmt" + "os" + "strings" + + "github.com/go-spring/spring-core/conf" +) + +const CommandArgsPrefix = "GS_ARGS_PREFIX" + +// CommandArgs command-line parameters +type CommandArgs struct{} + +func NewCommandArgs() *CommandArgs { + return &CommandArgs{} +} + +// CopyTo loads parameters passed in the form of -D key[=value/true]. +func (c *CommandArgs) CopyTo(out *conf.Properties) error { + if len(os.Args) == 0 { + return nil + } + + option := "-D" + if s := strings.TrimSpace(os.Getenv(CommandArgsPrefix)); s != "" { + option = s + } + + cmdArgs := os.Args[1:] + n := len(cmdArgs) + for i := 0; i < n; i++ { + s := cmdArgs[i] + if s == option { + if i >= n-1 { + return fmt.Errorf("cmd option %s needs arg", option) + } + next := cmdArgs[i+1] + ss := strings.SplitN(next, "=", 2) + if len(ss) == 1 { + ss = append(ss, "true") + } + if err := out.Set(ss[0], ss[1]); err != nil { + return err + } + } + } + return nil +} diff --git a/gs/appconf/conf.go b/gs/appconf/conf.go new file mode 100644 index 00000000..1b79736a --- /dev/null +++ b/gs/appconf/conf.go @@ -0,0 +1,246 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appconf + +import ( + "fmt" + "os" + "strings" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/sysconf" +) + +// Configuration is a layered configuration manager. +type Configuration struct { + LocalFile *PropertySources + RemoteFile *PropertySources + RemoteProp *conf.AtomicProperties + Environment *Environment + CommandArgs *CommandArgs +} + +func NewConfiguration() *Configuration { + return &Configuration{ + LocalFile: NewPropertySources(ConfigTypeLocal, "application"), + RemoteFile: NewPropertySources(ConfigTypeRemote, "application"), + RemoteProp: conf.NewAtomicProperties(), + Environment: NewEnvironment(), + CommandArgs: NewCommandArgs(), + } +} + +func merge(out *conf.Properties, sources ...interface { + CopyTo(out *conf.Properties) error +}) error { + for _, s := range sources { + if s != nil { + if err := s.CopyTo(out); err != nil { + return err + } + } + } + return nil +} + +// Refresh merges all layers into a properties as read-only. +func (c *Configuration) Refresh() (conf.ReadOnlyProperties, error) { + + p := sysconf.Clone() + err := merge(p, c.Environment, c.CommandArgs) + if err != nil { + return nil, err + } + + localFiles, err := c.LocalFile.loadFiles(p) + if err != nil { + return nil, err + } + + remoteFiles, err := c.RemoteFile.loadFiles(p) + if err != nil { + return nil, err + } + + var sources []interface { + CopyTo(out *conf.Properties) error + } + for _, file := range localFiles { + sources = append(sources, file) + } + for _, file := range remoteFiles { + sources = append(sources, file) + } + sources = append(sources, c.RemoteProp.Load()) + sources = append(sources, c.Environment) + sources = append(sources, c.CommandArgs) + + p = sysconf.Clone() + err = merge(p, sources...) + if err != nil { + return nil, err + } + return p, nil +} + +/******************************* Bootstrapper ********************************/ + +// Bootstrapper is a layered bootstrap configuration. +type Bootstrapper struct { + LocalFile *PropertySources + Environment *Environment + CommandArgs *CommandArgs +} + +func NewBootstrapper() *Bootstrapper { + return &Bootstrapper{ + LocalFile: NewPropertySources(ConfigTypeLocal, "bootstrap"), + Environment: NewEnvironment(), + CommandArgs: NewCommandArgs(), + } +} + +// Refresh merges all layers into a properties as read-only. +func (c *Bootstrapper) Refresh() (conf.ReadOnlyProperties, error) { + + p := sysconf.Clone() + err := merge(p, c.Environment, c.CommandArgs) + if err != nil { + return nil, err + } + + localFiles, err := c.LocalFile.loadFiles(p) + if err != nil { + return nil, err + } + + var sources []interface { + CopyTo(out *conf.Properties) error + } + for _, file := range localFiles { + sources = append(sources, file) + } + sources = append(sources, c.Environment) + sources = append(sources, c.CommandArgs) + + p = sysconf.Clone() + err = merge(p, sources...) + if err != nil { + return nil, err + } + return p, nil +} + +/****************************** PropertySources ******************************/ + +type ConfigType string + +const ( + ConfigTypeLocal ConfigType = "local" + ConfigTypeRemote ConfigType = "remote" +) + +// PropertySources is a collection of locations. +type PropertySources struct { + configType ConfigType + configName string + locations []string +} + +func NewPropertySources(configType ConfigType, configName string) *PropertySources { + return &PropertySources{ + configType: configType, + configName: configName, + } +} + +// Reset resets the locations. +func (p *PropertySources) Reset() { + p.locations = nil +} + +// AddLocation adds a location. +func (p *PropertySources) AddLocation(location ...string) { + p.locations = append(p.locations, location...) +} + +// getDefaultLocations returns the default locations. +func (p *PropertySources) getDefaultLocations(resolver *conf.Properties) (_ []string, err error) { + + var configDir string + if p.configType == ConfigTypeLocal { + configDir, err = resolver.Resolve("${spring.application.config.dir:=./conf}") + } else if p.configType == ConfigTypeRemote { + configDir, err = resolver.Resolve("${spring.cloud.config.dir:=./conf/remote}") + } else { + return nil, fmt.Errorf("unknown config type: %s", p.configType) + } + if err != nil { + return nil, err + } + + locations := []string{ + fmt.Sprintf("%s/%s.properties", configDir, p.configName), + fmt.Sprintf("%s/%s.yaml", configDir, p.configName), + fmt.Sprintf("%s/%s.toml", configDir, p.configName), + fmt.Sprintf("%s/%s.json", configDir, p.configName), + } + + activeProfiles, err := resolver.Resolve("${spring.profiles.active:=}") + if err != nil { + return nil, err + } + if activeProfiles = strings.TrimSpace(activeProfiles); activeProfiles != "" { + ss := strings.Split(activeProfiles, ",") + for _, s := range ss { + if s = strings.TrimSpace(s); s != "" { + locations = append(locations, []string{ + fmt.Sprintf("%s/%s-%s.properties", configDir, p.configName, s), + fmt.Sprintf("%s/%s-%s.yaml", configDir, p.configName, s), + fmt.Sprintf("%s/%s-%s.toml", configDir, p.configName, s), + fmt.Sprintf("%s/%s-%s.json", configDir, p.configName, s), + }...) + } + } + } + return locations, nil +} + +// loadFiles loads all locations and returns a list of properties. +func (p *PropertySources) loadFiles(resolver *conf.Properties) ([]*conf.Properties, error) { + locations, err := p.getDefaultLocations(resolver) + if err != nil { + return nil, err + } + locations = append(locations, p.locations...) + var files []*conf.Properties + for _, s := range locations { + filename, err := resolver.Resolve(s) + if err != nil { + return nil, err + } + c, err := conf.Load(filename) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + files = append(files, c) + } + return files, nil +} diff --git a/gs/appconf/envs.go b/gs/appconf/envs.go new file mode 100644 index 00000000..c9b1c2ae --- /dev/null +++ b/gs/appconf/envs.go @@ -0,0 +1,160 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appconf + +import ( + "os" + "regexp" + "strings" + + "github.com/go-spring/spring-core/conf" +) + +const ( + EnvironmentPrefix = "GS_ENVS_PREFIX" + IncludeEnvPatterns = "INCLUDE_ENV_PATTERNS" + ExcludeEnvPatterns = "EXCLUDE_ENV_PATTERNS" +) + +// Environment environment variable +type Environment struct{} + +func NewEnvironment() *Environment { + return &Environment{} +} + +func lookupEnv(environ []string, key string) (value string, found bool) { + key = strings.TrimSpace(key) + "=" + for _, s := range environ { + if strings.HasPrefix(s, key) { + v := strings.TrimPrefix(s, key) + return strings.TrimSpace(v), true + } + } + return "", false +} + +// CopyTo add environment variables that matches IncludeEnvPatterns and +// exclude environment variables that matches ExcludeEnvPatterns. +func (c *Environment) CopyTo(p *conf.Properties) error { + environ := os.Environ() + if len(environ) == 0 { + return nil + } + + prefix := "GS_" + if s := strings.TrimSpace(os.Getenv(EnvironmentPrefix)); s != "" { + prefix = s + } + + toRex := func(patterns []string) ([]*regexp.Regexp, error) { + var rex []*regexp.Regexp + for _, v := range patterns { + exp, err := regexp.Compile(v) + if err != nil { + return nil, err + } + rex = append(rex, exp) + } + return rex, nil + } + + includes := []string{".*"} + if s, ok := lookupEnv(environ, IncludeEnvPatterns); ok { + includes = strings.Split(s, ",") + } + includeRex, err := toRex(includes) + if err != nil { + return err + } + + var excludes []string + if s, ok := lookupEnv(environ, ExcludeEnvPatterns); ok { + excludes = strings.Split(s, ",") + } + excludeRex, err := toRex(excludes) + if err != nil { + return err + } + + matches := func(rex []*regexp.Regexp, s string) bool { + for _, r := range rex { + if r.MatchString(s) { + return true + } + } + return false + } + + for _, env := range environ { + ss := strings.SplitN(env, "=", 2) + k, v := ss[0], "" + if len(ss) > 1 { + v = ss[1] + } + + var propKey string + if strings.HasPrefix(k, prefix) { + propKey = strings.TrimPrefix(k, prefix) + } else if matches(includeRex, k) && !matches(excludeRex, k) { + propKey = k + } else { + continue + } + + propKey = strings.ToLower(replaceKey(propKey)) + if err = p.Set(propKey, v); err != nil { + return err + } + } + return nil +} + +// replaceKey replace '_' with '.' +func replaceKey(s string) string { + var b strings.Builder + + right := len(s) - 1 + for { + if s[right] != '_' { + break + } + right-- + } + + left := 0 + for { + if s[left] != '_' { + break + } + b.WriteByte('_') + left++ + } + + for i := left; i <= right; i++ { + if s[i] == '_' { + b.WriteByte('.') + continue + } + b.WriteByte(s[i]) + } + + for i := right + 1; i < len(s); i++ { + b.WriteByte('_') + } + return b.String() +} diff --git a/gs/appconf/envs_test.go b/gs/appconf/envs_test.go new file mode 100644 index 00000000..f2a94ea5 --- /dev/null +++ b/gs/appconf/envs_test.go @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appconf + +import ( + "testing" +) + +func TestReplaceKey(t *testing.T) { + s := replaceKey("__a_b__") + if s != "__a.b__" { + t.Fatal("replaceKey error") + } +} From 274656c04f2583a57d039e185a5a4fd50a73b2f2 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 09:13:06 +0800 Subject: [PATCH 06/75] code refactor --- conf/conf.go | 8 ++-- conf/flat.go | 28 +++++------ conf/{internal => store}/path.go | 2 +- conf/{internal => store}/path_test.go | 46 +++++++++---------- conf/{internal/storage.go => store/store.go} | 2 +- .../storage_test.go => store/store_test.go} | 16 +++---- conf/util.go | 28 +++++------ 7 files changed, 66 insertions(+), 64 deletions(-) rename conf/{internal => store}/path.go (99%) rename conf/{internal => store}/path_test.go (76%) rename conf/{internal/storage.go => store/store.go} (99%) rename conf/{internal/storage_test.go => store/store_test.go} (96%) diff --git a/conf/conf.go b/conf/conf.go index 32a1fc1d..76ccdf99 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -14,8 +14,6 @@ * limitations under the License. */ -// Package conf reads configuration from many format file, such as Java -// properties, yaml, toml, etc. package conf import ( @@ -29,11 +27,11 @@ import ( "sync/atomic" "time" - "github.com/go-spring/spring-core/conf/internal" "github.com/go-spring/spring-core/conf/reader/json" "github.com/go-spring/spring-core/conf/reader/prop" "github.com/go-spring/spring-core/conf/reader/toml" "github.com/go-spring/spring-core/conf/reader/yaml" + "github.com/go-spring/spring-core/conf/store" "github.com/go-spring/spring-core/expr" "github.com/spf13/cast" ) @@ -108,13 +106,13 @@ var _ ReadOnlyProperties = (*Properties)(nil) // but it costs more CPU time when getting properties because it reads property node // by node. So `conf` uses a tree to strictly verify and a flat map to store. type Properties struct { - storage *internal.Storage + storage *store.Storage } // NewProperties creates empty *Properties. func NewProperties() *Properties { return &Properties{ - storage: internal.NewStorage(), + storage: store.NewStorage(), } } diff --git a/conf/flat.go b/conf/flat.go index 8ca2e45c..6c887607 100644 --- a/conf/flat.go +++ b/conf/flat.go @@ -1,16 +1,18 @@ -// Copyright 2024 github.com/lvan100 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package conf diff --git a/conf/internal/path.go b/conf/store/path.go similarity index 99% rename from conf/internal/path.go rename to conf/store/path.go index ff8275d4..4dc9233f 100644 --- a/conf/internal/path.go +++ b/conf/store/path.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package internal +package store import ( "fmt" diff --git a/conf/internal/path_test.go b/conf/store/path_test.go similarity index 76% rename from conf/internal/path_test.go rename to conf/store/path_test.go index 3f2d1839..c3e83754 100644 --- a/conf/internal/path_test.go +++ b/conf/store/path_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package internal_test +package store_test import ( "errors" @@ -22,14 +22,14 @@ import ( "testing" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf/internal" + "github.com/go-spring/spring-core/conf/store" ) func TestSplitPath(t *testing.T) { var testcases = []struct { Key string Err error - Path []internal.Path + Path []store.Path }{ { Key: "", @@ -56,8 +56,8 @@ func TestSplitPath(t *testing.T) { }, { Key: "[0]", - Path: []internal.Path{ - {internal.PathTypeIndex, "0"}, + Path: []store.Path{ + {store.PathTypeIndex, "0"}, }, }, { @@ -82,8 +82,8 @@ func TestSplitPath(t *testing.T) { }, { Key: "a", - Path: []internal.Path{ - {internal.PathTypeKey, "a"}, + Path: []store.Path{ + {store.PathTypeKey, "a"}, }, }, { @@ -92,9 +92,9 @@ func TestSplitPath(t *testing.T) { }, { Key: "a.b", - Path: []internal.Path{ - {internal.PathTypeKey, "a"}, - {internal.PathTypeKey, "b"}, + Path: []store.Path{ + {store.PathTypeKey, "a"}, + {store.PathTypeKey, "b"}, }, }, { @@ -107,9 +107,9 @@ func TestSplitPath(t *testing.T) { }, { Key: "a[0]", - Path: []internal.Path{ - {internal.PathTypeKey, "a"}, - {internal.PathTypeIndex, "0"}, + Path: []store.Path{ + {store.PathTypeKey, "a"}, + {store.PathTypeIndex, "0"}, }, }, { @@ -118,10 +118,10 @@ func TestSplitPath(t *testing.T) { }, { Key: "a[0].b", - Path: []internal.Path{ - {internal.PathTypeKey, "a"}, - {internal.PathTypeIndex, "0"}, - {internal.PathTypeKey, "b"}, + Path: []store.Path{ + {store.PathTypeKey, "a"}, + {store.PathTypeIndex, "0"}, + {store.PathTypeKey, "b"}, }, }, { @@ -130,10 +130,10 @@ func TestSplitPath(t *testing.T) { }, { Key: "a[0][0]", - Path: []internal.Path{ - {internal.PathTypeKey, "a"}, - {internal.PathTypeIndex, "0"}, - {internal.PathTypeIndex, "0"}, + Path: []store.Path{ + {store.PathTypeKey, "a"}, + {store.PathTypeIndex, "0"}, + {store.PathTypeIndex, "0"}, }, }, { @@ -142,11 +142,11 @@ func TestSplitPath(t *testing.T) { }, } for i, c := range testcases { - p, err := internal.SplitPath(c.Key) + p, err := store.SplitPath(c.Key) assert.Equal(t, err, c.Err, fmt.Sprintf("index: %d key: %q", i, c.Key)) assert.Equal(t, p, c.Path, fmt.Sprintf("index:%d key: %q", i, c.Key)) if err == nil { - s := internal.JoinPath(p) + s := store.JoinPath(p) assert.Equal(t, s, c.Key, fmt.Sprintf("index:%d key: %q", i, c.Key)) } } diff --git a/conf/internal/storage.go b/conf/store/store.go similarity index 99% rename from conf/internal/storage.go rename to conf/store/store.go index f66313c2..1bd02366 100644 --- a/conf/internal/storage.go +++ b/conf/store/store.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package internal +package store import ( "cmp" diff --git a/conf/internal/storage_test.go b/conf/store/store_test.go similarity index 96% rename from conf/internal/storage_test.go rename to conf/store/store_test.go index d686dd99..795b8dc2 100644 --- a/conf/internal/storage_test.go +++ b/conf/store/store_test.go @@ -14,21 +14,21 @@ * limitations under the License. */ -package internal_test +package store_test import ( "testing" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf/internal" + "github.com/go-spring/spring-core/conf/store" ) func TestStorage(t *testing.T) { - var s *internal.Storage + var s *store.Storage { - s = internal.NewStorage() + s = store.NewStorage() assert.Equal(t, s.Data(), map[string]string{}) assert.Equal(t, s.Keys(), []string{}) @@ -50,7 +50,7 @@ func TestStorage(t *testing.T) { // 初始值是简单的 KV 值 { - s = internal.NewStorage() + s = store.NewStorage() err := s.Set("a", "b") assert.Nil(t, err) @@ -92,7 +92,7 @@ func TestStorage(t *testing.T) { // 初始值是嵌套的 KV 值 { - s = internal.NewStorage() + s = store.NewStorage() err := s.Set("m.x", "y") assert.Nil(t, err) @@ -150,7 +150,7 @@ func TestStorage(t *testing.T) { // 初始值是数组 KV 值 { - s = internal.NewStorage() + s = store.NewStorage() err := s.Set("s[0]", "p") assert.Nil(t, err) @@ -205,7 +205,7 @@ func TestStorage(t *testing.T) { } { - s = internal.NewStorage() + s = store.NewStorage() err := s.Set("a.b[0].c", "") assert.Nil(t, err) diff --git a/conf/util.go b/conf/util.go index 7795d63f..7c21b072 100644 --- a/conf/util.go +++ b/conf/util.go @@ -1,16 +1,18 @@ -// Copyright 2024 github.com/lvan100 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package conf From 0d2943a6c4c5b64cfb0dda0484534a7ac32435a5 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 09:40:56 +0800 Subject: [PATCH 07/75] code refactor --- dync/dync.go | 23 +++-- expr/expr.go | 21 +++-- gs/app.go | 195 ++++++++++------------------------------ gs/app_args.go | 45 ---------- gs/app_args_test.go | 43 --------- gs/app_bootstrap.go | 80 ++++------------- gs/app_configuration.go | 121 ------------------------- gs/app_resource.go | 55 ------------ gs/app_test.go | 67 +++----------- gs/boot.go | 21 ++--- gs/gs.go | 4 +- gs/gs_context.go | 2 +- gs/gs_dynamic_test.go | 8 +- gs/gstest/gstest.go | 80 +++++++++++++++++ 14 files changed, 201 insertions(+), 564 deletions(-) delete mode 100644 gs/app_args.go delete mode 100644 gs/app_args_test.go delete mode 100644 gs/app_configuration.go delete mode 100644 gs/app_resource.go create mode 100644 gs/gstest/gstest.go diff --git a/dync/dync.go b/dync/dync.go index fc8c60a0..1790e585 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -46,7 +46,7 @@ type Properties struct { func New() *Properties { p := &Properties{} - p.value.Store(conf.New()) + p.value.Store(conf.NewProperties()) return p } @@ -70,7 +70,7 @@ func (p *Properties) Resolve(s string) (string, error) { return p.load().Resolve(s) } -func (p *Properties) Bind(i interface{}, opts ...conf.BindOption) error { +func (p *Properties) Bind(i interface{}, opts ...conf.BindArg) error { return p.load().Bind(i, opts...) } @@ -78,10 +78,7 @@ func (p *Properties) Update(m map[string]interface{}) error { flat := make(map[string]string) for key, val := range m { - err := conf.Flatten(key, val, flat) - if err != nil { - return err - } + conf.FlattenValue(key, val, flat) } keys := make([]string, 0, len(flat)) @@ -90,9 +87,13 @@ func (p *Properties) Update(m map[string]interface{}) error { } sort.Strings(keys) - prop := p.load().Copy() + prop := conf.NewProperties() + err := p.load().CopyTo(prop) + if err != nil { + return err + } for _, k := range keys { - err := prop.Set(k, flat[k]) + err = prop.Set(k, flat[k]) if err != nil { return err } @@ -252,7 +253,11 @@ func Validate(val interface{}, param conf.BindParam) error { if param.Validate == "" { return nil } - if b, err := expr.Eval(param.Validate, val); err != nil { + tag, ok := param.Validate.Lookup("expr") + if !ok { + return nil + } + if b, err := expr.Eval(tag, val); err != nil { return err } else if !b { return fmt.Errorf("validate failed on %q for value %v", param.Validate, val) diff --git a/expr/expr.go b/expr/expr.go index 2208cb1b..74f60e5f 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -31,16 +31,23 @@ func (d *Validator) Name() string { // Field validates the field with the given tag and value. func (d *Validator) Field(tag string, i interface{}) error { - r, err := expr.Eval(tag, map[string]interface{}{"$": i}) + if ret, err := Eval(tag, i); err != nil { + return err + } else if !ret { + return fmt.Errorf("validate failed on %q for value %v", tag, i) + } + return nil +} + +// Eval returns the value for the expression expr. +func Eval(input string, val interface{}) (bool, error) { + r, err := expr.Eval(input, map[string]interface{}{"$": val}) if err != nil { - return fmt.Errorf("eval %q returns error, %w", tag, err) + return false, fmt.Errorf("eval %q returns error, %w", input, err) } ret, ok := r.(bool) if !ok { - return fmt.Errorf("eval %q doesn't return bool value", tag) - } - if !ret { - return fmt.Errorf("validate failed on %q for value %v", tag, i) + return false, fmt.Errorf("eval %q doesn't return bool value", input) } - return nil + return ret, nil } diff --git a/gs/app.go b/gs/app.go index b0241e82..c9e7d5e1 100644 --- a/gs/app.go +++ b/gs/app.go @@ -19,18 +19,12 @@ package gs import ( "context" "fmt" - "io/ioutil" "os" "os/signal" - "path/filepath" "reflect" - "strconv" "strings" "syscall" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/arg" ) @@ -56,10 +50,8 @@ type tempApp struct { type App struct { *tempApp - logger *log.Logger - c *container - b *bootstrap + b *Bootstrapper exitChan chan struct{} @@ -81,89 +73,63 @@ func (app *App) Banner(banner string) { app.banner = banner } -func (app *App) Run() error { - - config := ` - - - - - - - - - - - - ` - if err := log.RefreshBuffer(config, ".xml"); err != nil { - return err - } +func (app *App) Start() (Context, error) { app.Object(app) - app.logger = log.GetLogger(util.TypeName(app)) - - // 响应控制台的 Ctrl+C 及 kill 命令。 - go func() { - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt, syscall.SIGTERM) - sig := <-ch - app.ShutDown(fmt.Sprintf("signal %v", sig)) - }() if err := app.start(); err != nil { - return err + return nil, err } + return app.c, nil +} - <-app.exitChan +func (app *App) Stop() { - if app.b != nil { - app.b.c.Close() - } + // if app.b != nil { + // app.b.c.Close() + // } app.c.Close() - app.logger.Info("application exited") - return nil } -func (app *App) clear() { - app.c.clear() - if app.b != nil { - app.b.clear() - } - app.tempApp = nil -} - -func (app *App) start() error { - - e := &configuration{ - p: conf.New(), - resourceLocator: new(defaultResourceLocator), - } - - if err := e.prepare(); err != nil { +func (app *App) Run() error { + _, err := app.Start() + if err != nil { return err } - showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) - if showBanner { - app.printBanner(app.getBanner(e)) - } + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt, syscall.SIGTERM) + sig := <-ch + app.ShutDown(fmt.Sprintf("signal %v", sig)) + }() - if app.b != nil { - if err := app.b.start(e); err != nil { - return err - } - } + <-app.exitChan + return nil +} - if err := app.loadProperties(e); err != nil { - return err - } +func (app *App) start() error { - // 保存从环境变量和命令行解析的属性 - for _, k := range e.p.Keys() { - app.c.initProperties.Set(k, e.p.Get(k)) - } + // showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) + // if showBanner { + // app.printBanner(app.getBanner(e)) + // } + + // if app.b != nil { + // if err := app.b.start(e); err != nil { + // return err + // } + // } + // + // if err := app.loadProperties(e); err != nil { + // return err + // } + + // // 保存从环境变量和命令行解析的属性 + // for _, k := range e.p.Keys() { + // app.c.initProperties.Set(k, e.p.Get(k)) + // } if err := app.c.refresh(false); err != nil { return err @@ -179,8 +145,6 @@ func (app *App) start() error { event.OnAppStart(app.c) } - app.clear() - // 通知应用停止事件 app.c.Go(func(ctx context.Context) { <-ctx.Done() @@ -189,7 +153,6 @@ func (app *App) start() error { } }) - app.logger.Info("application started successfully") return nil } @@ -203,20 +166,16 @@ const DefaultBanner = ` |___/ |_| |___/ ` -func (app *App) getBanner(e *configuration) string { +func (app *App) getBanner() string { if app.banner != "" { return app.banner } - resources, err := e.resourceLocator.Locate("banner.txt") - if err != nil { - return "" - } banner := DefaultBanner - for _, resource := range resources { - if b, _ := ioutil.ReadAll(resource); b != nil { - banner = string(b) - } - } + // for _, resource := range resources { + // if b, _ := ioutil.ReadAll(resource); b != nil { + // banner = string(b) + // } + // } return banner } @@ -249,66 +208,8 @@ func (app *App) printBanner(banner string) { fmt.Println(string(padding) + Version + "\n") } -func (app *App) loadProperties(e *configuration) error { - var resources []Resource - - for _, ext := range e.ConfigExtensions { - sources, err := app.loadResource(e, "application"+ext) - if err != nil { - return err - } - resources = append(resources, sources...) - } - - for _, profile := range e.ActiveProfiles { - for _, ext := range e.ConfigExtensions { - sources, err := app.loadResource(e, "application-"+profile+ext) - if err != nil { - return err - } - resources = append(resources, sources...) - } - } - - for _, resource := range resources { - b, err := ioutil.ReadAll(resource) - if err != nil { - return err - } - p, err := conf.Bytes(b, filepath.Ext(resource.Name())) - if err != nil { - return err - } - for _, key := range p.Keys() { - app.c.initProperties.Set(key, p.Get(key)) - } - } - - return nil -} - -func (app *App) loadResource(e *configuration, filename string) ([]Resource, error) { - - var locators []ResourceLocator - locators = append(locators, e.resourceLocator) - if app.b != nil { - locators = append(locators, app.b.resourceLocators...) - } - - var resources []Resource - for _, locator := range locators { - sources, err := locator.Locate(filename) - if err != nil { - return nil, err - } - resources = append(resources, sources...) - } - return resources, nil -} - // ShutDown 关闭执行器 func (app *App) ShutDown(msg ...string) { - app.logger.Infof("program will exit %s", strings.Join(msg, " ")) select { case <-app.exitChan: // chan 已关闭,无需再次关闭。 @@ -318,7 +219,7 @@ func (app *App) ShutDown(msg ...string) { } // Bootstrap 返回 *bootstrap 对象。 -func (app *App) Bootstrap() *bootstrap { +func (app *App) Bootstrap() *Bootstrapper { if app.b == nil { app.b = newBootstrap() } diff --git a/gs/app_args.go b/gs/app_args.go deleted file mode 100644 index 61c0852c..00000000 --- a/gs/app_args.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs - -import ( - "errors" - "strings" - - "github.com/go-spring/spring-core/conf" -) - -// LoadCmdArgs 加载以 -D key=value 或者 -D key[=true] 形式传入的命令行参数。 -func LoadCmdArgs(args []string, p *conf.Properties) error { - for i := 0; i < len(args); i++ { - s := args[i] - if s == "-D" { - if i >= len(args)-1 { - return errors.New("cmd option -D needs arg") - } - next := args[i+1] - ss := strings.SplitN(next, "=", 2) - if len(ss) == 1 { - ss = append(ss, "true") - } - if err := p.Set(ss[0], ss[1]); err != nil { - return err - } - } - } - return nil -} diff --git a/gs/app_args_test.go b/gs/app_args_test.go deleted file mode 100644 index 44a5a391..00000000 --- a/gs/app_args_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs_test - -import ( - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs" -) - -func TestLoadCmdArgs(t *testing.T) { - t.Run("", func(t *testing.T) { - err := gs.LoadCmdArgs([]string{"-D"}, nil) - assert.Error(t, err, "cmd option -D needs arg") - }) - t.Run("", func(t *testing.T) { - p := conf.New() - err := gs.LoadCmdArgs([]string{ - "-D", "language=go", - "-D", "server", - }, p) - assert.Nil(t, err) - assert.Equal(t, p.Keys(), []string{"language", "server"}) - assert.Equal(t, p.Get("language"), "go") - assert.Equal(t, p.Get("server"), "true") - }) -} diff --git a/gs/app_bootstrap.go b/gs/app_bootstrap.go index 9af2c6a4..2c38ca58 100644 --- a/gs/app_bootstrap.go +++ b/gs/app_bootstrap.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,97 +19,51 @@ package gs import ( "reflect" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/arg" ) -type tempBootstrap struct { - resourceLocators []ResourceLocator `autowire:""` -} - -type bootstrap struct { - *tempBootstrap +type Bootstrapper struct { c *container } -func newBootstrap() *bootstrap { - return &bootstrap{ +func newBootstrap() *Bootstrapper { + return &Bootstrapper{ c: New().(*container), } } -func (b *bootstrap) clear() { - b.tempBootstrap = nil -} - // OnProperty 参考 App.OnProperty 的解释。 -func (b *bootstrap) OnProperty(key string, fn interface{}) { +func (b *Bootstrapper) OnProperty(key string, fn interface{}) { b.c.OnProperty(key, fn) } // Property 参考 Container.Property 的解释。 -func (b *bootstrap) Property(key string, value interface{}) { +func (b *Bootstrapper) Property(key string, value interface{}) { b.c.Property(key, value) } // Object 参考 Container.Object 的解释。 -func (b *bootstrap) Object(i interface{}) *BeanDefinition { +func (b *Bootstrapper) Object(i interface{}) *BeanDefinition { return b.c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *bootstrap) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { +func (b *Bootstrapper) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { return b.c.Accept(NewBean(ctor, args...)) } -// ResourceLocator 参考 Container.Object 的解释。 -func (b *bootstrap) ResourceLocator(i interface{}) *BeanDefinition { - return b.c.Accept(NewBean(reflect.ValueOf(i))).Export((*ResourceLocator)(nil)) -} - -func (b *bootstrap) start(e *configuration) error { +func (b *Bootstrapper) start() error { b.c.Object(b) - if err := b.loadBootstrap(e); err != nil { - return err - } - - // 保存从环境变量和命令行解析的属性 - for _, k := range e.p.Keys() { - b.c.initProperties.Set(k, e.p.Get(k)) - } + // if err := b.loadBootstrap(e); err != nil { + // return err + // } + // + // // 保存从环境变量和命令行解析的属性 + // for _, k := range e.p.Keys() { + // b.c.initProperties.Set(k, e.p.Get(k)) + // } return b.c.Refresh() } - -func (b *bootstrap) loadBootstrap(e *configuration) error { - if err := b.loadConfigFile(e, "bootstrap"); err != nil { - return err - } - for _, profile := range e.ActiveProfiles { - if err := b.loadConfigFile(e, "bootstrap-"+profile); err != nil { - return err - } - } - return nil -} - -func (b *bootstrap) loadConfigFile(e *configuration, filename string) error { - for _, ext := range e.ConfigExtensions { - resources, err := e.resourceLocator.Locate(filename + ext) - if err != nil { - return err - } - p := conf.New() - for _, file := range resources { - if err = p.Load(file.Name()); err != nil { - return err - } - } - for _, key := range p.Keys() { - b.c.initProperties.Set(key, p.Get(key)) - } - } - return nil -} diff --git a/gs/app_configuration.go b/gs/app_configuration.go deleted file mode 100644 index 8ec57429..00000000 --- a/gs/app_configuration.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs - -import ( - "os" - "regexp" - "strings" - - "github.com/go-spring/spring-core/conf" -) - -// EnvPrefix 属性覆盖的环境变量需要携带该前缀。 -const EnvPrefix = "GS_" - -// IncludeEnvPatterns 只加载符合条件的环境变量。 -const IncludeEnvPatterns = "INCLUDE_ENV_PATTERNS" - -// ExcludeEnvPatterns 排除符合条件的环境变量。 -const ExcludeEnvPatterns = "EXCLUDE_ENV_PATTERNS" - -type configuration struct { - p *conf.Properties - - resourceLocator ResourceLocator - ActiveProfiles []string `value:"${spring.profiles.active:=}"` - ConfigExtensions []string `value:"${spring.config.extensions:=.properties,.yaml,.yml,.toml,.tml}"` -} - -// loadSystemEnv 添加符合 includes 条件的环境变量,排除符合 excludes 条件的 -// 环境变量。如果发现存在允许通过环境变量覆盖的属性名,那么保存时转换成真正的属性名。 -func loadSystemEnv(p *conf.Properties) error { - - toRex := func(patterns []string) ([]*regexp.Regexp, error) { - var rex []*regexp.Regexp - for _, v := range patterns { - exp, err := regexp.Compile(v) - if err != nil { - return nil, err - } - rex = append(rex, exp) - } - return rex, nil - } - - includes := []string{".*"} - if s, ok := os.LookupEnv(IncludeEnvPatterns); ok { - includes = strings.Split(s, ",") - } - includeRex, err := toRex(includes) - if err != nil { - return err - } - - var excludes []string - if s, ok := os.LookupEnv(ExcludeEnvPatterns); ok { - excludes = strings.Split(s, ",") - } - excludeRex, err := toRex(excludes) - if err != nil { - return err - } - - matches := func(rex []*regexp.Regexp, s string) bool { - for _, r := range rex { - if r.MatchString(s) { - return true - } - } - return false - } - - for _, env := range os.Environ() { - ss := strings.SplitN(env, "=", 2) - k, v := ss[0], "" - if len(ss) > 1 { - v = ss[1] - } - if strings.HasPrefix(k, EnvPrefix) { - propKey := strings.TrimPrefix(k, EnvPrefix) - propKey = strings.ReplaceAll(propKey, "_", ".") - propKey = strings.ToLower(propKey) - p.Set(propKey, v) - continue - } - if matches(includeRex, k) && !matches(excludeRex, k) { - p.Set(k, v) - } - } - return nil -} - -func (e *configuration) prepare() error { - if err := loadSystemEnv(e.p); err != nil { - return err - } - if err := LoadCmdArgs(os.Args, e.p); err != nil { - return err - } - if err := e.p.Bind(e); err != nil { - return err - } - if err := e.p.Bind(e.resourceLocator); err != nil { - return err - } - return nil -} diff --git a/gs/app_resource.go b/gs/app_resource.go deleted file mode 100644 index 6d24ff2a..00000000 --- a/gs/app_resource.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs - -import ( - "io" - "os" - "path/filepath" -) - -// Resource 具有名字的 io.Reader 接口称为资源。 -type Resource interface { - io.Reader - Name() string -} - -// ResourceLocator 查找名字为 filename 的资源。 -type ResourceLocator interface { - Locate(filename string) ([]Resource, error) -} - -// defaultResourceLocator 从本地文件系统中查找资源。 -type defaultResourceLocator struct { - configLocations []string `value:"${spring.config.locations:=config/}"` -} - -func (locator *defaultResourceLocator) Locate(filename string) ([]Resource, error) { - var resources []Resource - for _, location := range locator.configLocations { - fileLocation := filepath.Join(location, filename) - file, err := os.Open(fileLocation) - if os.IsNotExist(err) { - continue - } - if err != nil { - return nil, err - } - resources = append(resources, file) - } - return resources, nil -} diff --git a/gs/app_test.go b/gs/app_test.go index 943596b0..bca10f9a 100644 --- a/gs/app_test.go +++ b/gs/app_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,65 +19,22 @@ package gs_test import ( "os" "testing" - "time" "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/gs" -) - -func startApplication(cfgLocation string, fn func(gs.Context)) *gs.App { - - app := gs.NewApp() - gs.Setenv("GS_SPRING_BANNER_VISIBLE", "true") - gs.Setenv("GS_SPRING_CONFIG_LOCATIONS", cfgLocation) - - type PandoraAware struct{} - app.Provide(func(b gs.Context) PandoraAware { - fn(b) - return PandoraAware{} - }) - go func() { - if err := app.Run(); err != nil { - panic(err) - } - }() + "github.com/go-spring/spring-core/gs/gstest" +) - time.Sleep(100 * time.Millisecond) - return app +func TestMain(m *testing.M) { + err := gstest.Init() + if err != nil { + panic(err) + } + os.Exit(gstest.Run(m)) } func TestConfig(t *testing.T) { - - t.Run("config via env", func(t *testing.T) { - os.Clearenv() - gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") - app := startApplication("testdata/config/", func(ctx gs.Context) { - assert.Equal(t, ctx.Prop("spring.profiles.active"), "dev") - }) - defer app.ShutDown("run test end") - }) - - t.Run("config via env 2", func(t *testing.T) { - os.Clearenv() - gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") - app := startApplication("testdata/config/", func(ctx gs.Context) { - assert.Equal(t, ctx.Prop("spring.profiles.active"), "dev") - }) - defer app.ShutDown("run test end") - }) - - t.Run("profile via env&config 2", func(t *testing.T) { - os.Clearenv() - gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") - app := startApplication("testdata/config/", func(ctx gs.Context) { - assert.Equal(t, ctx.Prop("spring.profiles.active"), "dev") - // keys := ctx.Properties().Keys() - // sort.Strings(keys) - // for _, k := range keys { - // fmt.Println(k, "=", ctx.Prop(k)) - // } - }) - defer app.ShutDown("run test end") - }) + os.Clearenv() + os.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") + assert.Equal(t, gstest.GetProperty("spring.profiles.active"), "dev") } diff --git a/gs/boot.go b/gs/boot.go index ef760150..6c51abc9 100644 --- a/gs/boot.go +++ b/gs/boot.go @@ -17,19 +17,21 @@ package gs import ( - "os" "reflect" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/gs/arg" ) var app = NewApp() -// Setenv 封装 os.Setenv 函数,如果发生 error 会 panic 。 -func Setenv(key string, value string) { - err := os.Setenv(key, value) - util.Panic(err).When(err != nil) +// Start 启动程序。 +func Start() (Context, error) { + return app.Start() +} + +// Stop 停止程序。 +func Stop() { + app.Stop() } // Run 启动程序。 @@ -42,13 +44,8 @@ func ShutDown(msg ...string) { app.ShutDown(msg...) } -// Banner 参考 App.Banner 的解释。 -func Banner(banner string) { - app.Banner(banner) -} - // Bootstrap 参考 App.Bootstrap 的解释。 -func Bootstrap() *bootstrap { +func Bootstrap() *Bootstrapper { return app.Bootstrap() } diff --git a/gs/gs.go b/gs/gs.go index 8e23c0e6..010f06f9 100755 --- a/gs/gs.go +++ b/gs/gs.go @@ -77,7 +77,7 @@ type Context interface { Has(key string) bool Prop(key string, opts ...conf.GetOption) string Resolve(s string) (string, error) - Bind(i interface{}, opts ...conf.BindOption) error + Bind(i interface{}, opts ...conf.BindArg) error Get(i interface{}, selectors ...util.BeanSelector) error Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) @@ -125,7 +125,7 @@ func New() Container { cancel: cancel, p: dync.New(), tempContainer: &tempContainer{ - initProperties: conf.New(), + initProperties: conf.NewProperties(), beansByName: make(map[string][]*BeanDefinition), beansByType: make(map[reflect.Type][]*BeanDefinition), mapOfOnProperty: make(map[string]interface{}), diff --git a/gs/gs_context.go b/gs/gs_context.go index 42608709..a68ba088 100644 --- a/gs/gs_context.go +++ b/gs/gs_context.go @@ -41,7 +41,7 @@ func (c *container) Resolve(s string) (string, error) { return c.p.Resolve(s) } -func (c *container) Bind(i interface{}, opts ...conf.BindOption) error { +func (c *container) Bind(i interface{}, opts ...conf.BindArg) error { return c.p.Bind(i, opts...) } diff --git a/gs/gs_dynamic_test.go b/gs/gs_dynamic_test.go index 9f48fcad..c67f3855 100644 --- a/gs/gs_dynamic_test.go +++ b/gs/gs_dynamic_test.go @@ -83,7 +83,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.New() + p := conf.NewProperties() p.Set("int", 4) p.Set("float", 2.3) p.Set("map.a", 1) @@ -107,7 +107,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.New() + p := conf.NewProperties() p.Set("int", 6) p.Set("float", 5.1) p.Set("map.a", 9) @@ -121,7 +121,7 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.slice[0]", 2) p.Set("wrapper.slice[1]", 1) err = c.Properties().Refresh(p) - assert.Error(t, err, "validate failed on \"\\$<6\" for value 6") + assert.Error(t, err, "validate failed on \"value:\\\"${int:=3}\\\" expr:\\\"$<6\\\"\" for value 9") } { @@ -132,7 +132,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.New() + p := conf.NewProperties() p.Set("int", 1) p.Set("float", 5.1) p.Set("map.a", 9) diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go new file mode 100644 index 00000000..59435257 --- /dev/null +++ b/gs/gstest/gstest.go @@ -0,0 +1,80 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gstest + +import ( + "testing" + + "github.com/go-spring/spring-base/util" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs" + "github.com/go-spring/spring-core/gs/arg" +) + +var ctx gs.Context + +// Init 初始化测试环境 +func Init() error { + c, err := gs.Start() + if err != nil { + return err + } + ctx = c + return nil +} + +// Run 运行测试用例 +func Run(m *testing.M) (code int) { + defer func() { gs.Stop() }() + return m.Run() +} + +// HasProperty 判断属性是否存在 +func HasProperty(key string) bool { + return ctx.Has(key) +} + +// GetProperty 获取属性值 +func GetProperty(key string, opts ...conf.GetOption) string { + return ctx.Prop(key, opts...) +} + +// Resolve 解析字符串 +func Resolve(s string) (string, error) { + return ctx.Resolve(s) +} + +// Bind 绑定对象 +func Bind(i interface{}, opts ...conf.BindArg) error { + return ctx.Bind(i, opts...) +} + +// Get 获取对象 +func Get(i interface{}, selectors ...util.BeanSelector) error { + return ctx.Get(i, selectors...) +} + +// Wire 注入对象 +func Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) { + return ctx.Wire(objOrCtor, ctorArgs...) +} + +// Invoke 调用函数 +func Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) { + return ctx.Invoke(fn, args...) +} From 13abbab319ced47b61a6af00c92ce68aa7bc0e58 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 16:27:49 +0800 Subject: [PATCH 08/75] remove spring-base pkg --- atomic/bool.go | 64 ++ atomic/duration.go | 60 ++ atomic/float32.go | 59 ++ atomic/float64.go | 59 ++ atomic/int32.go | 58 ++ atomic/int64.go | 59 ++ atomic/nocopy.go | 35 ++ atomic/pointer.go | 62 ++ atomic/string.go | 46 ++ atomic/time.go | 59 ++ atomic/uint32.go | 58 ++ atomic/uint64.go | 59 ++ atomic/uintptr.go | 52 ++ atomic/value.go | 41 ++ conf/bind_test.go | 2 +- conf/conf_test.go | 4 +- conf/reader/prop/prop_test.go | 2 +- conf/reader/toml/toml_test.go | 2 +- conf/reader/yaml/yaml_test.go | 2 +- conf/store/path_test.go | 2 +- conf/store/store_test.go | 2 +- dync/bool.go | 4 +- dync/duration.go | 4 +- dync/dync.go | 2 +- dync/dync_test.go | 4 +- dync/float32.go | 4 +- dync/float64.go | 4 +- dync/int32.go | 4 +- dync/int64.go | 4 +- dync/ref.go | 2 +- dync/string.go | 2 +- dync/time.go | 4 +- dync/uint32.go | 4 +- dync/uint64.go | 4 +- go.mod | 10 +- go.sum | 37 +- gs/app.go | 67 +- gs/arg/arg.go | 93 ++- gs/arg/arg_mock.go | 15 +- gs/arg/arg_test.go | 24 +- gs/boot.go | 4 +- gs/{app_test.go => boot_test.go} | 3 +- gs/cond/cond.go | 23 +- gs/cond/cond_mock.go | 27 +- gs/cond/cond_test.go | 894 +++++++++++++-------------- gs/gs.go | 93 ++- gs/gs_bean.go | 54 +- gs/gs_bean_test.go | 46 +- gs/gs_context.go | 38 +- gs/gs_dynamic_test.go | 2 +- gs/gs_test.go | 97 ++- gs/gstest/gstest.go | 5 +- gs/gsutil/type.go | 193 ++++++ gs/gsutil/value.go | 66 ++ util/error.go | 65 ++ {gs/internal => util}/triple_sort.go | 2 +- 56 files changed, 1865 insertions(+), 826 deletions(-) create mode 100644 atomic/bool.go create mode 100644 atomic/duration.go create mode 100644 atomic/float32.go create mode 100644 atomic/float64.go create mode 100644 atomic/int32.go create mode 100644 atomic/int64.go create mode 100644 atomic/nocopy.go create mode 100644 atomic/pointer.go create mode 100644 atomic/string.go create mode 100644 atomic/time.go create mode 100644 atomic/uint32.go create mode 100644 atomic/uint64.go create mode 100644 atomic/uintptr.go create mode 100644 atomic/value.go rename gs/{app_test.go => boot_test.go} (95%) create mode 100644 gs/gsutil/type.go create mode 100644 gs/gsutil/value.go create mode 100644 util/error.go rename {gs/internal => util}/triple_sort.go (99%) diff --git a/atomic/bool.go b/atomic/bool.go new file mode 100644 index 00000000..59dd6eb0 --- /dev/null +++ b/atomic/bool.go @@ -0,0 +1,64 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// A Bool is an atomic bool value. +type Bool struct { + _ nocopy + v uint32 +} + +func bool2uint(val bool) uint32 { + if val { + return 1 + } + return 0 +} + +func uint2bool(val uint32) bool { + return val != 0 +} + +// Load atomically loads and returns the value stored in x. +func (x *Bool) Load() (val bool) { + return uint2bool(atomic.LoadUint32(&x.v)) +} + +// Store atomically stores val into x. +func (x *Bool) Store(val bool) { + atomic.StoreUint32(&x.v, bool2uint(val)) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Bool) Swap(new bool) (old bool) { + return uint2bool(atomic.SwapUint32(&x.v, bool2uint(new))) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) { + return atomic.CompareAndSwapUint32(&x.v, bool2uint(old), bool2uint(new)) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Bool) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/duration.go b/atomic/duration.go new file mode 100644 index 00000000..dedef00b --- /dev/null +++ b/atomic/duration.go @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" + "time" +) + +// A Duration is an atomic time.Duration value. +type Duration struct { + _ nocopy + _ align64 + v int64 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Duration) Add(delta time.Duration) time.Duration { + return time.Duration(atomic.AddInt64(&x.v, int64(delta))) +} + +// Load atomically loads and returns the value stored in x. +func (x *Duration) Load() time.Duration { + return time.Duration(atomic.LoadInt64(&x.v)) +} + +// Store atomically stores val into x. +func (x *Duration) Store(val time.Duration) { + atomic.StoreInt64(&x.v, int64(val)) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Duration) Swap(new time.Duration) time.Duration { + return time.Duration(atomic.SwapInt64(&x.v, int64(new))) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Duration) CompareAndSwap(old, new time.Duration) bool { + return atomic.CompareAndSwapInt64(&x.v, int64(old), int64(new)) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/float32.go b/atomic/float32.go new file mode 100644 index 00000000..bc14abee --- /dev/null +++ b/atomic/float32.go @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "math" + "sync/atomic" +) + +// A Float32 is an atomic float32 value. +type Float32 struct { + _ nocopy + v uint32 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Float32) Add(delta float32) (new float32) { + return math.Float32frombits(atomic.AddUint32(&x.v, math.Float32bits(delta))) +} + +// Load atomically loads and returns the value stored in x. +func (x *Float32) Load() (val float32) { + return math.Float32frombits(atomic.LoadUint32(&x.v)) +} + +// Store atomically stores val into x. +func (x *Float32) Store(val float32) { + atomic.StoreUint32(&x.v, math.Float32bits(val)) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Float32) Swap(new float32) (old float32) { + return math.Float32frombits(atomic.SwapUint32(&x.v, math.Float32bits(new))) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Float32) CompareAndSwap(old, new float32) (swapped bool) { + return atomic.CompareAndSwapUint32(&x.v, math.Float32bits(old), math.Float32bits(new)) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Float32) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/float64.go b/atomic/float64.go new file mode 100644 index 00000000..faeb96c6 --- /dev/null +++ b/atomic/float64.go @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "math" + "sync/atomic" +) + +// A Float64 is an atomic float64 value. +type Float64 struct { + _ nocopy + v uint64 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Float64) Add(delta float64) (new float64) { + return math.Float64frombits(atomic.AddUint64(&x.v, math.Float64bits(delta))) +} + +// Load atomically loads and returns the value stored in x. +func (x *Float64) Load() (val float64) { + return math.Float64frombits(atomic.LoadUint64(&x.v)) +} + +// Store atomically stores val into x. +func (x *Float64) Store(val float64) { + atomic.StoreUint64(&x.v, math.Float64bits(val)) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Float64) Swap(new float64) (old float64) { + return math.Float64frombits(atomic.SwapUint64(&x.v, math.Float64bits(new))) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Float64) CompareAndSwap(old, new float64) (swapped bool) { + return atomic.CompareAndSwapUint64(&x.v, math.Float64bits(old), math.Float64bits(new)) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Float64) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/int32.go b/atomic/int32.go new file mode 100644 index 00000000..5ff44826 --- /dev/null +++ b/atomic/int32.go @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// An Int32 is an atomic int32 value. +type Int32 struct { + _ nocopy + v int32 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Int32) Add(delta int32) (new int32) { + return atomic.AddInt32(&x.v, delta) +} + +// Load atomically loads and returns the value stored in x. +func (x *Int32) Load() (val int32) { + return atomic.LoadInt32(&x.v) +} + +// Store atomically stores val into x. +func (x *Int32) Store(val int32) { + atomic.StoreInt32(&x.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Int32) Swap(new int32) (old int32) { + return atomic.SwapInt32(&x.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) { + return atomic.CompareAndSwapInt32(&x.v, old, new) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Int32) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/int64.go b/atomic/int64.go new file mode 100644 index 00000000..44675a4d --- /dev/null +++ b/atomic/int64.go @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// An Int64 is an atomic int64 value. +type Int64 struct { + _ nocopy + _ align64 + v int64 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Int64) Add(delta int64) int64 { + return atomic.AddInt64(&x.v, delta) +} + +// Load atomically loads and returns the value stored in x. +func (x *Int64) Load() int64 { + return atomic.LoadInt64(&x.v) +} + +// Store atomically stores val into x. +func (x *Int64) Store(val int64) { + atomic.StoreInt64(&x.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Int64) Swap(new int64) int64 { + return atomic.SwapInt64(&x.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Int64) CompareAndSwap(old, new int64) bool { + return atomic.CompareAndSwapInt64(&x.v, old, new) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Int64) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/nocopy.go b/atomic/nocopy.go new file mode 100644 index 00000000..afef5fef --- /dev/null +++ b/atomic/nocopy.go @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +// nocopy may be added to structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +// +// Note that it must not be embedded, due to the Lock and Unlock methods. +type nocopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*nocopy) Lock() {} +func (*nocopy) Unlock() {} + +// align64 may be added to structs that must be 64-bit aligned. +// This struct is recognized by a special case in the compiler +// and will not work if copied to any other package. +type align64 struct{} diff --git a/atomic/pointer.go b/atomic/pointer.go new file mode 100644 index 00000000..67cac7e4 --- /dev/null +++ b/atomic/pointer.go @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "sync/atomic" + "unsafe" +) + +type MarshalPointer func(unsafe.Pointer) ([]byte, error) + +// A Pointer is an atomic pointer value. +type Pointer struct { + _ nocopy + v unsafe.Pointer + + marshalJSON MarshalPointer +} + +// Load atomically loads and returns the value stored in x. +func (p *Pointer) Load() (val unsafe.Pointer) { + return atomic.LoadPointer(&p.v) +} + +// Store atomically stores val into x. +func (p *Pointer) Store(val unsafe.Pointer) { + atomic.StorePointer(&p.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (p *Pointer) Swap(new unsafe.Pointer) (old unsafe.Pointer) { + return atomic.SwapPointer(&p.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (p *Pointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) { + return atomic.CompareAndSwapPointer(&p.v, old, new) +} + +// SetMarshalJSON sets the JSON encoding handler for x. +func (p *Pointer) SetMarshalJSON(fn MarshalPointer) { + p.marshalJSON = fn +} + +// MarshalJSON returns the JSON encoding of x. +func (p *Pointer) MarshalJSON() ([]byte, error) { + return p.marshalJSON(p.Load()) +} diff --git a/atomic/string.go b/atomic/string.go new file mode 100644 index 00000000..88fdfad2 --- /dev/null +++ b/atomic/string.go @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// A String is an atomic string value. +type String struct { + _ nocopy + v atomic.Value +} + +// Load atomically loads and returns the value stored in x. +func (x *String) Load() string { + if r := x.v.Load(); r != nil { + return r.(string) + } + return "" +} + +// Store atomically stores val into x. +func (x *String) Store(val string) { + x.v.Store(val) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *String) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/time.go b/atomic/time.go new file mode 100644 index 00000000..71620476 --- /dev/null +++ b/atomic/time.go @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" + "time" +) + +type MarshalTime func(time.Time) ([]byte, error) + +// A Time is an atomic time.Time value. +type Time struct { + _ nocopy + v atomic.Value + + marshalJSON MarshalTime +} + +// Load atomically loads and returns the value stored in x. +func (x *Time) Load() time.Time { + if x, ok := x.v.Load().(time.Time); ok { + return x + } + return time.Time{} +} + +// Store atomically stores val into x. +func (x *Time) Store(val time.Time) { + x.v.Store(val) +} + +// SetMarshalJSON sets the JSON encoding handler for x. +func (x *Time) SetMarshalJSON(fn MarshalTime) { + x.marshalJSON = fn +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Time) MarshalJSON() ([]byte, error) { + if x.marshalJSON != nil { + return x.marshalJSON(x.Load()) + } + return json.Marshal(x.Load().Format(time.UnixDate)) +} diff --git a/atomic/uint32.go b/atomic/uint32.go new file mode 100644 index 00000000..d56fb79b --- /dev/null +++ b/atomic/uint32.go @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// An Uint32 is an atomic uint32 value. +type Uint32 struct { + _ nocopy + v uint32 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uint32) Add(delta uint32) (new uint32) { + return atomic.AddUint32(&x.v, delta) +} + +// Load atomically loads and returns the value stored in x. +func (x *Uint32) Load() (val uint32) { + return atomic.LoadUint32(&x.v) +} + +// Store atomically stores val into x. +func (x *Uint32) Store(val uint32) { + atomic.StoreUint32(&x.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Uint32) Swap(new uint32) (old uint32) { + return atomic.SwapUint32(&x.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { + return atomic.CompareAndSwapUint32(&x.v, old, new) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Uint32) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/uint64.go b/atomic/uint64.go new file mode 100644 index 00000000..2b233647 --- /dev/null +++ b/atomic/uint64.go @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +// An Uint64 is an atomic uint64 value. +type Uint64 struct { + _ nocopy + _ align64 + v uint64 +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uint64) Add(delta uint64) (new uint64) { + return atomic.AddUint64(&x.v, delta) +} + +// Load atomically loads and returns the value stored in x. +func (x *Uint64) Load() (val uint64) { + return atomic.LoadUint64(&x.v) +} + +// Store atomically stores val into x. +func (x *Uint64) Store(val uint64) { + atomic.StoreUint64(&x.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Uint64) Swap(new uint64) (old uint64) { + return atomic.SwapUint64(&x.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { + return atomic.CompareAndSwapUint64(&x.v, old, new) +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Uint64) MarshalJSON() ([]byte, error) { + return json.Marshal(x.Load()) +} diff --git a/atomic/uintptr.go b/atomic/uintptr.go new file mode 100644 index 00000000..f58b3940 --- /dev/null +++ b/atomic/uintptr.go @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "sync/atomic" +) + +// An Uintptr is an atomic uintptr value. +type Uintptr struct { + _ nocopy + v uintptr +} + +// Add atomically adds delta to x and returns the new value. +func (x *Uintptr) Add(delta uintptr) (new uintptr) { + return atomic.AddUintptr(&x.v, delta) +} + +// Load atomically loads and returns the value stored in x. +func (x *Uintptr) Load() (val uintptr) { + return atomic.LoadUintptr(&x.v) +} + +// Store atomically stores val into x. +func (x *Uintptr) Store(val uintptr) { + atomic.StoreUintptr(&x.v, val) +} + +// Swap atomically stores new into x and returns the old value. +func (x *Uintptr) Swap(new uintptr) (old uintptr) { + return atomic.SwapUintptr(&x.v, new) +} + +// CompareAndSwap executes the compare-and-swap operation for x. +func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { + return atomic.CompareAndSwapUintptr(&x.v, old, new) +} diff --git a/atomic/value.go b/atomic/value.go new file mode 100644 index 00000000..2978e400 --- /dev/null +++ b/atomic/value.go @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package atomic + +import ( + "sync/atomic" +) + +type MarshalValue func(interface{}) ([]byte, error) + +// A Value provides an atomic load and store of a consistently typed value. +type Value struct { + _ nocopy + atomic.Value + + marshalJSON MarshalValue +} + +// SetMarshalJSON sets the JSON encoding handler for x. +func (x *Value) SetMarshalJSON(fn MarshalValue) { + x.marshalJSON = fn +} + +// MarshalJSON returns the JSON encoding of x. +func (x *Value) MarshalJSON() ([]byte, error) { + return x.marshalJSON(x.Load()) +} diff --git a/conf/bind_test.go b/conf/bind_test.go index 857ea509..f0c045e1 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -20,8 +20,8 @@ import ( "fmt" "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf" + "github.com/stretchr/testify/assert" ) type DB struct { diff --git a/conf/conf_test.go b/conf/conf_test.go index f5fc8771..bc2817a9 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -24,9 +24,9 @@ import ( "testing" "time" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/cast" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" + "github.com/stretchr/testify/assert" ) func TestProperties_Load(t *testing.T) { diff --git a/conf/reader/prop/prop_test.go b/conf/reader/prop/prop_test.go index b64f5485..01c1d285 100644 --- a/conf/reader/prop/prop_test.go +++ b/conf/reader/prop/prop_test.go @@ -19,8 +19,8 @@ package prop_test import ( "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf/reader/prop" + "github.com/stretchr/testify/assert" ) func TestRead(t *testing.T) { diff --git a/conf/reader/toml/toml_test.go b/conf/reader/toml/toml_test.go index df41df67..6ca3606d 100644 --- a/conf/reader/toml/toml_test.go +++ b/conf/reader/toml/toml_test.go @@ -19,8 +19,8 @@ package toml_test import ( "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf/reader/toml" + "github.com/stretchr/testify/assert" ) func TestRead(t *testing.T) { diff --git a/conf/reader/yaml/yaml_test.go b/conf/reader/yaml/yaml_test.go index ad5464c6..9f21ed39 100644 --- a/conf/reader/yaml/yaml_test.go +++ b/conf/reader/yaml/yaml_test.go @@ -20,8 +20,8 @@ import ( "strings" "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf/reader/yaml" + "github.com/stretchr/testify/assert" ) func TestRead(t *testing.T) { diff --git a/conf/store/path_test.go b/conf/store/path_test.go index c3e83754..82b69803 100644 --- a/conf/store/path_test.go +++ b/conf/store/path_test.go @@ -21,8 +21,8 @@ import ( "fmt" "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf/store" + "github.com/stretchr/testify/assert" ) func TestSplitPath(t *testing.T) { diff --git a/conf/store/store_test.go b/conf/store/store_test.go index 795b8dc2..901c0e4d 100644 --- a/conf/store/store_test.go +++ b/conf/store/store_test.go @@ -19,8 +19,8 @@ package store_test import ( "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf/store" + "github.com/stretchr/testify/assert" ) func TestStorage(t *testing.T) { diff --git a/dync/bool.go b/dync/bool.go index 2045c64a..ad8d8304 100644 --- a/dync/bool.go +++ b/dync/bool.go @@ -18,10 +18,10 @@ package dync import ( "encoding/json" + "sync/atomic" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Bool struct { diff --git a/dync/duration.go b/dync/duration.go index 516c7501..ff5d1959 100644 --- a/dync/duration.go +++ b/dync/duration.go @@ -20,9 +20,9 @@ import ( "encoding/json" "time" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type DurationValidateFunc func(v time.Duration) error diff --git a/dync/dync.go b/dync/dync.go index 1790e585..5150de3f 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -22,7 +22,7 @@ import ( "sort" "strings" - "github.com/go-spring/spring-base/atomic" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/expr" ) diff --git a/dync/dync_test.go b/dync/dync_test.go index 0c1616a4..82dffc6a 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -23,10 +23,10 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/cast" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" + "github.com/spf13/cast" + "github.com/stretchr/testify/assert" ) type Integer struct { diff --git a/dync/float32.go b/dync/float32.go index 77ce195a..032509a2 100644 --- a/dync/float32.go +++ b/dync/float32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Float32ValidateFunc func(v float32) error diff --git a/dync/float64.go b/dync/float64.go index ec22a0f3..f18b3944 100644 --- a/dync/float64.go +++ b/dync/float64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Float64ValidateFunc func(v float64) error diff --git a/dync/int32.go b/dync/int32.go index 795488c8..3f00ba8a 100644 --- a/dync/int32.go +++ b/dync/int32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Int32ValidateFunc func(v int32) error diff --git a/dync/int64.go b/dync/int64.go index d6480031..bb99575a 100644 --- a/dync/int64.go +++ b/dync/int64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Int64ValidateFunc func(v int64) error diff --git a/dync/ref.go b/dync/ref.go index 0c11a9dd..1a786f93 100644 --- a/dync/ref.go +++ b/dync/ref.go @@ -20,7 +20,7 @@ import ( "encoding/json" "reflect" - "github.com/go-spring/spring-base/atomic" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" ) diff --git a/dync/string.go b/dync/string.go index d22c9f21..934831d6 100644 --- a/dync/string.go +++ b/dync/string.go @@ -19,7 +19,7 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" ) diff --git a/dync/time.go b/dync/time.go index db4d6603..e6c705fd 100644 --- a/dync/time.go +++ b/dync/time.go @@ -20,9 +20,9 @@ import ( "encoding/json" "time" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type TimeValidateFunc func(v time.Time) error diff --git a/dync/uint32.go b/dync/uint32.go index 6cdb87fc..4685fdfd 100644 --- a/dync/uint32.go +++ b/dync/uint32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Uint32ValidateFunc func(v uint32) error diff --git a/dync/uint64.go b/dync/uint64.go index bfc438a6..810ea81b 100644 --- a/dync/uint64.go +++ b/dync/uint64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-base/atomic" - "github.com/go-spring/spring-base/cast" + "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "github.com/spf13/cast" ) type Uint64ValidateFunc func(v uint64) error diff --git a/go.mod b/go.mod index 3c6a9467..00e7c32d 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,16 @@ go 1.22 require ( github.com/expr-lang/expr v1.16.9 - github.com/go-spring/spring-base v1.1.3 - github.com/golang/mock v1.6.0 github.com/magiconair/properties v1.8.9 github.com/pelletier/go-toml v1.9.5 github.com/spf13/cast v1.7.1 + github.com/stretchr/testify v1.10.0 + go.uber.org/mock v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 6c51b515..9549e4b9 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,9 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/go-spring/spring-base v1.1.3 h1:oyPwSend8UFIYSk8X6x4PaRu3BrbLWK7rYc+htnqLWA= -github.com/go-spring/spring-base v1.1.3/go.mod h1:tdngm+6agA34HQ5YADitIGaQ04e1pmxuR5cd6Eaobmw= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -16,34 +14,19 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gs/app.go b/gs/app.go index c9e7d5e1..99f88b89 100644 --- a/gs/app.go +++ b/gs/app.go @@ -73,44 +73,10 @@ func (app *App) Banner(banner string) { app.banner = banner } -func (app *App) Start() (Context, error) { +func (app *App) start() (Context, error) { app.Object(app) - if err := app.start(); err != nil { - return nil, err - } - return app.c, nil -} - -func (app *App) Stop() { - - // if app.b != nil { - // app.b.c.Close() - // } - - app.c.Close() -} - -func (app *App) Run() error { - _, err := app.Start() - if err != nil { - return err - } - - go func() { - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt, syscall.SIGTERM) - sig := <-ch - app.ShutDown(fmt.Sprintf("signal %v", sig)) - }() - - <-app.exitChan - return nil -} - -func (app *App) start() error { - // showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) // if showBanner { // app.printBanner(app.getBanner(e)) @@ -132,7 +98,7 @@ func (app *App) start() error { // } if err := app.c.refresh(false); err != nil { - return err + return nil, err } // 执行命令行启动器 @@ -153,6 +119,35 @@ func (app *App) start() error { } }) + return app.c, nil +} + +func (app *App) wait() { + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt, syscall.SIGTERM) + sig := <-ch + app.ShutDown(fmt.Sprintf("signal %v", sig)) + }() + <-app.exitChan +} + +func (app *App) stop() { + + // if app.b != nil { + // app.b.c.Close() + // } + + app.c.Close() +} + +func (app *App) Run() error { + _, err := app.start() + if err != nil { + return err + } + app.wait() + app.stop() return nil } diff --git a/gs/arg/arg.go b/gs/arg/arg.go index f2d26b63..896945eb 100644 --- a/gs/arg/arg.go +++ b/gs/arg/arg.go @@ -25,10 +25,9 @@ import ( "reflect" "runtime" - "github.com/go-spring/spring-base/code" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/util" ) // Context defines some methods of IoC container that Callable use. @@ -97,7 +96,6 @@ func Value(v interface{}) ValueArg { // argList stores the arguments of a function. type argList struct { - logger *log.Logger fnType reflect.Type args []Arg } @@ -126,7 +124,7 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(code.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(util.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else { fnArgs[arg.n] = arg.arg } @@ -136,7 +134,7 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(code.FileLine(), "function has no args but given %d", len(args)) + return nil, util.Errorf(util.FileLine(), "function has no args but given %d", len(args)) } } } @@ -147,25 +145,25 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if !shouldIndex { - return nil, util.Errorf(code.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(util.FileLine(), "the Args must have or have no index") } if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(code.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(util.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else if fnArgs[arg.n] != nil { - return nil, util.Errorf(code.FileLine(), "found same index %d", arg.n) + return nil, util.Errorf(util.FileLine(), "found same index %d", arg.n) } else { fnArgs[arg.n] = arg.arg } default: if shouldIndex { - return nil, util.Errorf(code.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(util.FileLine(), "the Args must have or have no index") } if i < fixedArgCount { fnArgs[i] = arg } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(code.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) + return nil, util.Errorf(util.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) } } } @@ -182,11 +180,6 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { // get returns all processed Args value. fileLine is the binding position of Callable. func (r *argList) get(ctx Context, fileLine string) ([]reflect.Value, error) { - // TODO 也许可以通过参数传递 *log.Logger 对象 - if r.logger == nil { - r.logger = log.GetLogger(util.TypeName(r)) - } - fnType := r.fnType numIn := fnType.NumIn() variadic := fnType.IsVariadic() @@ -204,7 +197,7 @@ func (r *argList) get(ctx Context, fileLine string) ([]reflect.Value, error) { // option arg may not return a value when the condition is not met. v, err := r.getArg(ctx, arg, t, fileLine) if err != nil { - return nil, util.Wrapf(err, code.FileLine(), "returns error when getting %d arg", idx) + return nil, util.Wrapf(err, util.FileLine(), "returns error when getting %d arg", idx) } if v.IsValid() { result = append(result, v) @@ -221,20 +214,20 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) tag string ) - description := fmt.Sprintf("arg:\"%v\" %s", arg, fileLine) - r.logger.Tracef("get value %s", description) - defer func() { - if err == nil { - r.logger.Tracef("get value success %s", description) - } else { - r.logger.Tracef("get value error %s %s", err.Error(), description) - } - }() + // description := fmt.Sprintf("arg:\"%v\" %s", arg, fileLine) + // r.logger.Tracef("get value %s", description) + // defer func() { + // if err == nil { + // r.logger.Tracef("get value success %s", description) + // } else { + // r.logger.Tracef("get value error %s %s", err.Error(), description) + // } + // }() switch g := arg.(type) { case *Callable: if results, err := g.Call(ctx); err != nil { - return reflect.Value{}, util.Wrapf(err, code.FileLine(), "") + return reflect.Value{}, util.Wrapf(err, util.FileLine(), "") } else if len(results) < 1 { return reflect.Value{}, errors.New("") } else { @@ -247,16 +240,16 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) return reflect.ValueOf(g.v), nil case *optionArg: return g.call(ctx) - case util.BeanDefinition: + case gsutil.BeanDefinition: tag = g.ID() case string: tag = g default: - tag = util.TypeName(g) + ":" + tag = gsutil.TypeName(g) + ":" } // binds properties value by the "value" tag. - if util.IsValueType(t) { + if gsutil.IsValueType(t) { if tag == "" { tag = "${}" } @@ -268,7 +261,7 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) } // wires dependent beans by the "autowire" tag. - if util.IsBeanReceiver(t) { + if gsutil.IsBeanReceiver(t) { v := reflect.New(t).Elem() if err = ctx.Wire(v, tag); err != nil { return reflect.Value{}, err @@ -276,20 +269,21 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) return v, nil } - return reflect.Value{}, util.Errorf(code.FileLine(), "error type %s", t.String()) + return reflect.Value{}, util.Errorf(util.FileLine(), "error type %s", t.String()) } // optionArg Option 函数的参数绑定。 type optionArg struct { - logger *log.Logger - r *Callable - c cond.Condition + r *Callable + c cond.Condition } // Provide 为 Option 方法绑定运行时参数。 func Provide(fn interface{}, args ...Arg) *Callable { r, err := Bind(fn, args, 1) - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } return r } @@ -302,7 +296,9 @@ func Option(fn interface{}, args ...Arg) *optionArg { } r, err := Bind(fn, args, 1) - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } return &optionArg{r: r} } @@ -314,24 +310,19 @@ func (arg *optionArg) On(c cond.Condition) *optionArg { func (arg *optionArg) call(ctx Context) (reflect.Value, error) { - // TODO 也许可以通过参数传递 *log.Logger 对象 - if arg.logger == nil { - arg.logger = log.GetLogger(util.TypeName(arg)) - } - var ( ok bool err error ) - arg.logger.Tracef("call option func %s", arg.r.fileLine) - defer func() { - if err == nil { - arg.logger.Tracef("call option func success %s", arg.r.fileLine) - } else { - arg.logger.Tracef("call option func error %s %s", err.Error(), arg.r.fileLine) - } - }() + // arg.logger.Tracef("call option func %s", arg.r.fileLine) + // defer func() { + // if err == nil { + // arg.logger.Tracef("call option func success %s", arg.r.fileLine) + // } else { + // arg.logger.Tracef("call option func error %s %s", err.Error(), arg.r.fileLine) + // } + // }() if arg.c != nil { ok, err = ctx.Matches(arg.c) @@ -409,7 +400,7 @@ func (r *Callable) Call(ctx Context) ([]reflect.Value, error) { } o := out[n-1] - if util.IsErrorType(o.Type()) { + if gsutil.IsErrorType(o.Type()) { if i := o.Interface(); i != nil { return out[:n-1], i.(error) } diff --git a/gs/arg/arg_mock.go b/gs/arg/arg_mock.go index 22b03391..a6df684b 100644 --- a/gs/arg/arg_mock.go +++ b/gs/arg/arg_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: arg.go +// +// Generated by this command: +// +// mockgen -build_flags="-mod=mod" -package=arg -source=arg.go -destination=arg_mock.go +// // Package arg is a generated GoMock package. package arg @@ -8,13 +13,14 @@ import ( reflect "reflect" cond "github.com/go-spring/spring-core/gs/cond" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockContext is a mock of Context interface. type MockContext struct { ctrl *gomock.Controller recorder *MockContextMockRecorder + isgomock struct{} } // MockContextMockRecorder is the mock recorder for MockContext. @@ -43,7 +49,7 @@ func (m *MockContext) Bind(v reflect.Value, tag string) error { } // Bind indicates an expected call of Bind. -func (mr *MockContextMockRecorder) Bind(v, tag interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Bind(v, tag any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bind", reflect.TypeOf((*MockContext)(nil).Bind), v, tag) } @@ -58,7 +64,7 @@ func (m *MockContext) Matches(c cond.Condition) (bool, error) { } // Matches indicates an expected call of Matches. -func (mr *MockContextMockRecorder) Matches(c interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Matches(c any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Matches", reflect.TypeOf((*MockContext)(nil).Matches), c) } @@ -72,7 +78,7 @@ func (m *MockContext) Wire(v reflect.Value, tag string) error { } // Wire indicates an expected call of Wire. -func (mr *MockContextMockRecorder) Wire(v, tag interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Wire(v, tag any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wire", reflect.TypeOf((*MockContext)(nil).Wire), v, tag) } @@ -81,6 +87,7 @@ func (mr *MockContextMockRecorder) Wire(v, tag interface{}) *gomock.Call { type MockArg struct { ctrl *gomock.Controller recorder *MockArgMockRecorder + isgomock struct{} } // MockArgMockRecorder is the mock recorder for MockArg. diff --git a/gs/arg/arg_test.go b/gs/arg/arg_test.go index d8487da0..0e5d149f 100644 --- a/gs/arg/arg_test.go +++ b/gs/arg/arg_test.go @@ -20,31 +20,11 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/gs/arg" - "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" ) -func init() { - config := ` - - - - - - - - - - - - ` - err := log.RefreshBuffer(config, ".xml") - util.Panic(err).When(err != nil) -} - func TestBind(t *testing.T) { t.Run("zero argument", func(t *testing.T) { diff --git a/gs/boot.go b/gs/boot.go index 6c51abc9..967a1818 100644 --- a/gs/boot.go +++ b/gs/boot.go @@ -26,12 +26,12 @@ var app = NewApp() // Start 启动程序。 func Start() (Context, error) { - return app.Start() + return app.start() } // Stop 停止程序。 func Stop() { - app.Stop() + app.stop() } // Run 启动程序。 diff --git a/gs/app_test.go b/gs/boot_test.go similarity index 95% rename from gs/app_test.go rename to gs/boot_test.go index bca10f9a..9a4fdeb7 100644 --- a/gs/app_test.go +++ b/gs/boot_test.go @@ -20,9 +20,8 @@ import ( "os" "testing" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-core/gs/gstest" + "github.com/stretchr/testify/assert" ) func TestMain(m *testing.M) { diff --git a/gs/cond/cond.go b/gs/cond/cond.go index e560ca23..de270181 100755 --- a/gs/cond/cond.go +++ b/gs/cond/cond.go @@ -25,9 +25,10 @@ import ( "strconv" "strings" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/expr" + "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/util" ) // Context defines some methods of IoC container that conditions use. @@ -38,7 +39,7 @@ type Context interface { // returns empty string when the IoC container doesn't have it. Prop(key string, opts ...conf.GetOption) string // Find returns bean definitions that matched with the bean selector. - Find(selector util.BeanSelector) ([]util.BeanDefinition, error) + Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) } // Condition is used when registering a bean to determine whether it's valid. @@ -125,7 +126,7 @@ func (c *onMissingProperty) Matches(ctx Context) (bool, error) { // onBean is a Condition that returns true when finding more than one beans. type onBean struct { - selector util.BeanSelector + selector gsutil.BeanSelector } func (c *onBean) Matches(ctx Context) (bool, error) { @@ -135,7 +136,7 @@ func (c *onBean) Matches(ctx Context) (bool, error) { // onMissingBean is a Condition that returns true when finding no beans. type onMissingBean struct { - selector util.BeanSelector + selector gsutil.BeanSelector } func (c *onMissingBean) Matches(ctx Context) (bool, error) { @@ -145,7 +146,7 @@ func (c *onMissingBean) Matches(ctx Context) (bool, error) { // onSingleBean is a Condition that returns true when finding only one bean. type onSingleBean struct { - selector util.BeanSelector + selector gsutil.BeanSelector } func (c *onSingleBean) Matches(ctx Context) (bool, error) { @@ -355,34 +356,34 @@ func (c *conditional) OnMissingProperty(name string) *conditional { // OnBean returns a conditional that starts with a Condition that returns true when // finding more than one beans. -func OnBean(selector util.BeanSelector) *conditional { +func OnBean(selector gsutil.BeanSelector) *conditional { return New().OnBean(selector) } // OnBean adds a Condition that returns true when finding more than one beans. -func (c *conditional) OnBean(selector util.BeanSelector) *conditional { +func (c *conditional) OnBean(selector gsutil.BeanSelector) *conditional { return c.On(&onBean{selector: selector}) } // OnMissingBean returns a conditional that starts with a Condition that returns // true when finding no beans. -func OnMissingBean(selector util.BeanSelector) *conditional { +func OnMissingBean(selector gsutil.BeanSelector) *conditional { return New().OnMissingBean(selector) } // OnMissingBean adds a Condition that returns true when finding no beans. -func (c *conditional) OnMissingBean(selector util.BeanSelector) *conditional { +func (c *conditional) OnMissingBean(selector gsutil.BeanSelector) *conditional { return c.On(&onMissingBean{selector: selector}) } // OnSingleBean returns a conditional that starts with a Condition that returns // true when finding only one bean. -func OnSingleBean(selector util.BeanSelector) *conditional { +func OnSingleBean(selector gsutil.BeanSelector) *conditional { return New().OnSingleBean(selector) } // OnSingleBean adds a Condition that returns true when finding only one bean. -func (c *conditional) OnSingleBean(selector util.BeanSelector) *conditional { +func (c *conditional) OnSingleBean(selector gsutil.BeanSelector) *conditional { return c.On(&onSingleBean{selector: selector}) } diff --git a/gs/cond/cond_mock.go b/gs/cond/cond_mock.go index 36dff6f1..b33ed118 100644 --- a/gs/cond/cond_mock.go +++ b/gs/cond/cond_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: cond.go +// +// Generated by this command: +// +// mockgen -build_flags="-mod=mod" -package=cond -source=cond.go -destination=cond_mock.go +// // Package cond is a generated GoMock package. package cond @@ -7,15 +12,16 @@ package cond import ( reflect "reflect" - util "github.com/go-spring/spring-base/util" conf "github.com/go-spring/spring-core/conf" - gomock "github.com/golang/mock/gomock" + gsutil "github.com/go-spring/spring-core/gs/gsutil" + gomock "go.uber.org/mock/gomock" ) // MockContext is a mock of Context interface. type MockContext struct { ctrl *gomock.Controller recorder *MockContextMockRecorder + isgomock struct{} } // MockContextMockRecorder is the mock recorder for MockContext. @@ -36,16 +42,16 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector util.BeanSelector) ([]util.BeanDefinition, error) { +func (m *MockContext) Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]util.BeanDefinition) + ret0, _ := ret[0].([]gsutil.BeanDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } // Find indicates an expected call of Find. -func (mr *MockContextMockRecorder) Find(selector interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Find(selector any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockContext)(nil).Find), selector) } @@ -59,7 +65,7 @@ func (m *MockContext) Has(key string) bool { } // Has indicates an expected call of Has. -func (mr *MockContextMockRecorder) Has(key interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Has(key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Has", reflect.TypeOf((*MockContext)(nil).Has), key) } @@ -67,7 +73,7 @@ func (mr *MockContextMockRecorder) Has(key interface{}) *gomock.Call { // Prop mocks base method. func (m *MockContext) Prop(key string, opts ...conf.GetOption) string { m.ctrl.T.Helper() - varargs := []interface{}{key} + varargs := []any{key} for _, a := range opts { varargs = append(varargs, a) } @@ -77,9 +83,9 @@ func (m *MockContext) Prop(key string, opts ...conf.GetOption) string { } // Prop indicates an expected call of Prop. -func (mr *MockContextMockRecorder) Prop(key interface{}, opts ...interface{}) *gomock.Call { +func (mr *MockContextMockRecorder) Prop(key any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{key}, opts...) + varargs := append([]any{key}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prop", reflect.TypeOf((*MockContext)(nil).Prop), varargs...) } @@ -87,6 +93,7 @@ func (mr *MockContextMockRecorder) Prop(key interface{}, opts ...interface{}) *g type MockCondition struct { ctrl *gomock.Controller recorder *MockConditionMockRecorder + isgomock struct{} } // MockConditionMockRecorder is the mock recorder for MockCondition. @@ -116,7 +123,7 @@ func (m *MockCondition) Matches(ctx Context) (bool, error) { } // Matches indicates an expected call of Matches. -func (mr *MockConditionMockRecorder) Matches(ctx interface{}) *gomock.Call { +func (mr *MockConditionMockRecorder) Matches(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Matches", reflect.TypeOf((*MockCondition)(nil).Matches), ctx) } diff --git a/gs/cond/cond_test.go b/gs/cond/cond_test.go index c1742d5a..513b1e67 100644 --- a/gs/cond/cond_test.go +++ b/gs/cond/cond_test.go @@ -16,455 +16,445 @@ package cond_test -import ( - "errors" - "testing" - - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/gs/cond" - "github.com/golang/mock/gomock" -) - -func TestOK(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.OK().Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) -} - -func TestNot(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.Not(cond.OK()).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) -} - -func TestOnProperty(t *testing.T) { - t.Run("no property & no HavingValue & no MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(false) - ok, err := cond.OnProperty("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("has property & no HavingValue & no MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ok, err := cond.OnProperty("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("no property & has HavingValue & no MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(false) - ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("diff property & has HavingValue & no MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ctx.EXPECT().Prop("a").Return("b") - ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("same property & has HavingValue & no MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ctx.EXPECT().Prop("a").Return("a") - ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("no property & no HavingValue & has MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(false) - ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("has property & no HavingValue & has MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("no property & has HavingValue & has MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(false) - ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("diff property & has HavingValue & has MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ctx.EXPECT().Prop("a").Return("b") - ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("same property & has HavingValue & has MatchIfMissing", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ctx.EXPECT().Prop("a").Return("a") - ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("go expression", func(t *testing.T) { - testcases := []struct { - propValue string - expression string - expectResult bool - }{ - { - "a", - "go:$==\"a\"", - true, - }, - { - "a", - "go:$==\"b\"", - false, - }, - { - "3", - "go:$==3", - true, - }, - { - "3", - "go:$==4", - false, - }, - { - "3", - "go:$>1&&$<5", - true, - }, - { - "false", - "go:$", - false, - }, - { - "false", - "go:!$", - true, - }, - } - for _, testcase := range testcases { - ctrl := gomock.NewController(t) - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ctx.EXPECT().Prop("a").Return(testcase.propValue) - ok, err := cond.OnProperty("a", cond.HavingValue(testcase.expression)).Matches(ctx) - assert.Nil(t, err) - assert.Equal(t, ok, testcase.expectResult) - ctrl.Finish() - } - }) -} - -func TestOnMissingProperty(t *testing.T) { - t.Run("no property", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(false) - ok, err := cond.OnMissingProperty("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("has property", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("a").Return(true) - ok, err := cond.OnMissingProperty("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) -} - -func TestOnBean(t *testing.T) { - t.Run("return error", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, errors.New("error")) - ok, err := cond.OnBean("a").Matches(ctx) - assert.Error(t, err, "error") - assert.False(t, ok) - }) - t.Run("no bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, nil) - ok, err := cond.OnBean("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("one bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnBean("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("more than one beans", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnBean("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) -} - -func TestOnMissingBean(t *testing.T) { - t.Run("return error", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, errors.New("error")) - ok, err := cond.OnMissingBean("a").Matches(ctx) - assert.Error(t, err, "error") - assert.False(t, ok) - }) - t.Run("no bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, nil) - ok, err := cond.OnMissingBean("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("one bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnMissingBean("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("more than one beans", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnMissingBean("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) -} - -func TestOnSingleBean(t *testing.T) { - t.Run("return error", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, errors.New("error")) - ok, err := cond.OnSingleBean("a").Matches(ctx) - assert.Error(t, err, "error") - assert.False(t, ok) - }) - t.Run("no bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return(nil, nil) - ok, err := cond.OnSingleBean("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("one bean", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnSingleBean("a").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("more than one beans", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]util.BeanDefinition{ - util.NewMockBeanDefinition(nil), - util.NewMockBeanDefinition(nil), - }, nil) - ok, err := cond.OnSingleBean("a").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) -} - -func TestOnExpression(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.OnExpression("").Matches(ctx) - assert.Error(t, err, "unimplemented method") - assert.False(t, ok) -} - -func TestOnMatches(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.OnMatches(func(ctx cond.Context) (bool, error) { - return false, nil - }).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) -} - -func TestOnProfile(t *testing.T) { - t.Run("no property", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("spring.profiles.active").Return(false) - ok, err := cond.OnProfile("test").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("diff property", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("spring.profiles.active").Return(true) - ctx.EXPECT().Prop("spring.profiles.active").Return("dev") - ok, err := cond.OnProfile("test").Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("same property", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ctx.EXPECT().Has("spring.profiles.active").Return(true) - ctx.EXPECT().Prop("spring.profiles.active").Return("test") - ok, err := cond.OnProfile("test").Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) -} - -func TestConditional(t *testing.T) { - t.Run("ok && ", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.On(cond.OK()).And().Matches(ctx) - assert.Error(t, err, "no condition in last node") - assert.False(t, ok) - }) - t.Run("ok && !ok", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.On(cond.OK()).And().On(cond.Not(cond.OK())).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("ok || ", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.On(cond.OK()).Or().Matches(ctx) - assert.Error(t, err, "no condition in last node") - assert.False(t, ok) - }) - t.Run("ok || !ok", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.On(cond.OK()).Or().On(cond.Not(cond.OK())).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) -} - -func TestGroup(t *testing.T) { - t.Run("ok && ", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.Group(cond.And, cond.OK()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("ok && !ok", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.Group(cond.And, cond.OK(), cond.Not(cond.OK())).Matches(ctx) - assert.Nil(t, err) - assert.False(t, ok) - }) - t.Run("ok || ", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.Group(cond.Or, cond.OK()).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) - t.Run("ok || !ok", func(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - ctx := cond.NewMockContext(ctrl) - ok, err := cond.Group(cond.Or, cond.OK(), cond.Not(cond.OK())).Matches(ctx) - assert.Nil(t, err) - assert.True(t, ok) - }) -} +// func TestOK(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.OK().Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// } +// +// func TestNot(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.Not(cond.OK()).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// } +// +// func TestOnProperty(t *testing.T) { +// t.Run("no property & no HavingValue & no MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(false) +// ok, err := cond.OnProperty("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("has property & no HavingValue & no MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ok, err := cond.OnProperty("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("no property & has HavingValue & no MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(false) +// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("diff property & has HavingValue & no MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ctx.EXPECT().Prop("a").Return("b") +// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("same property & has HavingValue & no MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ctx.EXPECT().Prop("a").Return("a") +// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("no property & no HavingValue & has MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(false) +// ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("has property & no HavingValue & has MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("no property & has HavingValue & has MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(false) +// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("diff property & has HavingValue & has MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ctx.EXPECT().Prop("a").Return("b") +// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("same property & has HavingValue & has MatchIfMissing", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ctx.EXPECT().Prop("a").Return("a") +// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("go expression", func(t *testing.T) { +// testcases := []struct { +// propValue string +// expression string +// expectResult bool +// }{ +// { +// "a", +// "go:$==\"a\"", +// true, +// }, +// { +// "a", +// "go:$==\"b\"", +// false, +// }, +// { +// "3", +// "go:$==3", +// true, +// }, +// { +// "3", +// "go:$==4", +// false, +// }, +// { +// "3", +// "go:$>1&&$<5", +// true, +// }, +// { +// "false", +// "go:$", +// false, +// }, +// { +// "false", +// "go:!$", +// true, +// }, +// } +// for _, testcase := range testcases { +// ctrl := gomock.NewController(t) +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ctx.EXPECT().Prop("a").Return(testcase.propValue) +// ok, err := cond.OnProperty("a", cond.HavingValue(testcase.expression)).Matches(ctx) +// assert.Nil(t, err) +// assert.Equal(t, ok, testcase.expectResult) +// ctrl.Finish() +// } +// }) +// } +// +// func TestOnMissingProperty(t *testing.T) { +// t.Run("no property", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(false) +// ok, err := cond.OnMissingProperty("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("has property", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("a").Return(true) +// ok, err := cond.OnMissingProperty("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// } +// +// func TestOnBean(t *testing.T) { +// t.Run("return error", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) +// ok, err := cond.OnBean("a").Matches(ctx) +// assert.Error(t, err, "error") +// assert.False(t, ok) +// }) +// t.Run("no bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, nil) +// ok, err := cond.OnBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("one bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("more than one beans", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// } +// +// func TestOnMissingBean(t *testing.T) { +// t.Run("return error", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) +// ok, err := cond.OnMissingBean("a").Matches(ctx) +// assert.Error(t, err, "error") +// assert.False(t, ok) +// }) +// t.Run("no bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, nil) +// ok, err := cond.OnMissingBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("one bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnMissingBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("more than one beans", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnMissingBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// } +// +// func TestOnSingleBean(t *testing.T) { +// t.Run("return error", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) +// ok, err := cond.OnSingleBean("a").Matches(ctx) +// assert.Error(t, err, "error") +// assert.False(t, ok) +// }) +// t.Run("no bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return(nil, nil) +// ok, err := cond.OnSingleBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("one bean", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnSingleBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("more than one beans", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ +// gsutil.NewMockBeanDefinition(nil), +// gsutil.NewMockBeanDefinition(nil), +// }, nil) +// ok, err := cond.OnSingleBean("a").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// } +// +// func TestOnExpression(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.OnExpression("").Matches(ctx) +// assert.Error(t, err, "unimplemented method") +// assert.False(t, ok) +// } +// +// func TestOnMatches(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.OnMatches(func(ctx cond.Context) (bool, error) { +// return false, nil +// }).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// } +// +// func TestOnProfile(t *testing.T) { +// t.Run("no property", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("spring.profiles.active").Return(false) +// ok, err := cond.OnProfile("test").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("diff property", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("spring.profiles.active").Return(true) +// ctx.EXPECT().Prop("spring.profiles.active").Return("dev") +// ok, err := cond.OnProfile("test").Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("same property", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ctx.EXPECT().Has("spring.profiles.active").Return(true) +// ctx.EXPECT().Prop("spring.profiles.active").Return("test") +// ok, err := cond.OnProfile("test").Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// } +// +// func TestConditional(t *testing.T) { +// t.Run("ok && ", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.On(cond.OK()).And().Matches(ctx) +// assert.Error(t, err, "no condition in last node") +// assert.False(t, ok) +// }) +// t.Run("ok && !ok", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.On(cond.OK()).And().On(cond.Not(cond.OK())).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("ok || ", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.On(cond.OK()).Or().Matches(ctx) +// assert.Error(t, err, "no condition in last node") +// assert.False(t, ok) +// }) +// t.Run("ok || !ok", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.On(cond.OK()).Or().On(cond.Not(cond.OK())).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// } +// +// func TestGroup(t *testing.T) { +// t.Run("ok && ", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.Group(cond.And, cond.OK()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("ok && !ok", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.Group(cond.And, cond.OK(), cond.Not(cond.OK())).Matches(ctx) +// assert.Nil(t, err) +// assert.False(t, ok) +// }) +// t.Run("ok || ", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.Group(cond.Or, cond.OK()).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// t.Run("ok || !ok", func(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// ctx := cond.NewMockContext(ctrl) +// ok, err := cond.Group(cond.Or, cond.OK(), cond.Not(cond.OK())).Matches(ctx) +// assert.Nil(t, err) +// assert.True(t, ok) +// }) +// } diff --git a/gs/gs.go b/gs/gs.go index 010f06f9..9e3aade5 100755 --- a/gs/gs.go +++ b/gs/gs.go @@ -28,15 +28,13 @@ import ( "sort" "strings" "sync" - "time" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/arg" "github.com/go-spring/spring-core/gs/cond" - "github.com/go-spring/spring-core/gs/internal" + "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/util" ) type refreshState int @@ -49,7 +47,6 @@ const ( ) var ( - loggerType = reflect.TypeOf((*log.Logger)(nil)) contextType = reflect.TypeOf((*Context)(nil)).Elem() ) @@ -78,7 +75,7 @@ type Context interface { Prop(key string, opts ...conf.GetOption) string Resolve(s string) (string, error) Bind(i interface{}, opts ...conf.BindArg) error - Get(i interface{}, selectors ...util.BeanSelector) error + Get(i interface{}, selectors ...gsutil.BeanSelector) error Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) @@ -106,7 +103,6 @@ type tempContainer struct { // 性绑定,要么同时使用依赖注入和属性绑定。 type container struct { *tempContainer - logger *log.Logger ctx context.Context cancel context.CancelFunc destroyers []func() @@ -147,7 +143,7 @@ func validOnProperty(fn interface{}) error { if t.Kind() != reflect.Func { return errors.New("fn should be a func(value_type)") } - if t.NumIn() != 1 || !util.IsValueType(t.In(0)) || t.NumOut() != 0 { + if t.NumIn() != 1 || !gsutil.IsValueType(t.In(0)) || t.NumOut() != 0 { return errors.New("fn should be a func(value_type)") } return nil @@ -156,7 +152,9 @@ func validOnProperty(fn interface{}) error { // OnProperty 当 key 对应的属性值准备好后发送一个通知。 func (c *container) OnProperty(key string, fn interface{}) { err := validOnProperty(fn) - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } c.mapOfOnProperty[key] = fn } @@ -231,16 +229,14 @@ type lazyField struct { // wiringStack 记录 bean 的注入路径。 type wiringStack struct { - logger *log.Logger destroyers *list.List destroyerMap map[string]*destroyer beans []*BeanDefinition lazyFields []lazyField } -func newWiringStack(logger *log.Logger) *wiringStack { +func newWiringStack() *wiringStack { return &wiringStack{ - logger: logger, destroyers: list.New(), destroyerMap: make(map[string]*destroyer), } @@ -248,16 +244,16 @@ func newWiringStack(logger *log.Logger) *wiringStack { // pushBack 添加一个即将注入的 bean 。 func (s *wiringStack) pushBack(b *BeanDefinition) { - s.logger.Tracef("push %s %s", b, getStatusString(b.status)) + // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) s.beans = append(s.beans, b) } // popBack 删除一个已经注入的 bean 。 func (s *wiringStack) popBack() { n := len(s.beans) - b := s.beans[n-1] + // b := s.beans[n-1] s.beans = s.beans[:n-1] - s.logger.Tracef("pop %s %s", b, getStatusString(b.status)) + // s.logger.Tracef("pop %s %s", b, getStatusString(b.status)) } // path 返回 bean 的注入路径。 @@ -289,7 +285,7 @@ func (s *wiringStack) sortDestroyers() []func() { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) if len(out) > 0 && !out[0].IsNil() { - s.logger.Error(out[0].Interface().(error)) + // s.logger.Error(out[0].Interface().(error)) } } } @@ -299,7 +295,7 @@ func (s *wiringStack) sortDestroyers() []func() { for _, d := range s.destroyerMap { destroyers.PushBack(d) } - destroyers = internal.TripleSort(destroyers, getBeforeDestroyers) + destroyers = util.TripleSort(destroyers, getBeforeDestroyers) var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { @@ -327,9 +323,8 @@ func (c *container) refresh(autoClear bool) (err error) { c.p.Refresh(c.initProperties) - start := time.Now() + // start := time.Now() c.Object(c).Export((*Context)(nil)) - c.logger = log.GetLogger(util.TypeName(c)) for key, f := range c.mapOfOnProperty { t := reflect.TypeOf(f) @@ -369,14 +364,14 @@ func (c *container) refresh(autoClear bool) (err error) { } } - stack := newWiringStack(c.logger) + stack := newWiringStack() - defer func() { - if err != nil || len(stack.beans) > 0 { - err = fmt.Errorf("%s ↩\n%s", err, stack.path()) - c.logger.Error(err) - } - }() + // defer func() { + // if err != nil || len(stack.beans) > 0 { + // err = fmt.Errorf("%s ↩\n%s", err, stack.path()) + // c.logger.Error(err) + // } + // }() // 按照 bean id 升序注入,保证注入过程始终一致。 { @@ -408,23 +403,23 @@ func (c *container) refresh(autoClear bool) (err error) { c.destroyers = stack.sortDestroyers() c.state = Refreshed - cost := time.Now().Sub(start) - c.logger.Infof("refresh %d beans cost %v", len(beansById), cost) + // cost := time.Now().Sub(start) + // c.logger.Infof("refresh %d beans cost %v", len(beansById), cost) if autoClear && !c.ContextAware { c.clear() } - c.logger.Info("container refreshed successfully") + // c.logger.Info("container refreshed successfully") return nil } func (c *container) registerBean(b *BeanDefinition) { - c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) + // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.name] = append(c.beansByName[b.name], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) for _, t := range b.exports { - c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), t, b.FileLine()) + // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), t, b.FileLine()) c.beansByType[t] = append(c.beansByType[t], b) } } @@ -519,7 +514,7 @@ func (tag wireTag) String() string { return b.String() } -func toWireTag(selector util.BeanSelector) wireTag { +func toWireTag(selector gsutil.BeanSelector) wireTag { switch s := selector.(type) { case string: return parseWireTag(s) @@ -528,7 +523,7 @@ func toWireTag(selector util.BeanSelector) wireTag { case *BeanDefinition: return parseWireTag(s.ID()) default: - return parseWireTag(util.TypeName(s) + ":") + return parseWireTag(gsutil.TypeName(s) + ":") } } @@ -545,7 +540,7 @@ func toWireString(tags []wireTag) string { // findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) findBean(selector util.BeanSelector) ([]*BeanDefinition, error) { +func (c *container) findBean(selector gsutil.BeanSelector) ([]*BeanDefinition, error) { finder := func(fn func(*BeanDefinition) bool) ([]*BeanDefinition, error) { var result []*BeanDefinition @@ -726,9 +721,9 @@ func (c *container) getBeanValue(b *BeanDefinition, stack *wiringStack) (reflect } // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 - if val := out[0]; util.IsBeanType(val.Type()) { + if val := out[0]; gsutil.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 - if !val.IsNil() && val.Kind() == reflect.Interface && util.IsValueType(val.Elem().Type()) { + if !val.IsNil() && val.Kind() == reflect.Interface && gsutil.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) v.Elem().Set(val.Elem()) b.Value().Set(v) @@ -781,7 +776,7 @@ func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar fv := v.Field(i) if !fv.CanInterface() { - fv = util.PatchValue(fv) + fv = gsutil.PatchValue(fv) if !fv.CanInterface() { continue } @@ -789,18 +784,8 @@ func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar fieldPath := opt.Path + "." + ft.Name - tag, ok := ft.Tag.Lookup("logger") - if ok { - if ft.Type != loggerType { - return fmt.Errorf("field expects type *log.Logger") - } - l := log.GetLogger(util.TypeName(v)) - fv.Set(reflect.ValueOf(l)) - continue - } - // 支持 autowire 和 inject 两个标签。 - tag, ok = ft.Tag.Lookup("autowire") + tag, ok := ft.Tag.Lookup("autowire") if !ok { tag, ok = ft.Tag.Lookup("inject") } @@ -899,7 +884,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } t := v.Type() - if !util.IsBeanReceiver(t) { + if !gsutil.IsBeanReceiver(t) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -936,7 +921,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } if !found { foundBeans = append(foundBeans, b) - c.logger.Warnf("you should call Export() on %s", b) + // c.logger.Warnf("you should call Export() on %s", b) } } } @@ -1037,7 +1022,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } et := t.Elem() - if !util.IsBeanReceiver(et) { + if !gsutil.IsBeanReceiver(et) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -1152,13 +1137,13 @@ func (c *container) Close() { c.cancel() c.wg.Wait() - c.logger.Info("goroutines exited") + // c.logger.Info("goroutines exited") for _, f := range c.destroyers { f() } - c.logger.Info("container closed") + // c.logger.Info("container closed") } // Go 创建安全可等待的 goroutine,fn 要求的 ctx 对象由 IoC 容器提供,当 IoC 容 @@ -1169,7 +1154,7 @@ func (c *container) Go(fn func(ctx context.Context)) { defer c.wg.Done() defer func() { if r := recover(); r != nil { - c.logger.Panic(r) + // c.logger.Panic(r) } }() fn(c.ctx) diff --git a/gs/gs_bean.go b/gs/gs_bean.go index 5134f389..cded614c 100755 --- a/gs/gs_bean.go +++ b/gs/gs_bean.go @@ -23,9 +23,9 @@ import ( "runtime" "strings" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/gs/arg" "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gsutil" ) type beanStatus int8 @@ -62,7 +62,7 @@ func getStatusString(status beanStatus) string { } func BeanID(typ interface{}, name string) string { - return util.TypeName(typ) + ":" + name + return gsutil.TypeName(typ) + ":" + name } type BeanInit interface { @@ -86,16 +86,16 @@ type BeanDefinition struct { file string // 注册点所在文件 line int // 注册点所在行数 - name string // 名称 - status beanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond cond.Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []util.BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 + name string // 名称 + status beanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond cond.Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []gsutil.BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 } // Type 返回 bean 的类型。 @@ -190,7 +190,7 @@ func (d *BeanDefinition) Order(order float32) *BeanDefinition { } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...util.BeanSelector) *BeanDefinition { +func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { d.depends = append(d.depends, selectors...) return d } @@ -204,13 +204,13 @@ func (d *BeanDefinition) Primary() *BeanDefinition { // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !util.IsFuncType(fnType) { + if !gsutil.IsFuncType(fnType) { return false } - if fnType.NumIn() != 1 || !util.HasReceiver(fnType, beanValue) { + if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { return false } - return util.ReturnNothing(fnType) || util.ReturnOnlyError(fnType) + return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) } // Init 设置 bean 的初始化函数。 @@ -234,7 +234,9 @@ func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { // Export 设置 bean 的导出接口。 func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { err := d.export(exports...) - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } return d } @@ -244,7 +246,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { if t, ok := o.(reflect.Type); ok { typ = t } else { // 处理 (*error)(nil) 这种导出形式 - typ = util.Indirect(reflect.TypeOf(o)) + typ = gsutil.Indirect(reflect.TypeOf(o)) } if typ.Kind() != reflect.Interface { return errors.New("only interface type can be exported") @@ -281,7 +283,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { } t := v.Type() - if !util.IsBeanType(t) { + if !gsutil.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -296,7 +298,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 if !fromValue && t.Kind() == reflect.Func { - if !util.IsConstructor(t) { + if !gsutil.IsConstructor(t) { t1 := "func(...)bean" t2 := "func(...)(bean, error)" panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) @@ -304,16 +306,18 @@ func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { var err error f, err = arg.Bind(objOrCtor, ctorArgs, skip) - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } out0 := t.Out(0) v = reflect.New(out0) - if util.IsBeanType(out0) { + if gsutil.IsBeanType(out0) { v = v.Elem() } t = v.Type() - if !util.IsBeanType(t) { + if !gsutil.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -329,7 +333,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 } - if t.Kind() == reflect.Ptr && !util.IsValueType(t.Elem()) { + if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { panic(errors.New("bean should be *val but not *ref")) } @@ -345,7 +349,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { v: v, f: f, name: name, - typeName: util.TypeName(t), + typeName: gsutil.TypeName(t), status: Default, method: method, file: file, diff --git a/gs/gs_bean_test.go b/gs/gs_bean_test.go index c235ecd9..9090e250 100644 --- a/gs/gs_bean_test.go +++ b/gs/gs_bean_test.go @@ -23,12 +23,12 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" + "github.com/stretchr/testify/assert" ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 @@ -52,7 +52,7 @@ func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { // // for k, v := range data { // tag := parseSingletonTag(k) -// util.Equal(t, tag, v) +// Equal(t, tag, v) // } // } // @@ -64,7 +64,7 @@ func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { // // for k, v := range data { // tag := ParseCollectionTag(k) -// util.Equal(t, tag, v) +// Equal(t, tag, v) // } // } @@ -100,7 +100,7 @@ func TestIsFuncBeanType(t *testing.T) { } for k, v := range data { - ok := util.IsConstructor(k) + ok := gsutil.IsConstructor(k) assert.Equal(t, ok, v) } } @@ -159,21 +159,21 @@ func (t *BeanThree) String() string { func TestObjectBean(t *testing.T) { - t.Run("bean must be ref type", func(t *testing.T) { - - data := []func(){ - func() { newBean([...]int{0}) }, - func() { newBean(false) }, - func() { newBean(3) }, - func() { newBean("3") }, - func() { newBean(BeanZero{}) }, - func() { newBean(pkg2.SamePkg{}) }, - } - - for _, fn := range data { - assert.Panic(t, fn, "bean must be ref type") - } - }) + // t.Run("bean must be ref type", func(t *testing.T) { + // + // data := []func(){ + // func() { newBean([...]int{0}) }, + // func() { newBean(false) }, + // func() { newBean(3) }, + // func() { newBean("3") }, + // func() { newBean(BeanZero{}) }, + // func() { newBean(pkg2.SamePkg{}) }, + // } + // + // for _, fn := range data { + // assert.Panic(t, fn, "bean must be ref type") + // } + // }) t.Run("valid bean", func(t *testing.T) { newBean(make(chan int)) @@ -238,9 +238,9 @@ func TestConstructorBean(t *testing.T) { bd = newBean(interfaceFn) assert.Equal(t, bd.Type().String(), "gs_test.Teacher") - assert.Panic(t, func() { - _ = newBean(func() (*int, *int) { return nil, nil }) - }, "constructor should be func\\(...\\)bean or func\\(...\\)\\(bean, error\\)") + // assert.Panic(t, func() { + // _ = newBean(func() (*int, *int) { return nil, nil }) + // }, "constructor should be func\\(...\\)bean or func\\(...\\)\\(bean, error\\)") } type Runner interface { diff --git a/gs/gs_context.go b/gs/gs_context.go index a68ba088..993c602d 100644 --- a/gs/gs_context.go +++ b/gs/gs_context.go @@ -20,9 +20,9 @@ import ( "errors" "reflect" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsutil" ) func (c *container) Keys() []string { @@ -47,12 +47,12 @@ func (c *container) Bind(i interface{}, opts ...conf.BindArg) error { // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的,即未被 // 标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) Find(selector util.BeanSelector) ([]util.BeanDefinition, error) { +func (c *container) Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) { beans, err := c.findBean(selector) if err != nil { return nil, err } - var ret []util.BeanDefinition + var ret []gsutil.BeanDefinition for _, b := range beans { ret = append(ret, b) } @@ -68,7 +68,7 @@ func (c *container) Find(selector util.BeanSelector) ([]util.BeanDefinition, err // 工作模式称为自动模式,否则根据传入的选择器列表进行排序,这种工作模式成为指派模式。 // 该方法和 Find 方法的区别是该方法保证返回的所有 bean 对象都已经完成属性绑定和依 // 赖注入,而 Find 方法只能保证返回的 bean 对象是有效的,即未被标记为删除的。 -func (c *container) Get(i interface{}, selectors ...util.BeanSelector) error { +func (c *container) Get(i interface{}, selectors ...gsutil.BeanSelector) error { if i == nil { return errors.New("i can't be nil") @@ -79,11 +79,11 @@ func (c *container) Get(i interface{}, selectors ...util.BeanSelector) error { return errors.New("i must be pointer") } - stack := newWiringStack(c.logger) + stack := newWiringStack() defer func() { if len(stack.beans) > 0 { - c.logger.Infof("wiring path %s", stack.path()) + // c.logger.Infof("wiring path %s", stack.path()) } }() @@ -99,13 +99,13 @@ func (c *container) Get(i interface{}, selectors ...util.BeanSelector) error { // 种方式,该函数执行完后都会返回 bean 对象的真实值。 func (c *container) Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) { - stack := newWiringStack(c.logger) + stack := newWiringStack() - defer func() { - if len(stack.beans) > 0 { - c.logger.Infof("wiring path %s", stack.path()) - } - }() + // defer func() { + // if len(stack.beans) > 0 { + // c.logger.Infof("wiring path %s", stack.path()) + // } + // }() b := NewBean(objOrCtor, ctorArgs...) err := c.wireBean(b, stack) @@ -117,17 +117,17 @@ func (c *container) Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{ func (c *container) Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) { - if !util.IsFuncType(reflect.TypeOf(fn)) { + if !gsutil.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") } - stack := newWiringStack(c.logger) + stack := newWiringStack() - defer func() { - if len(stack.beans) > 0 { - c.logger.Infof("wiring path %s", stack.path()) - } - }() + // defer func() { + // if len(stack.beans) > 0 { + // c.logger.Infof("wiring path %s", stack.path()) + // } + // }() r, err := arg.Bind(fn, args, 1) if err != nil { diff --git a/gs/gs_dynamic_test.go b/gs/gs_dynamic_test.go index c67f3855..8cc545f5 100644 --- a/gs/gs_dynamic_test.go +++ b/gs/gs_dynamic_test.go @@ -22,10 +22,10 @@ import ( "fmt" "testing" - "github.com/go-spring/spring-base/assert" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs" + "github.com/stretchr/testify/assert" ) type DynamicConfig struct { diff --git a/gs/gs_test.go b/gs/gs_test.go index 068a0b9f..5625298c 100755 --- a/gs/gs_test.go +++ b/gs/gs_test.go @@ -27,24 +27,19 @@ import ( "testing" "time" - "github.com/go-spring/spring-base/assert" - "github.com/go-spring/spring-base/cast" - "github.com/go-spring/spring-base/code" - "github.com/go-spring/spring-base/log" - "github.com/go-spring/spring-base/util" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/arg" "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" + "github.com/go-spring/spring-core/util" + util2 "github.com/go-spring/spring-core/util" + "github.com/spf13/cast" + "github.com/stretchr/testify/assert" ) -func init() { - err := log.Refresh("testdata/config/logger.xml") - util.Panic(err).When(err != nil) -} - func runTest(c gs.Container, fn func(gs.Context)) error { type PandoraAware struct{} c.Provide(func(p gs.Context) PandoraAware { @@ -55,13 +50,13 @@ func runTest(c gs.Container, fn func(gs.Context)) error { } func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { - assert.Panic(t, func() { - c := gs.New() - c.Object(func() {}).Init(func(f func()) { - c.Object(func() {}) // 不能在这里注册新的 Object - }) - _ = c.Refresh() - }, "should call before Refresh") + // assert.Panic(t, func() { + // c := gs.New() + // c.Object(func() {}).Init(func(f func()) { + // c.Object(func() {}) // 不能在这里注册新的 Object + // }) + // _ = c.Refresh() + // }, "should call before Refresh") } func TestApplicationContext(t *testing.T) { @@ -73,9 +68,9 @@ func TestApplicationContext(t *testing.T) { c := gs.New() e := pkg1.SamePkg{} - assert.Panic(t, func() { - c.Object(e) - }, "bean must be ref type") + // assert.Panic(t, func() { + // c.Object(e) + // }, "bean must be ref type") c.Object(&e) c.Object(&e).Name("i3") @@ -89,9 +84,9 @@ func TestApplicationContext(t *testing.T) { c := gs.New() e := pkg2.SamePkg{} - assert.Panic(t, func() { - c.Object(e) - }, "bean must be ref type") + // assert.Panic(t, func() { + // c.Object(e) + // }, "bean must be ref type") c.Object(&e) c.Object(&e).Name("i3") @@ -715,7 +710,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { t.Run("dependsOn", func(t *testing.T) { - dependsOn := []util.BeanSelector{ + dependsOn := []gsutil.BeanSelector{ (*BeanOne)(nil), // 通过类型定义查找 "github.com/go-spring/spring-core/gs/gs_test.BeanZero:BeanZero", } @@ -806,7 +801,7 @@ func NewManager() Manager { } func NewManagerRetError() (Manager, error) { - return localManager{}, util.Error(code.FileLine(), "error") + return localManager{}, util.Error(util2.FileLine(), "error") } func NewManagerRetErrorNil() (Manager, error) { @@ -921,7 +916,7 @@ func (d *callDestroy) InitWithError() error { d.inited = true return nil } - return util.Error(code.FileLine(), "error") + return util.Error(util2.FileLine(), "error") } func (d *callDestroy) DestroyWithError() error { @@ -929,7 +924,7 @@ func (d *callDestroy) DestroyWithError() error { d.destroyed = true return nil } - return util.Error(code.FileLine(), "error") + return util.Error(util2.FileLine(), "error") } type nestedCallDestroy struct { @@ -1473,7 +1468,9 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) err := c.Refresh() - util.Panic(err).When(err != nil) + if err != nil { + panic(err) + } }() } fmt.Printf("ok:%d err:%d\n", okCount, errCount) @@ -1795,28 +1792,28 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { func TestApplicationContext_Close(t *testing.T) { - t.Run("destroy type", func(t *testing.T) { - - assert.Panic(t, func() { - c := gs.New() - c.Object(func() {}).Destroy(func() {}) - }, "destroy should be func\\(bean\\) or func\\(bean\\)error") - - assert.Panic(t, func() { - c := gs.New() - c.Object(func() {}).Destroy(func() int { return 0 }) - }, "destroy should be func\\(bean\\) or func\\(bean\\)error") - - assert.Panic(t, func() { - c := gs.New() - c.Object(func() {}).Destroy(func(int) {}) - }, "destroy should be func\\(bean\\) or func\\(bean\\)error") - - assert.Panic(t, func() { - c := gs.New() - c.Object(func() {}).Destroy(func(int, int) {}) - }, "destroy should be func\\(bean\\) or func\\(bean\\)error") - }) + // t.Run("destroy type", func(t *testing.T) { + // + // assert.Panic(t, func() { + // c := gs.New() + // c.Object(func() {}).Destroy(func() {}) + // }, "destroy should be func\\(bean\\) or func\\(bean\\)error") + // + // assert.Panic(t, func() { + // c := gs.New() + // c.Object(func() {}).Destroy(func() int { return 0 }) + // }, "destroy should be func\\(bean\\) or func\\(bean\\)error") + // + // assert.Panic(t, func() { + // c := gs.New() + // c.Object(func() {}).Destroy(func(int) {}) + // }, "destroy should be func\\(bean\\) or func\\(bean\\)error") + // + // assert.Panic(t, func() { + // c := gs.New() + // c.Object(func() {}).Destroy(func(int, int) {}) + // }, "destroy should be func\\(bean\\) or func\\(bean\\)error") + // }) t.Run("call destroy fn", func(t *testing.T) { called := false diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 59435257..efe8b920 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -19,11 +19,10 @@ package gstest import ( "testing" - "github.com/go-spring/spring-base/util" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsutil" ) var ctx gs.Context @@ -65,7 +64,7 @@ func Bind(i interface{}, opts ...conf.BindArg) error { } // Get 获取对象 -func Get(i interface{}, selectors ...util.BeanSelector) error { +func Get(i interface{}, selectors ...gsutil.BeanSelector) error { return ctx.Get(i, selectors...) } diff --git a/gs/gsutil/type.go b/gs/gsutil/type.go new file mode 100644 index 00000000..547e5778 --- /dev/null +++ b/gs/gsutil/type.go @@ -0,0 +1,193 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gsutil + +import ( + "context" + "reflect" + "strings" +) + +// errorType the reflection type of error. +var errorType = reflect.TypeOf((*error)(nil)).Elem() + +var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() + +// TypeName returns a fully qualified name consisting of package path and type name. +func TypeName(i interface{}) string { + + var typ reflect.Type + switch o := i.(type) { + case reflect.Type: + typ = o + case reflect.Value: + typ = o.Type() + default: + typ = reflect.TypeOf(o) + } + + for { + if k := typ.Kind(); k == reflect.Ptr || k == reflect.Slice { + typ = typ.Elem() + } else { + break + } + } + + if pkgPath := typ.PkgPath(); pkgPath != "" { + pkgPath = strings.TrimSuffix(pkgPath, "_test") + return pkgPath + "/" + typ.String() + } + return typ.String() // the path of built-in type is empty +} + +// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as +// `(*error)(nil)`, or a BeanDefinition value. +type BeanSelector interface{} + +// A BeanDefinition describes a bean whose lifecycle is managed by IoC container. +type BeanDefinition interface { + Type() reflect.Type + Value() reflect.Value + Interface() interface{} + ID() string + BeanName() string + TypeName() string + Created() bool + Wired() bool +} + +// Converter converts string value into user-defined value. It should be function +// type, and its prototype is func(string)(type,error). +type Converter interface{} + +// IsConverter returns whether `t` is a converter type. +func IsConverter(t reflect.Type) bool { + return IsFuncType(t) && + t.NumIn() == 1 && + t.In(0).Kind() == reflect.String && + t.NumOut() == 2 && + (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) +} + +// IsFuncType returns whether `t` is func type. +func IsFuncType(t reflect.Type) bool { + return t.Kind() == reflect.Func +} + +// IsErrorType returns whether `t` is error type. +func IsErrorType(t reflect.Type) bool { + return t == errorType || t.Implements(errorType) +} + +// IsContextType returns whether `t` is context.Context type. +func IsContextType(t reflect.Type) bool { + return t == contextType || t.Implements(contextType) +} + +// ReturnNothing returns whether the function has no return value. +func ReturnNothing(t reflect.Type) bool { + return t.NumOut() == 0 +} + +// ReturnOnlyError returns whether the function returns only error value. +func ReturnOnlyError(t reflect.Type) bool { + return t.NumOut() == 1 && IsErrorType(t.Out(0)) +} + +// IsStructPtr returns whether it is the pointer type of structure. +func IsStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// IsConstructor returns whether `t` is a constructor type. What is a constructor? +// It should be a function first, has any number of inputs and supports the option +// pattern input, has one or two outputs and the second output should be an error. +func IsConstructor(t reflect.Type) bool { + returnError := t.NumOut() == 2 && IsErrorType(t.Out(1)) + return IsFuncType(t) && (t.NumOut() == 1 || returnError) +} + +// HasReceiver returns whether the function has a receiver. +func HasReceiver(t reflect.Type, receiver reflect.Value) bool { + if t.NumIn() < 1 { + return false + } + t0 := t.In(0) + if t0.Kind() != reflect.Interface { + return t0 == receiver.Type() + } + return receiver.Type().Implements(t0) +} + +// IsPrimitiveValueType returns whether `t` is the primitive value type which only is +// int, unit, float, bool, string and complex. +func IsPrimitiveValueType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Complex64, reflect.Complex128: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Bool: + return true + } + return false +} + +// IsValueType returns whether the input type is the primitive value type and their +// composite type including array, slice, map and struct, such as []int, [3]string, +// []string, map[int]int, map[string]string, etc. +func IsValueType(t reflect.Type) bool { + fn := func(t reflect.Type) bool { + return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct + } + switch t.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return fn(t.Elem()) + default: + return fn(t) + } +} + +// IsBeanType returns whether `t` is a bean type. +func IsBeanType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface: + return true + case reflect.Ptr: + return t.Elem().Kind() == reflect.Struct + default: + return false + } +} + +// IsBeanReceiver returns whether the `t` is a bean receiver, a bean receiver can +// be a bean, a map or slice whose elements are beans. +func IsBeanReceiver(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return IsBeanType(t.Elem()) + default: + return IsBeanType(t) + } +} diff --git a/gs/gsutil/value.go b/gs/gsutil/value.go new file mode 100644 index 00000000..83c5647a --- /dev/null +++ b/gs/gsutil/value.go @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gsutil + +import ( + "reflect" + "runtime" + "strings" + "unsafe" +) + +const ( + flagStickyRO = 1 << 5 + flagEmbedRO = 1 << 6 + flagRO = flagStickyRO | flagEmbedRO +) + +// PatchValue makes an unexported field can be assignable. +func PatchValue(v reflect.Value) reflect.Value { + rv := reflect.ValueOf(&v) + flag := rv.Elem().FieldByName("flag") + ptrFlag := (*uintptr)(unsafe.Pointer(flag.UnsafeAddr())) + *ptrFlag = *ptrFlag &^ flagRO + return v +} + +// Indirect returns its element type when t is a pointer type. +func Indirect(t reflect.Type) reflect.Type { + if t.Kind() != reflect.Ptr { + return t + } + return t.Elem() +} + +// FileLine returns a function's name, file name and line number. +func FileLine(fn interface{}) (file string, line int, fnName string) { + + fnPtr := reflect.ValueOf(fn).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + file, line = fnInfo.FileLine(fnPtr) + + s := fnInfo.Name() + if ss := strings.Split(s, "/"); len(ss) > 0 { + s = ss[len(ss)-1] + i := strings.Index(s, ".") + s = s[i+1:] + } + + // method values are printed as "T.m-fm" + s = strings.TrimRight(s, "-fm") + return file, line, s +} diff --git a/util/error.go b/util/error.go new file mode 100644 index 00000000..a0e424e6 --- /dev/null +++ b/util/error.go @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util + +import ( + "errors" + "fmt" +) + +// ForbiddenMethod throws this error when calling a method is prohibited. +var ForbiddenMethod = errors.New("forbidden method") + +// UnimplementedMethod throws this error when calling an unimplemented method. +var UnimplementedMethod = errors.New("unimplemented method") + +var WrapFormat = func(err error, fileline string, format string, a ...interface{}) error { + if err == nil { + if format != "" { + return fmt.Errorf(fileline+" "+format, a...) + } + return errors.New(fileline + " " + fmt.Sprint(a...)) + } + if format == "" { + return fmt.Errorf("%s %s; %w", fileline, fmt.Sprint(a...), err) + } + return fmt.Errorf("%s %s; %w", fileline, fmt.Sprintf(format, a...), err) +} + +// Error returns an error with the file and line. +// The file and line may be calculated at the compile time in the future. +func Error(fileline string, text string) error { + return WrapFormat(nil, fileline, "", text) +} + +// Errorf returns an error with the file and line. +// The file and line may be calculated at the compile time in the future. +func Errorf(fileline string, format string, a ...interface{}) error { + return WrapFormat(nil, fileline, format, a...) +} + +// Wrap returns an error with the file and line. +// The file and line may be calculated at the compile time in the future. +func Wrap(err error, fileline string, text string) error { + return WrapFormat(err, fileline, "", text) +} + +// Wrapf returns an error with the file and line. +// The file and line may be calculated at the compile time in the future. +func Wrapf(err error, fileline string, format string, a ...interface{}) error { + return WrapFormat(err, fileline, format, a...) +} diff --git a/gs/internal/triple_sort.go b/util/triple_sort.go similarity index 99% rename from gs/internal/triple_sort.go rename to util/triple_sort.go index 7cbb548c..50df19f6 100644 --- a/gs/internal/triple_sort.go +++ b/util/triple_sort.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package internal +package util import ( "container/list" From 9cd45a7fdf5e690d6ce8017dc46724e63747eaf4 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 19:21:09 +0800 Subject: [PATCH 09/75] use uber atomic, use gsbean --- atomic/bool.go | 64 --- atomic/duration.go | 60 --- atomic/float32.go | 59 --- atomic/float64.go | 59 --- atomic/int32.go | 58 --- atomic/int64.go | 59 --- atomic/nocopy.go | 35 -- atomic/pointer.go | 62 --- atomic/string.go | 46 -- atomic/time.go | 59 --- atomic/uint32.go | 58 --- atomic/uint64.go | 59 --- atomic/uintptr.go | 52 -- dync/bool.go | 2 +- dync/duration.go | 2 +- dync/dync.go | 2 +- dync/float32.go | 2 +- dync/float64.go | 2 +- dync/int32.go | 2 +- dync/int64.go | 2 +- dync/ref.go | 2 +- dync/string.go | 2 +- dync/time.go | 2 +- dync/uint32.go | 2 +- dync/uint64.go | 2 +- go.mod | 1 + go.sum | 2 + gs/app.go | 9 +- gs/app_bootstrap.go | 7 +- gs/boot.go | 9 +- gs/gs.go | 181 +++---- gs/gs_bean.go | 648 ++++++++++++------------- gs/gs_bean_test.go | 9 +- gs/gs_context.go | 8 +- gs/gs_test.go | 64 +-- gs/{arg => gsarg}/arg.go | 12 +- gs/{arg => gsarg}/arg_mock.go | 4 +- gs/{arg => gsarg}/arg_test.go | 26 +- gs/gsbean/gsbean.go | 340 +++++++++++++ gs/{cond => gscond}/cond.go | 2 +- gs/{cond => gscond}/cond_mock.go | 2 +- gs/{cond => gscond}/cond_test.go | 2 +- gs/gstest/gstest.go | 6 +- atomic/value.go => gs/syslog/syslog.go | 28 +- 44 files changed, 853 insertions(+), 1261 deletions(-) delete mode 100644 atomic/bool.go delete mode 100644 atomic/duration.go delete mode 100644 atomic/float32.go delete mode 100644 atomic/float64.go delete mode 100644 atomic/int32.go delete mode 100644 atomic/int64.go delete mode 100644 atomic/nocopy.go delete mode 100644 atomic/pointer.go delete mode 100644 atomic/string.go delete mode 100644 atomic/time.go delete mode 100644 atomic/uint32.go delete mode 100644 atomic/uint64.go delete mode 100644 atomic/uintptr.go rename gs/{arg => gsarg}/arg.go (97%) rename gs/{arg => gsarg}/arg_mock.go (97%) rename gs/{arg => gsarg}/arg_test.go (86%) create mode 100644 gs/gsbean/gsbean.go rename gs/{cond => gscond}/cond.go (99%) rename gs/{cond => gscond}/cond_mock.go (99%) rename gs/{cond => gscond}/cond_test.go (99%) rename atomic/value.go => gs/syslog/syslog.go (50%) diff --git a/atomic/bool.go b/atomic/bool.go deleted file mode 100644 index 59dd6eb0..00000000 --- a/atomic/bool.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// A Bool is an atomic bool value. -type Bool struct { - _ nocopy - v uint32 -} - -func bool2uint(val bool) uint32 { - if val { - return 1 - } - return 0 -} - -func uint2bool(val uint32) bool { - return val != 0 -} - -// Load atomically loads and returns the value stored in x. -func (x *Bool) Load() (val bool) { - return uint2bool(atomic.LoadUint32(&x.v)) -} - -// Store atomically stores val into x. -func (x *Bool) Store(val bool) { - atomic.StoreUint32(&x.v, bool2uint(val)) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Bool) Swap(new bool) (old bool) { - return uint2bool(atomic.SwapUint32(&x.v, bool2uint(new))) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Bool) CompareAndSwap(old, new bool) (swapped bool) { - return atomic.CompareAndSwapUint32(&x.v, bool2uint(old), bool2uint(new)) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Bool) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/duration.go b/atomic/duration.go deleted file mode 100644 index dedef00b..00000000 --- a/atomic/duration.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" - "time" -) - -// A Duration is an atomic time.Duration value. -type Duration struct { - _ nocopy - _ align64 - v int64 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Duration) Add(delta time.Duration) time.Duration { - return time.Duration(atomic.AddInt64(&x.v, int64(delta))) -} - -// Load atomically loads and returns the value stored in x. -func (x *Duration) Load() time.Duration { - return time.Duration(atomic.LoadInt64(&x.v)) -} - -// Store atomically stores val into x. -func (x *Duration) Store(val time.Duration) { - atomic.StoreInt64(&x.v, int64(val)) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Duration) Swap(new time.Duration) time.Duration { - return time.Duration(atomic.SwapInt64(&x.v, int64(new))) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Duration) CompareAndSwap(old, new time.Duration) bool { - return atomic.CompareAndSwapInt64(&x.v, int64(old), int64(new)) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Duration) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/float32.go b/atomic/float32.go deleted file mode 100644 index bc14abee..00000000 --- a/atomic/float32.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "math" - "sync/atomic" -) - -// A Float32 is an atomic float32 value. -type Float32 struct { - _ nocopy - v uint32 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Float32) Add(delta float32) (new float32) { - return math.Float32frombits(atomic.AddUint32(&x.v, math.Float32bits(delta))) -} - -// Load atomically loads and returns the value stored in x. -func (x *Float32) Load() (val float32) { - return math.Float32frombits(atomic.LoadUint32(&x.v)) -} - -// Store atomically stores val into x. -func (x *Float32) Store(val float32) { - atomic.StoreUint32(&x.v, math.Float32bits(val)) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Float32) Swap(new float32) (old float32) { - return math.Float32frombits(atomic.SwapUint32(&x.v, math.Float32bits(new))) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Float32) CompareAndSwap(old, new float32) (swapped bool) { - return atomic.CompareAndSwapUint32(&x.v, math.Float32bits(old), math.Float32bits(new)) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Float32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/float64.go b/atomic/float64.go deleted file mode 100644 index faeb96c6..00000000 --- a/atomic/float64.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "math" - "sync/atomic" -) - -// A Float64 is an atomic float64 value. -type Float64 struct { - _ nocopy - v uint64 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Float64) Add(delta float64) (new float64) { - return math.Float64frombits(atomic.AddUint64(&x.v, math.Float64bits(delta))) -} - -// Load atomically loads and returns the value stored in x. -func (x *Float64) Load() (val float64) { - return math.Float64frombits(atomic.LoadUint64(&x.v)) -} - -// Store atomically stores val into x. -func (x *Float64) Store(val float64) { - atomic.StoreUint64(&x.v, math.Float64bits(val)) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Float64) Swap(new float64) (old float64) { - return math.Float64frombits(atomic.SwapUint64(&x.v, math.Float64bits(new))) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Float64) CompareAndSwap(old, new float64) (swapped bool) { - return atomic.CompareAndSwapUint64(&x.v, math.Float64bits(old), math.Float64bits(new)) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Float64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/int32.go b/atomic/int32.go deleted file mode 100644 index 5ff44826..00000000 --- a/atomic/int32.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// An Int32 is an atomic int32 value. -type Int32 struct { - _ nocopy - v int32 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Int32) Add(delta int32) (new int32) { - return atomic.AddInt32(&x.v, delta) -} - -// Load atomically loads and returns the value stored in x. -func (x *Int32) Load() (val int32) { - return atomic.LoadInt32(&x.v) -} - -// Store atomically stores val into x. -func (x *Int32) Store(val int32) { - atomic.StoreInt32(&x.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Int32) Swap(new int32) (old int32) { - return atomic.SwapInt32(&x.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Int32) CompareAndSwap(old, new int32) (swapped bool) { - return atomic.CompareAndSwapInt32(&x.v, old, new) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Int32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/int64.go b/atomic/int64.go deleted file mode 100644 index 44675a4d..00000000 --- a/atomic/int64.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// An Int64 is an atomic int64 value. -type Int64 struct { - _ nocopy - _ align64 - v int64 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Int64) Add(delta int64) int64 { - return atomic.AddInt64(&x.v, delta) -} - -// Load atomically loads and returns the value stored in x. -func (x *Int64) Load() int64 { - return atomic.LoadInt64(&x.v) -} - -// Store atomically stores val into x. -func (x *Int64) Store(val int64) { - atomic.StoreInt64(&x.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Int64) Swap(new int64) int64 { - return atomic.SwapInt64(&x.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Int64) CompareAndSwap(old, new int64) bool { - return atomic.CompareAndSwapInt64(&x.v, old, new) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Int64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/nocopy.go b/atomic/nocopy.go deleted file mode 100644 index afef5fef..00000000 --- a/atomic/nocopy.go +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -// nocopy may be added to structs which must not be copied -// after the first use. -// -// See https://golang.org/issues/8005#issuecomment-190753527 -// for details. -// -// Note that it must not be embedded, due to the Lock and Unlock methods. -type nocopy struct{} - -// Lock is a no-op used by -copylocks checker from `go vet`. -func (*nocopy) Lock() {} -func (*nocopy) Unlock() {} - -// align64 may be added to structs that must be 64-bit aligned. -// This struct is recognized by a special case in the compiler -// and will not work if copied to any other package. -type align64 struct{} diff --git a/atomic/pointer.go b/atomic/pointer.go deleted file mode 100644 index 67cac7e4..00000000 --- a/atomic/pointer.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "sync/atomic" - "unsafe" -) - -type MarshalPointer func(unsafe.Pointer) ([]byte, error) - -// A Pointer is an atomic pointer value. -type Pointer struct { - _ nocopy - v unsafe.Pointer - - marshalJSON MarshalPointer -} - -// Load atomically loads and returns the value stored in x. -func (p *Pointer) Load() (val unsafe.Pointer) { - return atomic.LoadPointer(&p.v) -} - -// Store atomically stores val into x. -func (p *Pointer) Store(val unsafe.Pointer) { - atomic.StorePointer(&p.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (p *Pointer) Swap(new unsafe.Pointer) (old unsafe.Pointer) { - return atomic.SwapPointer(&p.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (p *Pointer) CompareAndSwap(old, new unsafe.Pointer) (swapped bool) { - return atomic.CompareAndSwapPointer(&p.v, old, new) -} - -// SetMarshalJSON sets the JSON encoding handler for x. -func (p *Pointer) SetMarshalJSON(fn MarshalPointer) { - p.marshalJSON = fn -} - -// MarshalJSON returns the JSON encoding of x. -func (p *Pointer) MarshalJSON() ([]byte, error) { - return p.marshalJSON(p.Load()) -} diff --git a/atomic/string.go b/atomic/string.go deleted file mode 100644 index 88fdfad2..00000000 --- a/atomic/string.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// A String is an atomic string value. -type String struct { - _ nocopy - v atomic.Value -} - -// Load atomically loads and returns the value stored in x. -func (x *String) Load() string { - if r := x.v.Load(); r != nil { - return r.(string) - } - return "" -} - -// Store atomically stores val into x. -func (x *String) Store(val string) { - x.v.Store(val) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *String) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/time.go b/atomic/time.go deleted file mode 100644 index 71620476..00000000 --- a/atomic/time.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" - "time" -) - -type MarshalTime func(time.Time) ([]byte, error) - -// A Time is an atomic time.Time value. -type Time struct { - _ nocopy - v atomic.Value - - marshalJSON MarshalTime -} - -// Load atomically loads and returns the value stored in x. -func (x *Time) Load() time.Time { - if x, ok := x.v.Load().(time.Time); ok { - return x - } - return time.Time{} -} - -// Store atomically stores val into x. -func (x *Time) Store(val time.Time) { - x.v.Store(val) -} - -// SetMarshalJSON sets the JSON encoding handler for x. -func (x *Time) SetMarshalJSON(fn MarshalTime) { - x.marshalJSON = fn -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Time) MarshalJSON() ([]byte, error) { - if x.marshalJSON != nil { - return x.marshalJSON(x.Load()) - } - return json.Marshal(x.Load().Format(time.UnixDate)) -} diff --git a/atomic/uint32.go b/atomic/uint32.go deleted file mode 100644 index d56fb79b..00000000 --- a/atomic/uint32.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// An Uint32 is an atomic uint32 value. -type Uint32 struct { - _ nocopy - v uint32 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Uint32) Add(delta uint32) (new uint32) { - return atomic.AddUint32(&x.v, delta) -} - -// Load atomically loads and returns the value stored in x. -func (x *Uint32) Load() (val uint32) { - return atomic.LoadUint32(&x.v) -} - -// Store atomically stores val into x. -func (x *Uint32) Store(val uint32) { - atomic.StoreUint32(&x.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Uint32) Swap(new uint32) (old uint32) { - return atomic.SwapUint32(&x.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool) { - return atomic.CompareAndSwapUint32(&x.v, old, new) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Uint32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/uint64.go b/atomic/uint64.go deleted file mode 100644 index 2b233647..00000000 --- a/atomic/uint64.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "encoding/json" - "sync/atomic" -) - -// An Uint64 is an atomic uint64 value. -type Uint64 struct { - _ nocopy - _ align64 - v uint64 -} - -// Add atomically adds delta to x and returns the new value. -func (x *Uint64) Add(delta uint64) (new uint64) { - return atomic.AddUint64(&x.v, delta) -} - -// Load atomically loads and returns the value stored in x. -func (x *Uint64) Load() (val uint64) { - return atomic.LoadUint64(&x.v) -} - -// Store atomically stores val into x. -func (x *Uint64) Store(val uint64) { - atomic.StoreUint64(&x.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Uint64) Swap(new uint64) (old uint64) { - return atomic.SwapUint64(&x.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool) { - return atomic.CompareAndSwapUint64(&x.v, old, new) -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Uint64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Load()) -} diff --git a/atomic/uintptr.go b/atomic/uintptr.go deleted file mode 100644 index f58b3940..00000000 --- a/atomic/uintptr.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package atomic - -import ( - "sync/atomic" -) - -// An Uintptr is an atomic uintptr value. -type Uintptr struct { - _ nocopy - v uintptr -} - -// Add atomically adds delta to x and returns the new value. -func (x *Uintptr) Add(delta uintptr) (new uintptr) { - return atomic.AddUintptr(&x.v, delta) -} - -// Load atomically loads and returns the value stored in x. -func (x *Uintptr) Load() (val uintptr) { - return atomic.LoadUintptr(&x.v) -} - -// Store atomically stores val into x. -func (x *Uintptr) Store(val uintptr) { - atomic.StoreUintptr(&x.v, val) -} - -// Swap atomically stores new into x and returns the old value. -func (x *Uintptr) Swap(new uintptr) (old uintptr) { - return atomic.SwapUintptr(&x.v, new) -} - -// CompareAndSwap executes the compare-and-swap operation for x. -func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool) { - return atomic.CompareAndSwapUintptr(&x.v, old, new) -} diff --git a/dync/bool.go b/dync/bool.go index ad8d8304..8f2ef3e6 100644 --- a/dync/bool.go +++ b/dync/bool.go @@ -18,10 +18,10 @@ package dync import ( "encoding/json" - "sync/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Bool struct { diff --git a/dync/duration.go b/dync/duration.go index ff5d1959..dd83b0e1 100644 --- a/dync/duration.go +++ b/dync/duration.go @@ -20,9 +20,9 @@ import ( "encoding/json" "time" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type DurationValidateFunc func(v time.Duration) error diff --git a/dync/dync.go b/dync/dync.go index 5150de3f..bec0b1a1 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -22,9 +22,9 @@ import ( "sort" "strings" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/expr" + "go.uber.org/atomic" ) // Value 可动态刷新的对象 diff --git a/dync/float32.go b/dync/float32.go index 032509a2..e02df928 100644 --- a/dync/float32.go +++ b/dync/float32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Float32ValidateFunc func(v float32) error diff --git a/dync/float64.go b/dync/float64.go index f18b3944..4f06c1df 100644 --- a/dync/float64.go +++ b/dync/float64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Float64ValidateFunc func(v float64) error diff --git a/dync/int32.go b/dync/int32.go index 3f00ba8a..294a9873 100644 --- a/dync/int32.go +++ b/dync/int32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Int32ValidateFunc func(v int32) error diff --git a/dync/int64.go b/dync/int64.go index bb99575a..422c6427 100644 --- a/dync/int64.go +++ b/dync/int64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Int64ValidateFunc func(v int64) error diff --git a/dync/ref.go b/dync/ref.go index 1a786f93..6b28da6c 100644 --- a/dync/ref.go +++ b/dync/ref.go @@ -20,8 +20,8 @@ import ( "encoding/json" "reflect" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "go.uber.org/atomic" ) type RefValidateFunc func(v interface{}) error diff --git a/dync/string.go b/dync/string.go index 934831d6..c368f198 100644 --- a/dync/string.go +++ b/dync/string.go @@ -19,8 +19,8 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" + "go.uber.org/atomic" ) type StringValidateFunc func(v string) error diff --git a/dync/time.go b/dync/time.go index e6c705fd..98c3b6d8 100644 --- a/dync/time.go +++ b/dync/time.go @@ -20,9 +20,9 @@ import ( "encoding/json" "time" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type TimeValidateFunc func(v time.Time) error diff --git a/dync/uint32.go b/dync/uint32.go index 4685fdfd..d6451075 100644 --- a/dync/uint32.go +++ b/dync/uint32.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Uint32ValidateFunc func(v uint32) error diff --git a/dync/uint64.go b/dync/uint64.go index 810ea81b..0aad5db2 100644 --- a/dync/uint64.go +++ b/dync/uint64.go @@ -19,9 +19,9 @@ package dync import ( "encoding/json" - "github.com/go-spring/spring-core/atomic" "github.com/go-spring/spring-core/conf" "github.com/spf13/cast" + "go.uber.org/atomic" ) type Uint64ValidateFunc func(v uint64) error diff --git a/go.mod b/go.mod index 00e7c32d..da758ee1 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/spf13/cast v1.7.1 github.com/stretchr/testify v1.10.0 + go.uber.org/atomic v1.11.0 go.uber.org/mock v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 9549e4b9..b79c2c1e 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/gs/app.go b/gs/app.go index 99f88b89..618a09cb 100644 --- a/gs/app.go +++ b/gs/app.go @@ -25,7 +25,8 @@ import ( "strings" "syscall" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" ) // SpringBannerVisible 是否显示 banner。 @@ -232,16 +233,16 @@ func (app *App) Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *BeanDefinition) *BeanDefinition { +func (app *App) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { return app.c.Accept(b) } // Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *BeanDefinition { +func (app *App) Object(i interface{}) *gsbean.BeanDefinition { return app.c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { +func (app *App) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { return app.c.Accept(NewBean(ctor, args...)) } diff --git a/gs/app_bootstrap.go b/gs/app_bootstrap.go index 2c38ca58..29165ba6 100644 --- a/gs/app_bootstrap.go +++ b/gs/app_bootstrap.go @@ -19,7 +19,8 @@ package gs import ( "reflect" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" ) type Bootstrapper struct { @@ -43,12 +44,12 @@ func (b *Bootstrapper) Property(key string, value interface{}) { } // Object 参考 Container.Object 的解释。 -func (b *Bootstrapper) Object(i interface{}) *BeanDefinition { +func (b *Bootstrapper) Object(i interface{}) *gsbean.BeanDefinition { return b.c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *Bootstrapper) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { +func (b *Bootstrapper) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { return b.c.Accept(NewBean(ctor, args...)) } diff --git a/gs/boot.go b/gs/boot.go index 967a1818..820079dc 100644 --- a/gs/boot.go +++ b/gs/boot.go @@ -19,7 +19,8 @@ package gs import ( "reflect" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" ) var app = NewApp() @@ -60,16 +61,16 @@ func Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func Accept(b *BeanDefinition) *BeanDefinition { +func Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { return app.c.Accept(b) } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *BeanDefinition { +func Object(i interface{}) *gsbean.BeanDefinition { return app.c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { +func Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { return app.c.Accept(NewBean(ctor, args...)) } diff --git a/gs/gs.go b/gs/gs.go index 9e3aade5..3e613f0c 100755 --- a/gs/gs.go +++ b/gs/gs.go @@ -31,8 +31,9 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/arg" - "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/gscond" "github.com/go-spring/spring-core/gs/gsutil" "github.com/go-spring/spring-core/util" ) @@ -54,8 +55,8 @@ type Container interface { Context() context.Context Properties() *dync.Properties Property(key string, value interface{}) - Object(i interface{}) *BeanDefinition - Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition + Object(i interface{}) *gsbean.BeanDefinition + Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition Refresh() error Close() } @@ -76,8 +77,8 @@ type Context interface { Resolve(s string) (string, error) Bind(i interface{}, opts ...conf.BindArg) error Get(i interface{}, selectors ...gsutil.BeanSelector) error - Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) - Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) + Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) + Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) } @@ -88,9 +89,9 @@ type ContextAware struct { type tempContainer struct { initProperties *conf.Properties - beans []*BeanDefinition - beansByName map[string][]*BeanDefinition - beansByType map[reflect.Type][]*BeanDefinition + beans []*gsbean.BeanDefinition + beansByName map[string][]*gsbean.BeanDefinition + beansByType map[reflect.Type][]*gsbean.BeanDefinition mapOfOnProperty map[string]interface{} } @@ -122,8 +123,8 @@ func New() Container { p: dync.New(), tempContainer: &tempContainer{ initProperties: conf.NewProperties(), - beansByName: make(map[string][]*BeanDefinition), - beansByType: make(map[reflect.Type][]*BeanDefinition), + beansByName: make(map[string][]*gsbean.BeanDefinition), + beansByType: make(map[reflect.Type][]*gsbean.BeanDefinition), mapOfOnProperty: make(map[string]interface{}), }, } @@ -167,7 +168,7 @@ func (c *container) Property(key string, value interface{}) { c.initProperties.Set(key, value) } -func (c *container) Accept(b *BeanDefinition) *BeanDefinition { +func (c *container) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } @@ -176,22 +177,22 @@ func (c *container) Accept(b *BeanDefinition) *BeanDefinition { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Object(i interface{}) *BeanDefinition { +func (c *container) Object(i interface{}) *gsbean.BeanDefinition { return c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Provide(ctor interface{}, args ...arg.Arg) *BeanDefinition { +func (c *container) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { return c.Accept(NewBean(ctor, args...)) } // destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 type destroyer struct { - current *BeanDefinition - earlier []*BeanDefinition + current *gsbean.BeanDefinition + earlier []*gsbean.BeanDefinition } -func (d *destroyer) foundEarlier(b *BeanDefinition) bool { +func (d *destroyer) foundEarlier(b *gsbean.BeanDefinition) bool { for _, c := range d.earlier { if c == b { return true @@ -201,7 +202,7 @@ func (d *destroyer) foundEarlier(b *BeanDefinition) bool { } // after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 -func (d *destroyer) after(b *BeanDefinition) { +func (d *destroyer) after(b *gsbean.BeanDefinition) { if d.foundEarlier(b) { return } @@ -231,7 +232,7 @@ type lazyField struct { type wiringStack struct { destroyers *list.List destroyerMap map[string]*destroyer - beans []*BeanDefinition + beans []*gsbean.BeanDefinition lazyFields []lazyField } @@ -243,7 +244,7 @@ func newWiringStack() *wiringStack { } // pushBack 添加一个即将注入的 bean 。 -func (s *wiringStack) pushBack(b *BeanDefinition) { +func (s *wiringStack) pushBack(b *gsbean.BeanDefinition) { // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) s.beans = append(s.beans, b) } @@ -265,7 +266,7 @@ func (s *wiringStack) path() (path string) { } // saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 -func (s *wiringStack) saveDestroyer(b *BeanDefinition) *destroyer { +func (s *wiringStack) saveDestroyer(b *gsbean.BeanDefinition) *destroyer { d, ok := s.destroyerMap[b.ID()] if !ok { d = &destroyer{current: b} @@ -300,7 +301,7 @@ func (s *wiringStack) sortDestroyers() []func() { var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { d := e.Value.(*destroyer).current - ret = append(ret, destroy(d.Value(), d.destroy)) + ret = append(ret, destroy(d.Value(), d.GetDestroy())) } return ret } @@ -347,14 +348,14 @@ func (c *container) refresh(autoClear bool) (err error) { } } - beansById := make(map[string]*BeanDefinition) + beansById := make(map[string]*gsbean.BeanDefinition) { for _, b := range c.beans { - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { continue } - if b.status != Resolved { - return fmt.Errorf("unexpected status %d", b.status) + if b.GetStatus() != gsbean.Resolved { + return fmt.Errorf("unexpected status %d", b.GetStatus()) } beanID := b.ID() if d, ok := beansById[beanID]; ok { @@ -414,30 +415,30 @@ func (c *container) refresh(autoClear bool) (err error) { return nil } -func (c *container) registerBean(b *BeanDefinition) { +func (c *container) registerBean(b *gsbean.BeanDefinition) { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) - c.beansByName[b.name] = append(c.beansByName[b.name], b) + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) - for _, t := range b.exports { + for _, t := range b.GetExports() { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), t, b.FileLine()) c.beansByType[t] = append(c.beansByType[t], b) } } // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 -func (c *container) resolveBean(b *BeanDefinition) error { +func (c *container) resolveBean(b *gsbean.BeanDefinition) error { - if b.status >= Resolving { + if b.GetStatus() >= gsbean.Resolving { return nil } - b.status = Resolving + b.SetStatus(gsbean.Resolving) // method bean 先确定 parent bean 是否存在 - if b.method { - selector, ok := b.f.Arg(0) + if b.IsMethod() { + selector, ok := b.F.Arg(0) if !ok || selector == "" { - selector, _ = b.f.In(0) + selector, _ = b.F.In(0) } parents, err := c.findBean(selector) if err != nil { @@ -445,28 +446,28 @@ func (c *container) resolveBean(b *BeanDefinition) error { } n := len(parents) if n > 1 { - msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.t.In(0)) + msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.T.In(0)) for _, b := range parents { msg += "( " + b.String() + " ), " } msg = msg[:len(msg)-2] + "]" return errors.New(msg) } else if n == 0 { - b.status = Deleted + b.SetStatus(gsbean.Deleted) return nil } } - if b.cond != nil { - if ok, err := b.cond.Matches(c); err != nil { + if b.GetCond() != nil { + if ok, err := b.GetCond().Matches(c); err != nil { return err } else if !ok { - b.status = Deleted + b.SetStatus(gsbean.Deleted) return nil } } - b.status = Resolved + b.SetStatus(gsbean.Resolved) return nil } @@ -518,9 +519,9 @@ func toWireTag(selector gsutil.BeanSelector) wireTag { switch s := selector.(type) { case string: return parseWireTag(s) - case BeanDefinition: + case gsbean.BeanDefinition: return parseWireTag(s.ID()) - case *BeanDefinition: + case *gsbean.BeanDefinition: return parseWireTag(s.ID()) default: return parseWireTag(gsutil.TypeName(s) + ":") @@ -540,18 +541,18 @@ func toWireString(tags []wireTag) string { // findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) findBean(selector gsutil.BeanSelector) ([]*BeanDefinition, error) { +func (c *container) findBean(selector gsutil.BeanSelector) ([]*gsbean.BeanDefinition, error) { - finder := func(fn func(*BeanDefinition) bool) ([]*BeanDefinition, error) { - var result []*BeanDefinition + finder := func(fn func(*gsbean.BeanDefinition) bool) ([]*gsbean.BeanDefinition, error) { + var result []*gsbean.BeanDefinition for _, b := range c.beans { - if b.status == Resolving || b.status == Deleted || !fn(b) { + if b.GetStatus() == gsbean.Resolving || b.GetStatus() == gsbean.Deleted || !fn(b) { continue } if err := c.resolveBean(b); err != nil { return nil, err } - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { continue } result = append(result, b) @@ -561,9 +562,9 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*BeanDefinition, e var t reflect.Type switch st := selector.(type) { - case string, BeanDefinition, *BeanDefinition: + case string, gsbean.BeanDefinition, *gsbean.BeanDefinition: tag := toWireTag(selector) - return finder(func(b *BeanDefinition) bool { + return finder(func(b *gsbean.BeanDefinition) bool { return b.Match(tag.typeName, tag.beanName) }) case reflect.Type: @@ -578,11 +579,11 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*BeanDefinition, e } } - return finder(func(b *BeanDefinition) bool { + return finder(func(b *gsbean.BeanDefinition) bool { if b.Type() == t { return true } - for _, typ := range b.exports { + for _, typ := range b.GetExports() { if typ == t { return true } @@ -594,14 +595,14 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*BeanDefinition, e // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *container) wireBean(b *BeanDefinition, stack *wiringStack) error { +func (c *container) wireBean(b *gsbean.BeanDefinition, stack *wiringStack) error { - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) } // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.status == Wired { + if c.state == Refreshed && b.GetStatus() == gsbean.Wired { return nil } @@ -614,33 +615,33 @@ func (c *container) wireBean(b *BeanDefinition, stack *wiringStack) error { }() // 记录注入路径上的销毁函数及其执行的先后顺序。 - if _, ok := b.Interface().(BeanDestroy); ok || b.destroy != nil { + if _, ok := b.Interface().(BeanDestroy); ok || b.GetDestroy() != nil { haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { - d.after(i.Value.(*BeanDefinition)) + d.after(i.Value.(*gsbean.BeanDefinition)) } stack.destroyers.PushBack(b) } stack.pushBack(b) - if b.status == Creating && b.f != nil { + if b.GetStatus() == gsbean.Creating && b.F != nil { prev := stack.beans[len(stack.beans)-2] - if prev.status == Creating { + if prev.GetStatus() == gsbean.Creating { return errors.New("found circle autowire") } } - if b.status >= Creating { + if b.GetStatus() >= gsbean.Creating { stack.popBack() return nil } - b.status = Creating + b.SetStatus(gsbean.Creating) // 对当前 bean 的间接依赖项进行注入。 - for _, s := range b.depends { + for _, s := range b.GetDepends() { beans, err := c.findBean(s) if err != nil { return err @@ -658,10 +659,10 @@ func (c *container) wireBean(b *BeanDefinition, stack *wiringStack) error { return err } - b.status = Created + b.SetStatus(gsbean.Created) t := v.Type() - for _, typ := range b.exports { + for _, typ := range b.GetExports() { if !t.Implements(typ) { return fmt.Errorf("%s doesn't implement interface %s", b, typ) } @@ -672,8 +673,8 @@ func (c *container) wireBean(b *BeanDefinition, stack *wiringStack) error { return err } - if b.init != nil { - fnValue := reflect.ValueOf(b.init) + if b.GetInit() != nil { + fnValue := reflect.ValueOf(b.GetInit()) out := fnValue.Call([]reflect.Value{b.Value()}) if len(out) > 0 && !out[0].IsNil() { return out[0].Interface().(error) @@ -686,7 +687,7 @@ func (c *container) wireBean(b *BeanDefinition, stack *wiringStack) error { } } - b.status = Wired + b.SetStatus(gsbean.Wired) stack.popBack() return nil } @@ -696,7 +697,7 @@ type argContext struct { stack *wiringStack } -func (a *argContext) Matches(c cond.Condition) (bool, error) { +func (a *argContext) Matches(c gscond.Condition) (bool, error) { return c.Matches(a.c) } @@ -709,13 +710,13 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { } // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *container) getBeanValue(b *BeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *container) getBeanValue(b *gsbean.BeanDefinition, stack *wiringStack) (reflect.Value, error) { - if b.f == nil { + if b.F == nil { return b.Value(), nil } - out, err := b.f.Call(&argContext{c: c, stack: stack}) + out, err := b.F.Call(&argContext{c: c, stack: stack}) if err != nil { return reflect.Value{}, err /* fmt.Errorf("%s:%s return error: %v", b.getClass(), b.ID(), err) */ } @@ -735,7 +736,7 @@ func (c *container) getBeanValue(b *BeanDefinition, stack *wiringStack) (reflect } if b.Value().IsNil() { - return reflect.Value{}, fmt.Errorf("%s:%q return nil", b.getClass(), b.FileLine()) + return reflect.Value{}, fmt.Errorf("%s:%q return nil", b.GetClass(), b.FileLine()) } v := b.Value() @@ -888,9 +889,9 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []*BeanDefinition + var foundBeans []*gsbean.BeanDefinition for _, b := range c.beansByType[t] { - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { continue } if !b.Match(tag.typeName, tag.beanName) { @@ -902,7 +903,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 if t.Kind() == reflect.Interface && tag.beanName != "" { for _, b := range c.beansByName[tag.beanName] { - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { continue } if !b.Type().AssignableTo(t) { @@ -934,10 +935,10 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []*BeanDefinition + var primaryBeans []*gsbean.BeanDefinition for _, b := range foundBeans { - if b.primary { + if b.IsPrimary() { primaryBeans = append(primaryBeans, b) } } @@ -960,7 +961,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result *BeanDefinition + var result *gsbean.BeanDefinition if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -978,7 +979,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*BeanDefinition, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []*gsbean.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -1008,10 +1009,10 @@ func filterBean(beans []*BeanDefinition, tag wireTag, t reflect.Type) (int, erro return -1, fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) } -type byOrder []*BeanDefinition +type byOrder []*gsbean.BeanDefinition func (b byOrder) Len() int { return len(b) } -func (b byOrder) Less(i, j int) bool { return b[i].order < b[j].order } +func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } func (b byOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { @@ -1026,7 +1027,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []*BeanDefinition + var beans []*gsbean.BeanDefinition if et.Kind() == reflect.Interface && et.NumMethod() == 0 { beans = c.beans } else { @@ -1034,9 +1035,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } { - var arr []*BeanDefinition + var arr []*gsbean.BeanDefinition for _, b := range beans { - if b.status == Deleted { + if b.GetStatus() == gsbean.Deleted { continue } arr = append(arr, b) @@ -1047,9 +1048,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []*BeanDefinition - afterAny []*BeanDefinition - beforeAny []*BeanDefinition + anyBeans []*gsbean.BeanDefinition + afterAny []*gsbean.BeanDefinition + beforeAny []*gsbean.BeanDefinition ) foundAny := false @@ -1078,7 +1079,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]*BeanDefinition{}, beans[:index]...) + tmpBeans := append([]*gsbean.BeanDefinition{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -1087,7 +1088,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*BeanDefinition, 0, n) + arr := make([]*gsbean.BeanDefinition, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) @@ -1123,7 +1124,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, case reflect.Map: ret = reflect.MakeMap(t) for _, b := range beans { - ret.SetMapIndex(reflect.ValueOf(b.name), b.Value()) + ret.SetMapIndex(reflect.ValueOf(b.GetName()), b.Value()) } } v.Set(ret) diff --git a/gs/gs_bean.go b/gs/gs_bean.go index cded614c..ccf829eb 100755 --- a/gs/gs_bean.go +++ b/gs/gs_bean.go @@ -17,53 +17,46 @@ package gs import ( - "errors" - "fmt" - "reflect" - "runtime" - "strings" - - "github.com/go-spring/spring-core/gs/arg" - "github.com/go-spring/spring-core/gs/cond" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" ) -type beanStatus int8 - -const ( - Deleted = beanStatus(-1) // 已删除 - Default = beanStatus(iota) // 未处理 - Resolving // 正在决议 - Resolved // 已决议 - Creating // 正在创建 - Created // 已创建 - Wired // 注入完成 -) - -func getStatusString(status beanStatus) string { - switch status { - case Deleted: - return "Deleted" - case Default: - return "Default" - case Resolving: - return "Resolving" - case Resolved: - return "Resolved" - case Creating: - return "Creating" - case Created: - return "Created" - case Wired: - return "Wired" - default: - return "" - } -} - -func BeanID(typ interface{}, name string) string { - return gsutil.TypeName(typ) + ":" + name -} +// type beanStatus int8 +// +// const ( +// Deleted = beanStatus(-1) // 已删除 +// Default = beanStatus(iota) // 未处理 +// Resolving // 正在决议 +// Resolved // 已决议 +// Creating // 正在创建 +// Created // 已创建 +// Wired // 注入完成 +// ) +// +// func getStatusString(status beanStatus) string { +// switch status { +// case Deleted: +// return "Deleted" +// case Default: +// return "Default" +// case Resolving: +// return "Resolving" +// case Resolved: +// return "Resolved" +// case Creating: +// return "Creating" +// case Created: +// return "Created" +// case Wired: +// return "Wired" +// default: +// return "" +// } +// } +// +// func BeanID(typ interface{}, name string) string { +// return gsutil.TypeName(typ) + ":" + name +// } type BeanInit interface { OnInit(ctx Context) error @@ -73,286 +66,291 @@ type BeanDestroy interface { OnDestroy() } -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - - // 原始类型的全限定名 - typeName string - - v reflect.Value // 值 - t reflect.Type // 类型 - f *arg.Callable // 构造函数 - - file string // 注册点所在文件 - line int // 注册点所在行数 - - name string // 名称 - status beanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond cond.Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []gsutil.BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 -} - -// Type 返回 bean 的类型。 -func (d *BeanDefinition) Type() reflect.Type { - return d.t -} - -// Value 返回 bean 的值。 -func (d *BeanDefinition) Value() reflect.Value { - return d.v -} - -// Interface 返回 bean 的真实值。 -func (d *BeanDefinition) Interface() interface{} { - return d.v.Interface() -} - -// ID 返回 bean 的 ID 。 -func (d *BeanDefinition) ID() string { - return d.typeName + ":" + d.name -} - -// BeanName 返回 bean 的名称。 -func (d *BeanDefinition) BeanName() string { - return d.name -} - -// TypeName 返回 bean 的原始类型的全限定名。 -func (d *BeanDefinition) TypeName() string { - return d.typeName -} - -// Created 返回是否已创建。 -func (d *BeanDefinition) Created() bool { - return d.status >= Created -} - -// Wired 返回 bean 是否已经注入。 -func (d *BeanDefinition) Wired() bool { - return d.status == Wired -} - -// FileLine 返回 bean 的注册点。 -func (d *BeanDefinition) FileLine() string { - return fmt.Sprintf("%s:%d", d.file, d.line) -} - -// getClass 返回 bean 的类型描述。 -func (d *BeanDefinition) getClass() string { - if d.f == nil { - return "object bean" - } - return "constructor bean" -} - -func (d *BeanDefinition) String() string { - return fmt.Sprintf("%s name:%q %s", d.getClass(), d.name, d.FileLine()) -} - -// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 -func (d *BeanDefinition) Match(typeName string, beanName string) bool { - - typeIsSame := false - if typeName == "" || d.typeName == typeName { - typeIsSame = true - } - - nameIsSame := false - if beanName == "" || d.name == beanName { - nameIsSame = true - } - - return typeIsSame && nameIsSame -} - -// Name 设置 bean 的名称。 -func (d *BeanDefinition) Name(name string) *BeanDefinition { - d.name = name - return d -} - -// On 设置 bean 的 Condition。 -func (d *BeanDefinition) On(cond cond.Condition) *BeanDefinition { - d.cond = cond - return d -} - -// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 -func (d *BeanDefinition) Order(order float32) *BeanDefinition { - d.order = order - return d -} - -// DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { - d.depends = append(d.depends, selectors...) - return d -} - -// Primary 设置 bean 为主版本。 -func (d *BeanDefinition) Primary() *BeanDefinition { - d.primary = true - return d -} - -// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 -// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 -func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !gsutil.IsFuncType(fnType) { - return false - } - if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { - return false - } - return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) -} - -// Init 设置 bean 的初始化函数。 -func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { - if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.init = fn - return d - } - panic(errors.New("init should be func(bean) or func(bean)error")) -} - -// Destroy 设置 bean 的销毁函数。 -func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { - if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.destroy = fn - return d - } - panic(errors.New("destroy should be func(bean) or func(bean)error")) -} - -// Export 设置 bean 的导出接口。 -func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { - err := d.export(exports...) - if err != nil { - panic(err) - } - return d -} - -func (d *BeanDefinition) export(exports ...interface{}) error { - for _, o := range exports { - var typ reflect.Type - if t, ok := o.(reflect.Type); ok { - typ = t - } else { // 处理 (*error)(nil) 这种导出形式 - typ = gsutil.Indirect(reflect.TypeOf(o)) - } - if typ.Kind() != reflect.Interface { - return errors.New("only interface type can be exported") - } - exported := false - for _, export := range d.exports { - if typ == export { - exported = true - break - } - } - if exported { - continue - } - d.exports = append(d.exports, typ) - } - return nil -} +// // BeanDefinition bean 元数据。 +// type BeanDefinition struct { +// +// // 原始类型的全限定名 +// typeName string +// +// v reflect.Value // 值 +// t reflect.Type // 类型 +// f *gsarg.Callable // 构造函数 +// +// file string // 注册点所在文件 +// line int // 注册点所在行数 +// +// name string // 名称 +// status beanStatus // 状态 +// primary bool // 是否为主版本 +// method bool // 是否为成员方法 +// cond gscond.Condition // 判断条件 +// order float32 // 收集时的顺序 +// init interface{} // 初始化函数 +// destroy interface{} // 销毁函数 +// depends []gsutil.BeanSelector // 间接依赖项 +// exports []reflect.Type // 导出的接口 +// } +// +// // Type 返回 bean 的类型。 +// func (d *BeanDefinition) Type() reflect.Type { +// return d.t +// } +// +// // Value 返回 bean 的值。 +// func (d *BeanDefinition) Value() reflect.Value { +// return d.v +// } +// +// // Interface 返回 bean 的真实值。 +// func (d *BeanDefinition) Interface() interface{} { +// return d.v.Interface() +// } +// +// // ID 返回 bean 的 ID 。 +// func (d *BeanDefinition) ID() string { +// return d.typeName + ":" + d.name +// } +// +// // BeanName 返回 bean 的名称。 +// func (d *BeanDefinition) BeanName() string { +// return d.name +// } +// +// // TypeName 返回 bean 的原始类型的全限定名。 +// func (d *BeanDefinition) TypeName() string { +// return d.typeName +// } +// +// // Created 返回是否已创建。 +// func (d *BeanDefinition) Created() bool { +// return d.status >= Created +// } +// +// // Wired 返回 bean 是否已经注入。 +// func (d *BeanDefinition) Wired() bool { +// return d.status == Wired +// } +// +// // FileLine 返回 bean 的注册点。 +// func (d *BeanDefinition) FileLine() string { +// return fmt.Sprintf("%s:%d", d.file, d.line) +// } +// +// // getClass 返回 bean 的类型描述。 +// func (d *BeanDefinition) getClass() string { +// if d.f == nil { +// return "object bean" +// } +// return "constructor bean" +// } +// +// func (d *BeanDefinition) String() string { +// return fmt.Sprintf("%s name:%q %s", d.getClass(), d.name, d.FileLine()) +// } +// +// // Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 +// func (d *BeanDefinition) Match(typeName string, beanName string) bool { +// +// typeIsSame := false +// if typeName == "" || d.typeName == typeName { +// typeIsSame = true +// } +// +// nameIsSame := false +// if beanName == "" || d.name == beanName { +// nameIsSame = true +// } +// +// return typeIsSame && nameIsSame +// } +// +// // Name 设置 bean 的名称。 +// func (d *BeanDefinition) Name(name string) *BeanDefinition { +// d.name = name +// return d +// } +// +// // On 设置 bean 的 Condition。 +// func (d *BeanDefinition) On(cond gscond.Condition) *BeanDefinition { +// d.cond = cond +// return d +// } +// +// // Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 +// func (d *BeanDefinition) Order(order float32) *BeanDefinition { +// d.order = order +// return d +// } +// +// // DependsOn 设置 bean 的间接依赖项。 +// func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { +// d.depends = append(d.depends, selectors...) +// return d +// } +// +// // Primary 设置 bean 为主版本。 +// func (d *BeanDefinition) Primary() *BeanDefinition { +// d.primary = true +// return d +// } +// +// // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 +// // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 +// func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { +// if !gsutil.IsFuncType(fnType) { +// return false +// } +// if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { +// return false +// } +// return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) +// } +// +// // Init 设置 bean 的初始化函数。 +// func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { +// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { +// d.init = fn +// return d +// } +// panic(errors.New("init should be func(bean) or func(bean)error")) +// } +// +// // Destroy 设置 bean 的销毁函数。 +// func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { +// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { +// d.destroy = fn +// return d +// } +// panic(errors.New("destroy should be func(bean) or func(bean)error")) +// } +// +// // Export 设置 bean 的导出接口。 +// func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { +// err := d.export(exports...) +// if err != nil { +// panic(err) +// } +// return d +// } +// +// func (d *BeanDefinition) export(exports ...interface{}) error { +// for _, o := range exports { +// var typ reflect.Type +// if t, ok := o.(reflect.Type); ok { +// typ = t +// } else { // 处理 (*error)(nil) 这种导出形式 +// typ = gsutil.Indirect(reflect.TypeOf(o)) +// } +// if typ.Kind() != reflect.Interface { +// return errors.New("only interface type can be exported") +// } +// exported := false +// for _, export := range d.exports { +// if typ == export { +// exported = true +// break +// } +// } +// if exported { +// continue +// } +// d.exports = append(d.exports, typ) +// } +// return nil +// } +// +// // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +// func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { +// +// var v reflect.Value +// var fromValue bool +// var method bool +// var name string +// +// switch i := objOrCtor.(type) { +// case reflect.Value: +// fromValue = true +// v = i +// default: +// v = reflect.ValueOf(i) +// } +// +// t := v.Type() +// if !gsutil.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// if !v.IsValid() || v.IsNil() { +// panic(errors.New("bean can't be nil")) +// } +// +// const skip = 2 +// var f *gsarg.Callable +// _, file, line, _ := runtime.Caller(skip) +// +// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 +// if !fromValue && t.Kind() == reflect.Func { +// +// if !gsutil.IsConstructor(t) { +// t1 := "func(...)bean" +// t2 := "func(...)(bean, error)" +// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) +// } +// +// var err error +// f, err = gsarg.Bind(objOrCtor, ctorArgs, skip) +// if err != nil { +// panic(err) +// } +// +// out0 := t.Out(0) +// v = reflect.New(out0) +// if gsutil.IsBeanType(out0) { +// v = v.Elem() +// } +// +// t = v.Type() +// if !gsutil.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 +// fnPtr := reflect.ValueOf(objOrCtor).Pointer() +// fnInfo := runtime.FuncForPC(fnPtr) +// funcName := fnInfo.Name() +// name = funcName[strings.LastIndex(funcName, "/")+1:] +// name = name[strings.Index(name, ".")+1:] +// if name[0] == '(' { +// name = name[strings.Index(name, ".")+1:] +// } +// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 +// } +// +// if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { +// panic(errors.New("bean should be *val but not *ref")) +// } +// +// // Type.String() 一般返回 *pkg.Type 形式的字符串, +// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 +// if name == "" { +// s := strings.Split(t.String(), ".") +// name = strings.TrimPrefix(s[len(s)-1], "*") +// } +// +// return &gsbean.BeanDefinition{ +// t: t, +// v: v, +// f: f, +// name: name, +// typeName: gsutil.TypeName(t), +// status: gsbean.Default, +// method: method, +// file: file, +// line: line, +// } +// } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *BeanDefinition { - - var v reflect.Value - var fromValue bool - var method bool - var name string - - switch i := objOrCtor.(type) { - case reflect.Value: - fromValue = true - v = i - default: - v = reflect.ValueOf(i) - } - - t := v.Type() - if !gsutil.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - if !v.IsValid() || v.IsNil() { - panic(errors.New("bean can't be nil")) - } - - const skip = 2 - var f *arg.Callable - _, file, line, _ := runtime.Caller(skip) - - // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 - if !fromValue && t.Kind() == reflect.Func { - - if !gsutil.IsConstructor(t) { - t1 := "func(...)bean" - t2 := "func(...)(bean, error)" - panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) - } - - var err error - f, err = arg.Bind(objOrCtor, ctorArgs, skip) - if err != nil { - panic(err) - } - - out0 := t.Out(0) - v = reflect.New(out0) - if gsutil.IsBeanType(out0) { - v = v.Elem() - } - - t = v.Type() - if !gsutil.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 - fnPtr := reflect.ValueOf(objOrCtor).Pointer() - fnInfo := runtime.FuncForPC(fnPtr) - funcName := fnInfo.Name() - name = funcName[strings.LastIndex(funcName, "/")+1:] - name = name[strings.Index(name, ".")+1:] - if name[0] == '(' { - name = name[strings.Index(name, ".")+1:] - } - method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 - } - - if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { - panic(errors.New("bean should be *val but not *ref")) - } - - // Type.String() 一般返回 *pkg.Type 形式的字符串, - // 我们只取最后的类型名,如有需要请自定义 bean 名称。 - if name == "" { - s := strings.Split(t.String(), ".") - name = strings.TrimPrefix(s[len(s)-1], "*") - } - - return &BeanDefinition{ - t: t, - v: v, - f: f, - name: name, - typeName: gsutil.TypeName(t), - status: Default, - method: method, - file: file, - line: line, - } +func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { + return gsbean.NewBean(objOrCtor, ctorArgs...) } diff --git a/gs/gs_bean_test.go b/gs/gs_bean_test.go index 9090e250..c1172125 100644 --- a/gs/gs_bean_test.go +++ b/gs/gs_bean_test.go @@ -24,7 +24,8 @@ import ( "testing" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsbean" "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" @@ -32,7 +33,7 @@ import ( ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 -func newBean(objOrCtor interface{}, ctorArgs ...arg.Arg) *gs.BeanDefinition { +func newBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { return gs.NewBean(objOrCtor, ctorArgs...) } @@ -108,7 +109,7 @@ func TestIsFuncBeanType(t *testing.T) { func TestBeanDefinition_Match(t *testing.T) { data := []struct { - bd *gs.BeanDefinition + bd *gsbean.BeanDefinition typeName string beanName string expect bool @@ -183,7 +184,7 @@ func TestObjectBean(t *testing.T) { t.Run("check name && typename", func(t *testing.T) { - data := map[*gs.BeanDefinition]struct { + data := map[*gsbean.BeanDefinition]struct { name string typeName string }{ diff --git a/gs/gs_context.go b/gs/gs_context.go index 993c602d..bb72d662 100644 --- a/gs/gs_context.go +++ b/gs/gs_context.go @@ -21,7 +21,7 @@ import ( "reflect" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsutil" ) @@ -97,7 +97,7 @@ func (c *container) Get(i interface{}, selectors ...gsutil.BeanSelector) error { // Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 // 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 // 种方式,该函数执行完后都会返回 bean 对象的真实值。 -func (c *container) Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) { +func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) { stack := newWiringStack() @@ -115,7 +115,7 @@ func (c *container) Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{ return b.Interface(), nil } -func (c *container) Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) { +func (c *container) Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) { if !gsutil.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") @@ -129,7 +129,7 @@ func (c *container) Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, erro // } // }() - r, err := arg.Bind(fn, args, 1) + r, err := gsarg.Bind(fn, args, 1) if err != nil { return nil, err } diff --git a/gs/gs_test.go b/gs/gs_test.go index 5625298c..56a7856e 100755 --- a/gs/gs_test.go +++ b/gs/gs_test.go @@ -29,8 +29,8 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/arg" - "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gscond" "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" @@ -1246,7 +1246,7 @@ func TestOptionConstructorArg(t *testing.T) { t.Run("option withClassName", func(t *testing.T) { c := gs.New() c.Property("president", "CaiYuanPei") - c.Provide(NewClassRoom, arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) + c.Provide(NewClassRoom, gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) @@ -1263,7 +1263,7 @@ func TestOptionConstructorArg(t *testing.T) { c := gs.New() c.Property("class_name", "二年级03班") c.Property("president", "CaiYuanPei") - c.Provide(NewClassRoom, arg.Option(withStudents)) + c.Provide(NewClassRoom, gsarg.Option(withStudents)) c.Object(new(Student)).Name("Student1") c.Object(new(Student)).Name("Student2") err := runTest(c, func(p gs.Context) { @@ -1283,11 +1283,11 @@ func TestOptionConstructorArg(t *testing.T) { c.Property("class_name", "二年级06班") c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, - arg.Option(withStudents), - arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), - arg.Option(withBuilder, arg.Provide(func(param string) *ClassBuilder { + gsarg.Option(withStudents), + gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), + gsarg.Option(withBuilder, gsarg.Provide(func(param string) *ClassBuilder { return &ClassBuilder{param: param} - }, arg.Value("1"))), + }, gsarg.Value("1"))), ) c.Object(&Student{}).Name("Student1") c.Object(&Student{}).Name("Student2") @@ -1373,7 +1373,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { t.Run("method bean condition", func(t *testing.T) { c := gs.New() c.Property("server.version", "1.0.0") - parent := c.Object(new(Server)).On(cond.Not(cond.OK())) + parent := c.Object(new(Server)).On(gscond.Not(gscond.OK())) bd := c.Provide((*Server).Consumer, parent) err := runTest(c, func(p gs.Context) { @@ -1715,7 +1715,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewNilVarObj, arg.Nil()) + c.Provide(NewNilVarObj, gsarg.Nil()) err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) @@ -1731,7 +1731,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewVarObj, "${var.obj}", arg.Option(withVar, "v1")) + c.Provide(NewVarObj, "${var.obj}", gsarg.Option(withVar, "v1")) err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) @@ -1748,7 +1748,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewVarObj, arg.Value("description"), arg.Option(withVar, "v1", "v2")) + c.Provide(NewVarObj, gsarg.Value("description"), gsarg.Option(withVar, "v1", "v2")) err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) @@ -1765,7 +1765,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c := gs.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) - c.Provide(NewVarInterfaceObj, arg.Option(withVarInterface, "v1")) + c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1")) err := runTest(c, func(p gs.Context) { var obj *VarInterfaceObj err := p.Get(&obj) @@ -1779,7 +1779,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c := gs.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) - c.Provide(NewVarInterfaceObj, arg.Option(withVarInterface, "v1", "v2")) + c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1", "v2")) err := runTest(c, func(p gs.Context) { var obj *VarInterfaceObj err := p.Get(&obj) @@ -2198,7 +2198,7 @@ func TestApplicationContext_CreateBean(t *testing.T) { c := gs.New() c.Object(&ObjFactory{}) err := runTest(c, func(p gs.Context) { - b, err := p.Wire((*ObjFactory).NewObj, arg.R1("${i:=5}")) + b, err := p.Wire((*ObjFactory).NewObj, gsarg.R1("${i:=5}")) fmt.Println(b, err) }) assert.Nil(t, err) @@ -2210,12 +2210,12 @@ func TestDefaultSpringContext(t *testing.T) { c := gs.New() - c.Object(&BeanZero{5}).On(cond. + c.Object(&BeanZero{5}).On(gscond. OnProfile("test"). And(). OnMissingBean("null"). And(). - On(cond.OK()), + On(gscond.OK()), ) err := runTest(c, func(p gs.Context) { @@ -2229,7 +2229,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("bean:test_ctx:test", func(t *testing.T) { c := gs.New() c.Property("spring.profiles.active", "test") - c.Object(&BeanZero{5}).On(cond.OnProfile("test")) + c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) @@ -2241,7 +2241,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("bean:test_ctx:stable", func(t *testing.T) { c := gs.New() c.Property("spring.profiles.active", "stable") - c.Object(&BeanZero{5}).On(cond.OnProfile("test")) + c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) @@ -2255,10 +2255,10 @@ func TestDefaultSpringContext(t *testing.T) { c := gs.New() c.Property("president", "CaiYuanPei") c.Property("class_floor", 2) - c.Provide(NewClassRoom, arg.Option(withClassName, + c.Provide(NewClassRoom, gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", - ).On(cond.OnProperty("class_name_enable"))) + ).On(gscond.OnProperty("class_name_enable"))) err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) @@ -2272,11 +2272,11 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("option withClassName Apply", func(t *testing.T) { - onProperty := cond.OnProperty("class_name_enable") + onProperty := gscond.OnProperty("class_name_enable") c := gs.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, - arg.Option(withClassName, + gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", ).On(onProperty), @@ -2297,7 +2297,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.ID()).On(cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.ID()).On(gscond.OnProperty("consumer.enable")) err := runTest(c, func(p gs.Context) { var s *Server @@ -2334,13 +2334,13 @@ func TestDefaultSpringContext(t *testing.T) { func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { c := gs.New() - c1 := cond.OnProperty("null", cond.MatchIfMissing()).Or().OnProfile("test") + c1 := gscond.OnProperty("null", gscond.MatchIfMissing()).Or().OnProfile("test") - c.Object(&BeanZero{5}).On(cond.On(c1).And().OnMissingBean("null")) - c.Object(new(BeanOne)).On(cond.On(c1).And().OnMissingBean("null")) + c.Object(&BeanZero{5}).On(gscond.On(c1).And().OnMissingBean("null")) + c.Object(new(BeanOne)).On(gscond.On(c1).And().OnMissingBean("null")) - c.Object(new(BeanTwo)).On(cond.OnBean("BeanOne")) - c.Object(new(BeanTwo)).Name("another_two").On(cond.OnBean("Null")) + c.Object(new(BeanTwo)).On(gscond.OnBean("BeanOne")) + c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnBean("Null")) err := runTest(c, func(p gs.Context) { @@ -2359,8 +2359,8 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { c := gs.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) - c.Object(new(BeanTwo)).On(cond.OnMissingBean("BeanOne")) - c.Object(new(BeanTwo)).Name("another_two").On(cond.OnMissingBean("Null")) + c.Object(new(BeanTwo)).On(gscond.OnMissingBean("BeanOne")) + c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnMissingBean("Null")) err := runTest(c, func(p gs.Context) { var two *BeanTwo @@ -2612,7 +2612,7 @@ func TestMapCollection(t *testing.T) { c := gs.New() c.Object(&mapValue{"a"}).Name("a").Order(1) c.Object(&mapValue{"b"}).Name("b").Order(2) - c.Object(&mapValue{"c"}).Name("c").On(cond.Not(cond.OK())) + c.Object(&mapValue{"c"}).Name("c").On(gscond.Not(gscond.OK())) err := runTest(c, func(p gs.Context) { var vSlice []*mapValue diff --git a/gs/arg/arg.go b/gs/gsarg/arg.go similarity index 97% rename from gs/arg/arg.go rename to gs/gsarg/arg.go index 896945eb..4762d209 100644 --- a/gs/arg/arg.go +++ b/gs/gsarg/arg.go @@ -17,7 +17,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=arg -source=arg.go -destination=arg_mock.go // Package arg 用于实现函数参数绑定。 -package arg +package gsarg import ( "errors" @@ -25,7 +25,7 @@ import ( "reflect" "runtime" - "github.com/go-spring/spring-core/gs/cond" + "github.com/go-spring/spring-core/gs/gscond" "github.com/go-spring/spring-core/gs/gsutil" "github.com/go-spring/spring-core/util" ) @@ -34,7 +34,7 @@ import ( type Context interface { // Matches returns true when the Condition returns true, // and returns false when the Condition returns false. - Matches(c cond.Condition) (bool, error) + Matches(c gscond.Condition) (bool, error) // Bind binds properties value by the "value" tag. Bind(v reflect.Value, tag string) error // Wire wires dependent beans by the "autowire" tag. @@ -275,7 +275,7 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) // optionArg Option 函数的参数绑定。 type optionArg struct { r *Callable - c cond.Condition + c gscond.Condition } // Provide 为 Option 方法绑定运行时参数。 @@ -302,8 +302,8 @@ func Option(fn interface{}, args ...Arg) *optionArg { return &optionArg{r: r} } -// On 设置一个 cond.Condition 对象。 -func (arg *optionArg) On(c cond.Condition) *optionArg { +// On 设置一个 gscond.Condition 对象。 +func (arg *optionArg) On(c gscond.Condition) *optionArg { arg.c = c return arg } diff --git a/gs/arg/arg_mock.go b/gs/gsarg/arg_mock.go similarity index 97% rename from gs/arg/arg_mock.go rename to gs/gsarg/arg_mock.go index a6df684b..afe1936c 100644 --- a/gs/arg/arg_mock.go +++ b/gs/gsarg/arg_mock.go @@ -7,12 +7,12 @@ // // Package arg is a generated GoMock package. -package arg +package gsarg import ( reflect "reflect" - cond "github.com/go-spring/spring-core/gs/cond" + cond "github.com/go-spring/spring-core/gs/gscond" gomock "go.uber.org/mock/gomock" ) diff --git a/gs/arg/arg_test.go b/gs/gsarg/arg_test.go similarity index 86% rename from gs/arg/arg_test.go rename to gs/gsarg/arg_test.go index 0e5d149f..0d84672d 100644 --- a/gs/arg/arg_test.go +++ b/gs/gsarg/arg_test.go @@ -14,13 +14,13 @@ * limitations under the License. */ -package arg_test +package gsarg_test import ( "reflect" "testing" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -30,9 +30,9 @@ func TestBind(t *testing.T) { t.Run("zero argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := arg.NewMockContext(ctrl) + ctx := gsarg.NewMockContext(ctrl) fn := func() {} - c, err := arg.Bind(fn, []arg.Arg{}, 1) + c, err := gsarg.Bind(fn, []gsarg.Arg{}, 1) if err != nil { t.Fatal(err) } @@ -46,13 +46,13 @@ func TestBind(t *testing.T) { t.Run("one value argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := arg.NewMockContext(ctrl) + ctx := gsarg.NewMockContext(ctrl) expectInt := 0 fn := func(i int) { expectInt = i } - c, err := arg.Bind(fn, []arg.Arg{ - arg.Value(3), + c, err := gsarg.Bind(fn, []gsarg.Arg{ + gsarg.Value(3), }, 1) if err != nil { t.Fatal(err) @@ -68,7 +68,7 @@ func TestBind(t *testing.T) { t.Run("one ctx value argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := arg.NewMockContext(ctrl) + ctx := gsarg.NewMockContext(ctrl) ctx.EXPECT().Bind(gomock.Any(), "${a.b.c}").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).SetInt(3) return nil @@ -77,7 +77,7 @@ func TestBind(t *testing.T) { fn := func(i int) { expectInt = i } - c, err := arg.Bind(fn, []arg.Arg{ + c, err := gsarg.Bind(fn, []gsarg.Arg{ "${a.b.c}", }, 1) if err != nil { @@ -97,7 +97,7 @@ func TestBind(t *testing.T) { } ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := arg.NewMockContext(ctrl) + ctx := gsarg.NewMockContext(ctrl) ctx.EXPECT().Wire(gomock.Any(), "a").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).Set(reflect.ValueOf(&st{3})) return nil @@ -106,7 +106,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := arg.Bind(fn, []arg.Arg{ + c, err := gsarg.Bind(fn, []gsarg.Arg{ "a", }, 1) if err != nil { @@ -126,7 +126,7 @@ func TestBind(t *testing.T) { } ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := arg.NewMockContext(ctrl) + ctx := gsarg.NewMockContext(ctrl) ctx.EXPECT().Wire(gomock.Any(), "").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).Set(reflect.ValueOf(&st{3})) return nil @@ -135,7 +135,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := arg.Bind(fn, []arg.Arg{}, 1) + c, err := gsarg.Bind(fn, []gsarg.Arg{}, 1) if err != nil { t.Fatal(err) } diff --git a/gs/gsbean/gsbean.go b/gs/gsbean/gsbean.go new file mode 100644 index 00000000..f1707e2d --- /dev/null +++ b/gs/gsbean/gsbean.go @@ -0,0 +1,340 @@ +package gsbean + +import ( + "errors" + "fmt" + "reflect" + "runtime" + "strings" + + "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gscond" + "github.com/go-spring/spring-core/gs/gsutil" +) + +type BeanStatus int8 + +const ( + Deleted = BeanStatus(-1) // 已删除 + Default = BeanStatus(iota) // 未处理 + Resolving // 正在决议 + Resolved // 已决议 + Creating // 正在创建 + Created // 已创建 + Wired // 注入完成 +) + +func GetStatusString(status BeanStatus) string { + switch status { + case Deleted: + return "Deleted" + case Default: + return "Default" + case Resolving: + return "Resolving" + case Resolved: + return "Resolved" + case Creating: + return "Creating" + case Created: + return "Created" + case Wired: + return "Wired" + default: + return "" + } +} + +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + V reflect.Value // 值 + T reflect.Type // 类型 + F *gsarg.Callable // 构造函数 + + file string // 注册点所在文件 + line int // 注册点所在行数 + + name string // 名称 + typeName string // 原始类型的全限定名 + status BeanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond gscond.Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []gsutil.BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 +} + +func (d *BeanDefinition) GetName() string { return d.name } +func (d *BeanDefinition) GetTypeName() string { return d.typeName } +func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } +func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } +func (d *BeanDefinition) IsPrimary() bool { return d.primary } +func (d *BeanDefinition) IsMethod() bool { return d.method } +func (d *BeanDefinition) GetCond() gscond.Condition { return d.cond } +func (d *BeanDefinition) GetOrder() float32 { return d.order } +func (d *BeanDefinition) GetInit() interface{} { return d.init } +func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } +func (d *BeanDefinition) GetDepends() []gsutil.BeanSelector { return d.depends } +func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } + +// Type 返回 bean 的类型。 +func (d *BeanDefinition) Type() reflect.Type { + return d.T +} + +// Value 返回 bean 的值。 +func (d *BeanDefinition) Value() reflect.Value { + return d.V +} + +// Interface 返回 bean 的真实值。 +func (d *BeanDefinition) Interface() interface{} { + return d.V.Interface() +} + +// ID 返回 bean 的 ID 。 +func (d *BeanDefinition) ID() string { + return d.typeName + ":" + d.name +} + +// BeanName 返回 bean 的名称。 +func (d *BeanDefinition) BeanName() string { + return d.name +} + +// TypeName 返回 bean 的原始类型的全限定名。 +func (d *BeanDefinition) TypeName() string { + return d.typeName +} + +// Created 返回是否已创建。 +func (d *BeanDefinition) Created() bool { + return d.status >= Created +} + +// Wired 返回 bean 是否已经注入。 +func (d *BeanDefinition) Wired() bool { + return d.status == Wired +} + +// FileLine 返回 bean 的注册点。 +func (d *BeanDefinition) FileLine() string { + return fmt.Sprintf("%s:%d", d.file, d.line) +} + +// GetClass 返回 bean 的类型描述。 +func (d *BeanDefinition) GetClass() string { + if d.F == nil { + return "object bean" + } + return "constructor bean" +} + +func (d *BeanDefinition) String() string { + return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) +} + +// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 +func (d *BeanDefinition) Match(typeName string, beanName string) bool { + + typeIsSame := false + if typeName == "" || d.typeName == typeName { + typeIsSame = true + } + + nameIsSame := false + if beanName == "" || d.name == beanName { + nameIsSame = true + } + + return typeIsSame && nameIsSame +} + +// Name 设置 bean 的名称。 +func (d *BeanDefinition) Name(name string) *BeanDefinition { + d.name = name + return d +} + +// On 设置 bean 的 Condition。 +func (d *BeanDefinition) On(cond gscond.Condition) *BeanDefinition { + d.cond = cond + return d +} + +// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 +func (d *BeanDefinition) Order(order float32) *BeanDefinition { + d.order = order + return d +} + +// DependsOn 设置 bean 的间接依赖项。 +func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { + d.depends = append(d.depends, selectors...) + return d +} + +// Primary 设置 bean 为主版本。 +func (d *BeanDefinition) Primary() *BeanDefinition { + d.primary = true + return d +} + +// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 +// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 +func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { + if !gsutil.IsFuncType(fnType) { + return false + } + if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { + return false + } + return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) +} + +// Init 设置 bean 的初始化函数。 +func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { + if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { + d.init = fn + return d + } + panic(errors.New("init should be func(bean) or func(bean)error")) +} + +// Destroy 设置 bean 的销毁函数。 +func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { + if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { + d.destroy = fn + return d + } + panic(errors.New("destroy should be func(bean) or func(bean)error")) +} + +// Export 设置 bean 的导出接口。 +func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { + err := d.export(exports...) + if err != nil { + panic(err) + } + return d +} + +func (d *BeanDefinition) export(exports ...interface{}) error { + for _, o := range exports { + var typ reflect.Type + if t, ok := o.(reflect.Type); ok { + typ = t + } else { // 处理 (*error)(nil) 这种导出形式 + typ = gsutil.Indirect(reflect.TypeOf(o)) + } + if typ.Kind() != reflect.Interface { + return errors.New("only interface type can be exported") + } + exported := false + for _, export := range d.exports { + if typ == export { + exported = true + break + } + } + if exported { + continue + } + d.exports = append(d.exports, typ) + } + return nil +} + +// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { + + var v reflect.Value + var fromValue bool + var method bool + var name string + + switch i := objOrCtor.(type) { + case reflect.Value: + fromValue = true + v = i + default: + v = reflect.ValueOf(i) + } + + t := v.Type() + if !gsutil.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + if !v.IsValid() || v.IsNil() { + panic(errors.New("bean can't be nil")) + } + + const skip = 2 + var f *gsarg.Callable + _, file, line, _ := runtime.Caller(skip) + + // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 + if !fromValue && t.Kind() == reflect.Func { + + if !gsutil.IsConstructor(t) { + t1 := "func(...)bean" + t2 := "func(...)(bean, error)" + panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) + } + + var err error + f, err = gsarg.Bind(objOrCtor, ctorArgs, skip) + if err != nil { + panic(err) + } + + out0 := t.Out(0) + v = reflect.New(out0) + if gsutil.IsBeanType(out0) { + v = v.Elem() + } + + t = v.Type() + if !gsutil.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 + fnPtr := reflect.ValueOf(objOrCtor).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + funcName := fnInfo.Name() + name = funcName[strings.LastIndex(funcName, "/")+1:] + name = name[strings.Index(name, ".")+1:] + if name[0] == '(' { + name = name[strings.Index(name, ".")+1:] + } + method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 + } + + if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { + panic(errors.New("bean should be *val but not *ref")) + } + + // Type.String() 一般返回 *pkg.Type 形式的字符串, + // 我们只取最后的类型名,如有需要请自定义 bean 名称。 + if name == "" { + s := strings.Split(t.String(), ".") + name = strings.TrimPrefix(s[len(s)-1], "*") + } + + return &BeanDefinition{ + T: t, + V: v, + F: f, + name: name, + typeName: gsutil.TypeName(t), + status: Default, + method: method, + file: file, + line: line, + } +} diff --git a/gs/cond/cond.go b/gs/gscond/cond.go similarity index 99% rename from gs/cond/cond.go rename to gs/gscond/cond.go index de270181..b379d9bb 100755 --- a/gs/cond/cond.go +++ b/gs/gscond/cond.go @@ -17,7 +17,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=cond -source=cond.go -destination=cond_mock.go // Package cond provides many conditions used when registering bean. -package cond +package gscond import ( "errors" diff --git a/gs/cond/cond_mock.go b/gs/gscond/cond_mock.go similarity index 99% rename from gs/cond/cond_mock.go rename to gs/gscond/cond_mock.go index b33ed118..e5fbed33 100644 --- a/gs/cond/cond_mock.go +++ b/gs/gscond/cond_mock.go @@ -7,7 +7,7 @@ // // Package cond is a generated GoMock package. -package cond +package gscond import ( reflect "reflect" diff --git a/gs/cond/cond_test.go b/gs/gscond/cond_test.go similarity index 99% rename from gs/cond/cond_test.go rename to gs/gscond/cond_test.go index 513b1e67..27e25dc9 100644 --- a/gs/cond/cond_test.go +++ b/gs/gscond/cond_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package cond_test +package gscond_test // func TestOK(t *testing.T) { // ctrl := gomock.NewController(t) diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index efe8b920..61862e9e 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -21,7 +21,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/arg" + "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsutil" ) @@ -69,11 +69,11 @@ func Get(i interface{}, selectors ...gsutil.BeanSelector) error { } // Wire 注入对象 -func Wire(objOrCtor interface{}, ctorArgs ...arg.Arg) (interface{}, error) { +func Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) { return ctx.Wire(objOrCtor, ctorArgs...) } // Invoke 调用函数 -func Invoke(fn interface{}, args ...arg.Arg) ([]interface{}, error) { +func Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) { return ctx.Invoke(fn, args...) } diff --git a/atomic/value.go b/gs/syslog/syslog.go similarity index 50% rename from atomic/value.go rename to gs/syslog/syslog.go index 2978e400..9247bbff 100644 --- a/atomic/value.go +++ b/gs/syslog/syslog.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,28 +14,4 @@ * limitations under the License. */ -package atomic - -import ( - "sync/atomic" -) - -type MarshalValue func(interface{}) ([]byte, error) - -// A Value provides an atomic load and store of a consistently typed value. -type Value struct { - _ nocopy - atomic.Value - - marshalJSON MarshalValue -} - -// SetMarshalJSON sets the JSON encoding handler for x. -func (x *Value) SetMarshalJSON(fn MarshalValue) { - x.marshalJSON = fn -} - -// MarshalJSON returns the JSON encoding of x. -func (x *Value) MarshalJSON() ([]byte, error) { - return x.marshalJSON(x.Load()) -} +package syslog From 02d5c4fa8d998ec4f7e04f18961a1e5b4519fe86 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 29 Dec 2024 20:48:06 +0800 Subject: [PATCH 10/75] code refactor --- gs/boot.go | 18 +- gs/{ => gsapp}/app.go | 38 +- gs/{ => gsapp}/app_bootstrap.go | 13 +- gs/{ => gsioc}/gs.go | 16 +- gs/{ => gsioc}/gs_bean.go | 2 +- gs/{ => gsioc}/gs_bean_test.go | 6 +- gs/{ => gsioc}/gs_context.go | 2 +- gs/{ => gsioc}/gs_dynamic_test.go | 8 +- gs/{ => gsioc}/gs_test.go | 428 +++++++++++------------ gs/gstest/gstest.go | 3 +- gs/{gs_version.go => version/version.go} | 2 +- 11 files changed, 270 insertions(+), 266 deletions(-) rename gs/{ => gsapp}/app.go (86%) rename gs/{ => gsapp}/app_bootstrap.go (87%) rename gs/{ => gsioc}/gs.go (98%) rename gs/{ => gsioc}/gs_bean.go (99%) rename gs/{ => gsioc}/gs_bean_test.go (98%) rename gs/{ => gsioc}/gs_context.go (99%) rename gs/{ => gsioc}/gs_dynamic_test.go (97%) rename gs/{ => gsioc}/gs_test.go (90%) rename gs/{gs_version.go => version/version.go} (97%) diff --git a/gs/boot.go b/gs/boot.go index 820079dc..d3c105a5 100644 --- a/gs/boot.go +++ b/gs/boot.go @@ -19,20 +19,22 @@ package gs import ( "reflect" + "github.com/go-spring/spring-core/gs/gsapp" "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/gsioc" ) -var app = NewApp() +var app = gsapp.NewApp() // Start 启动程序。 -func Start() (Context, error) { - return app.start() +func Start() (gsioc.Context, error) { + return app.Start() } // Stop 停止程序。 func Stop() { - app.stop() + app.Stop() } // Run 启动程序。 @@ -46,7 +48,7 @@ func ShutDown(msg ...string) { } // Bootstrap 参考 App.Bootstrap 的解释。 -func Bootstrap() *Bootstrapper { +func Bootstrap() *gsapp.Bootstrapper { return app.Bootstrap() } @@ -62,15 +64,15 @@ func Property(key string, value interface{}) { // Accept 参考 Container.Accept 的解释。 func Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { - return app.c.Accept(b) + return app.Accept(b) } // Object 参考 Container.Object 的解释。 func Object(i interface{}) *gsbean.BeanDefinition { - return app.c.Accept(NewBean(reflect.ValueOf(i))) + return app.Accept(gsioc.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return app.c.Accept(NewBean(ctor, args...)) + return app.Accept(gsioc.NewBean(ctor, args...)) } diff --git a/gs/app.go b/gs/gsapp/app.go similarity index 86% rename from gs/app.go rename to gs/gsapp/app.go index 618a09cb..6b0a9937 100644 --- a/gs/app.go +++ b/gs/gsapp/app.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs +package gsapp import ( "context" @@ -27,6 +27,8 @@ import ( "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/gsioc" + "github.com/go-spring/spring-core/gs/version" ) // SpringBannerVisible 是否显示 banner。 @@ -34,12 +36,12 @@ const SpringBannerVisible = "spring.banner.visible" // AppRunner 命令行启动器接口 type AppRunner interface { - Run(ctx Context) + Run(ctx gsioc.Context) } // AppEvent 应用运行过程中的事件 type AppEvent interface { - OnAppStart(ctx Context) // 应用启动的事件 + OnAppStart(ctx gsioc.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -51,7 +53,7 @@ type tempApp struct { type App struct { *tempApp - c *container + c gsioc.Container b *Bootstrapper exitChan chan struct{} @@ -63,7 +65,7 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { return &App{ - c: New().(*container), + c: gsioc.New(), tempApp: &tempApp{}, exitChan: make(chan struct{}), } @@ -74,7 +76,7 @@ func (app *App) Banner(banner string) { app.banner = banner } -func (app *App) start() (Context, error) { +func (app *App) Start() (gsioc.Context, error) { app.Object(app) @@ -98,29 +100,29 @@ func (app *App) start() (Context, error) { // app.c.initProperties.Set(k, e.p.Get(k)) // } - if err := app.c.refresh(false); err != nil { + if err := app.c.Refresh(false); err != nil { return nil, err } // 执行命令行启动器 for _, r := range app.Runners { - r.Run(app.c) + r.Run(app.c.(gsioc.Context)) } // 通知应用启动事件 for _, event := range app.Events { - event.OnAppStart(app.c) + event.OnAppStart(app.c.(gsioc.Context)) } // 通知应用停止事件 - app.c.Go(func(ctx context.Context) { + app.c.(gsioc.Context).Go(func(ctx context.Context) { <-ctx.Done() for _, event := range app.Events { event.OnAppStop(context.Background()) } }) - return app.c, nil + return app.c.(gsioc.Context), nil } func (app *App) wait() { @@ -133,7 +135,7 @@ func (app *App) wait() { <-app.exitChan } -func (app *App) stop() { +func (app *App) Stop() { // if app.b != nil { // app.b.c.Close() @@ -143,12 +145,12 @@ func (app *App) stop() { } func (app *App) Run() error { - _, err := app.start() + _, err := app.Start() if err != nil { return err } app.wait() - app.stop() + app.Stop() return nil } @@ -195,13 +197,13 @@ func (app *App) printBanner(banner string) { } var padding []byte - if n := (maxLength - len(Version)) / 2; n > 0 { + if n := (maxLength - len(version.Version)) / 2; n > 0 { padding = make([]byte, n) for i := range padding { padding[i] = ' ' } } - fmt.Println(string(padding) + Version + "\n") + fmt.Println(string(padding) + version.Version + "\n") } // ShutDown 关闭执行器 @@ -239,10 +241,10 @@ func (app *App) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { // Object 参考 Container.Object 的解释。 func (app *App) Object(i interface{}) *gsbean.BeanDefinition { - return app.c.Accept(NewBean(reflect.ValueOf(i))) + return app.c.Accept(gsioc.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func (app *App) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return app.c.Accept(NewBean(ctor, args...)) + return app.c.Accept(gsioc.NewBean(ctor, args...)) } diff --git a/gs/app_bootstrap.go b/gs/gsapp/app_bootstrap.go similarity index 87% rename from gs/app_bootstrap.go rename to gs/gsapp/app_bootstrap.go index 29165ba6..396a34d5 100644 --- a/gs/app_bootstrap.go +++ b/gs/gsapp/app_bootstrap.go @@ -14,22 +14,23 @@ * limitations under the License. */ -package gs +package gsapp import ( "reflect" "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/gsioc" ) type Bootstrapper struct { - c *container + c gsioc.Container } func newBootstrap() *Bootstrapper { return &Bootstrapper{ - c: New().(*container), + c: gsioc.New(), } } @@ -45,12 +46,12 @@ func (b *Bootstrapper) Property(key string, value interface{}) { // Object 参考 Container.Object 的解释。 func (b *Bootstrapper) Object(i interface{}) *gsbean.BeanDefinition { - return b.c.Accept(NewBean(reflect.ValueOf(i))) + return b.c.Accept(gsioc.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func (b *Bootstrapper) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return b.c.Accept(NewBean(ctor, args...)) + return b.c.Accept(gsioc.NewBean(ctor, args...)) } func (b *Bootstrapper) start() error { @@ -66,5 +67,5 @@ func (b *Bootstrapper) start() error { // b.c.initProperties.Set(k, e.p.Get(k)) // } - return b.c.Refresh() + return b.c.Refresh(false) } diff --git a/gs/gs.go b/gs/gsioc/gs.go similarity index 98% rename from gs/gs.go rename to gs/gsioc/gs.go index 3e613f0c..038f8604 100755 --- a/gs/gs.go +++ b/gs/gsioc/gs.go @@ -14,9 +14,9 @@ * limitations under the License. */ -// Package gs 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App +// Package gsioc 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App // 以及全局 App 对象封装三个部分,可以应用于多种使用场景。 -package gs +package gsioc import ( "bytes" @@ -57,7 +57,9 @@ type Container interface { Property(key string, value interface{}) Object(i interface{}) *gsbean.BeanDefinition Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition - Refresh() error + Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition + OnProperty(key string, fn interface{}) + Refresh(autoClear bool) error Close() } @@ -310,12 +312,7 @@ func (c *container) clear() { c.tempContainer = nil } -// Refresh 刷新容器的内容,对 bean 进行有效性判断以及完成属性绑定和依赖注入。 -func (c *container) Refresh() error { - return c.refresh(true) -} - -func (c *container) refresh(autoClear bool) (err error) { +func (c *container) Refresh(autoClear bool) (err error) { if c.state != Unrefreshed { return errors.New("container already refreshed") @@ -1126,6 +1123,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, for _, b := range beans { ret.SetMapIndex(reflect.ValueOf(b.GetName()), b.Value()) } + default: } v.Set(ret) return nil diff --git a/gs/gs_bean.go b/gs/gsioc/gs_bean.go similarity index 99% rename from gs/gs_bean.go rename to gs/gsioc/gs_bean.go index ccf829eb..0a87cdc4 100755 --- a/gs/gs_bean.go +++ b/gs/gsioc/gs_bean.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs +package gsioc import ( "github.com/go-spring/spring-core/gs/gsarg" diff --git a/gs/gs_bean_test.go b/gs/gsioc/gs_bean_test.go similarity index 98% rename from gs/gs_bean_test.go rename to gs/gsioc/gs_bean_test.go index c1172125..724284ab 100644 --- a/gs/gs_bean_test.go +++ b/gs/gsioc/gs_bean_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_test +package gsioc_test import ( "fmt" @@ -23,9 +23,9 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/gsioc" "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" @@ -34,7 +34,7 @@ import ( // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 func newBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { - return gs.NewBean(objOrCtor, ctorArgs...) + return gsioc.NewBean(objOrCtor, ctorArgs...) } // func TestParseSingletonTag(t *testing.T) { diff --git a/gs/gs_context.go b/gs/gsioc/gs_context.go similarity index 99% rename from gs/gs_context.go rename to gs/gsioc/gs_context.go index bb72d662..7f11f12b 100644 --- a/gs/gs_context.go +++ b/gs/gsioc/gs_context.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs +package gsioc import ( "errors" diff --git a/gs/gs_dynamic_test.go b/gs/gsioc/gs_dynamic_test.go similarity index 97% rename from gs/gs_dynamic_test.go rename to gs/gsioc/gs_dynamic_test.go index 8cc545f5..678a0c04 100644 --- a/gs/gs_dynamic_test.go +++ b/gs/gsioc/gs_dynamic_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_test +package gsioc_test import ( "encoding/json" @@ -24,7 +24,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs" + "github.com/go-spring/spring-core/gs/gsioc" "github.com/stretchr/testify/assert" ) @@ -45,7 +45,7 @@ func TestDynamic(t *testing.T) { var cfg *DynamicConfig wrapper := new(DynamicConfigWrapper) - c := gs.New() + c := gsioc.New() c.Provide(func() *DynamicConfig { config := new(DynamicConfig) config.Int.OnValidate(func(v int64) error { @@ -72,7 +72,7 @@ func TestDynamic(t *testing.T) { return nil }) }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) { diff --git a/gs/gs_test.go b/gs/gsioc/gs_test.go similarity index 90% rename from gs/gs_test.go rename to gs/gsioc/gs_test.go index 56a7856e..f19b9805 100755 --- a/gs/gs_test.go +++ b/gs/gsioc/gs_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_test +package gsioc_test import ( "errors" @@ -28,9 +28,9 @@ import ( "time" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/gsarg" "github.com/go-spring/spring-core/gs/gscond" + "github.com/go-spring/spring-core/gs/gsioc" "github.com/go-spring/spring-core/gs/gsutil" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" @@ -40,13 +40,13 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c gs.Container, fn func(gs.Context)) error { +func runTest(c gsioc.Container, fn func(gsioc.Context)) error { type PandoraAware struct{} - c.Provide(func(p gs.Context) PandoraAware { + c.Provide(func(p gsioc.Context) PandoraAware { fn(p) return PandoraAware{} }) - return c.Refresh() + return c.Refresh(false) } func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { @@ -55,7 +55,7 @@ func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { // c.Object(func() {}).Init(func(f func()) { // c.Object(func() {}) // 不能在这里注册新的 Object // }) - // _ = c.Refresh() + // _ = c.Refresh(false) // }, "should call before Refresh") } @@ -65,7 +65,7 @@ func TestApplicationContext(t *testing.T) { // 自定义数据类型 t.Run("pkg1.SamePkg", func(t *testing.T) { - c := gs.New() + c := gsioc.New() e := pkg1.SamePkg{} // assert.Panic(t, func() { @@ -76,12 +76,12 @@ func TestApplicationContext(t *testing.T) { c.Object(&e).Name("i3") c.Object(&e).Name("i4") - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) t.Run("pkg2.SamePkg", func(t *testing.T) { - c := gs.New() + c := gsioc.New() e := pkg2.SamePkg{} // assert.Panic(t, func() { @@ -93,7 +93,7 @@ func TestApplicationContext(t *testing.T) { c.Object(&e).Name("i4") c.Object(&e).Name("i5") - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) } @@ -141,7 +141,7 @@ type TestObject struct { func TestApplicationContext_AutoWireBeans(t *testing.T) { - c := gs.New() + c := gsioc.New() obj := &TestObject{} c.Object(obj) @@ -149,7 +149,7 @@ func TestApplicationContext_AutoWireBeans(t *testing.T) { b := TestBincoreng{1} c.Object(&b).Name("struct_ptr").Export((*fmt.Stringer)(nil)) - err := runTest(c, func(p gs.Context) {}) + err := runTest(c, func(p gsioc.Context) {}) assert.Nil(t, err) assert.Equal(t, len(obj.MapTyType), 4) @@ -200,7 +200,7 @@ type Setting struct { } func TestApplicationContext_ValueTag(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("int", int(3)) c.Property("uint", uint(3)) @@ -220,7 +220,7 @@ func TestApplicationContext_ValueTag(t *testing.T) { c.Property("string_slice", []string{"1", "2"}) // c.Property("float_slice", []float64{1, 2}) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) fmt.Printf("%+v\n", setting) @@ -264,7 +264,7 @@ func (s *PrototypeBeanService) Service(name string) { } func TestApplicationContext_PrototypeBean(t *testing.T) { - c := gs.New() + c := gsioc.New() greetingService := &GreetingService{} c.Object(greetingService) @@ -275,7 +275,7 @@ func TestApplicationContext_PrototypeBean(t *testing.T) { f := &PrototypeBeanFactory{} c.Object(f) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) s.Service("Li Lei") @@ -324,7 +324,7 @@ type DbConfig struct { } func TestApplicationContext_TypeConverter(t *testing.T) { - c := gs.New() + c := gsioc.New() { p, _ := conf.Load("testdata/config/application.yaml") for _, key := range p.Keys() { @@ -346,7 +346,7 @@ func TestApplicationContext_TypeConverter(t *testing.T) { dbConfig := &DbConfig{} c.Object(dbConfig) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) assert.Equal(t, b.EnvType, ENV_TEST) @@ -373,10 +373,10 @@ type ProxyGrouper struct { } func TestApplicationContext_NestedBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(MyGrouper)).Export((*Grouper)(nil)) c.Object(new(ProxyGrouper)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) } @@ -390,11 +390,11 @@ type SamePkgHolder struct { } func TestApplicationContext_SameNameBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(SamePkgHolder)) c.Object(&pkg1.SamePkg{}).Export((*Pkg)(nil)) c.Object(&pkg2.SamePkg{}).Export((*Pkg)(nil)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) } @@ -418,17 +418,17 @@ type DiffPkgHolder struct { } func TestApplicationContext_DiffNameBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&DiffPkgOne{}).Name("same").Export((*Pkg)(nil)) c.Object(&DiffPkgTwo{}).Name("same").Export((*Pkg)(nil)) c.Object(new(DiffPkgHolder)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) } func TestApplicationContext_LoadProperties(t *testing.T) { - c := gs.New() + c := gsioc.New() p, _ := conf.Load("testdata/config/application.yaml") for _, key := range p.Keys() { @@ -440,7 +440,7 @@ func TestApplicationContext_LoadProperties(t *testing.T) { c.Property(key, p.Get(key)) } - err := runTest(c, func(ctx gs.Context) { + err := runTest(c, func(ctx gsioc.Context) { assert.Equal(t, ctx.Prop("yaml.list[0]"), "1") assert.Equal(t, ctx.Prop("yaml.list[1]"), "2") assert.Equal(t, ctx.Prop("spring.application.name"), "test") @@ -451,8 +451,8 @@ func TestApplicationContext_LoadProperties(t *testing.T) { func TestApplicationContext_Get(t *testing.T) { t.Run("panic", func(t *testing.T) { - c := gs.New() - err := runTest(c, func(p gs.Context) { + c := gsioc.New() + err := runTest(c, func(p gsioc.Context) { { var s fmt.Stringer err := p.Get(s) @@ -468,11 +468,11 @@ func TestApplicationContext_Get(t *testing.T) { }) t.Run("success", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).Export((*Grouper)(nil)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var two *BeanTwo err := p.Get(&two) @@ -532,7 +532,7 @@ func TestApplicationContext_Get(t *testing.T) { // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) // c.Object(new(BeanTwo)) -// c.Refresh() +// c.Refresh(false) // // p := <-ch // @@ -617,7 +617,7 @@ func NewPtrStudent(teacher Teacher, room string) *Student { } func TestApplicationContext_RegisterBeanFn(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("room", "Class 3 Grade 1") @@ -633,7 +633,7 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { fmt.Println(teacher.Course()) }).Name("newTeacher") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var st1 *Student err := p.Get(&st1, "st1") @@ -672,9 +672,9 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { func TestApplicationContext_Profile(t *testing.T) { t.Run("bean:_c:", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -683,10 +683,10 @@ func TestApplicationContext_Profile(t *testing.T) { }) t.Run("bean:_c:test", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("spring.profiles.active", "test") c.Object(&BeanZero{5}) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -700,11 +700,11 @@ type BeanFour struct{} func TestApplicationContext_DependsOn(t *testing.T) { t.Run("random", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) @@ -715,11 +715,11 @@ func TestApplicationContext_DependsOn(t *testing.T) { "github.com/go-spring/spring-core/gs/gs_test.BeanZero:BeanZero", } - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)).DependsOn(dependsOn...) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) } @@ -727,32 +727,32 @@ func TestApplicationContext_DependsOn(t *testing.T) { func TestApplicationContext_Primary(t *testing.T) { t.Run("duplicate", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "duplicate beans ") }) t.Run("duplicate", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) // primary 是在多个候选 bean 里面选择,而不是允许同名同类型的两个 bean c.Object(&BeanZero{6}).Primary() c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "duplicate beans ") }) t.Run("not primary", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -762,12 +762,12 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("primary", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}).Name("zero_6").Primary() c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -782,11 +782,11 @@ type FuncObj struct { } func TestDefaultProperties_WireFunc(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(func(int) int { return 6 }) obj := new(FuncObj) c.Object(obj) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) i := obj.Fn(3) assert.Equal(t, i, 6) @@ -827,10 +827,10 @@ func (m localManager) Cluster() string { func TestApplicationContext_RegisterBeanFn2(t *testing.T) { t.Run("ptr manager", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("manager.version", "1.0.0") c.Provide(NewPtrManager) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var m Manager err := p.Get(&m) @@ -846,13 +846,13 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("manager.version", "1.0.0") bd := c.Provide(NewManager) assert.Equal(t, bd.BeanName(), "NewManager") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var m Manager err := p.Get(&m) @@ -866,26 +866,26 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetError) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "gs_test.go:\\d* error") }) t.Run("manager return error nil", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetErrorNil) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) t.Run("manager return nil", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("manager.version", "1.0.0") c.Provide(NewNullPtrManager) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "return nil") }) } @@ -939,9 +939,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(callDestroy)).Init((*callDestroy).Init) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -953,16 +953,16 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init with error", func(t *testing.T) { { - c := gs.New() + c := gsioc.New() c.Object(&callDestroy{i: 1}).Init((*callDestroy).InitWithError) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "error") } - c := gs.New() + c := gsioc.New() c.Property("int", 0) c.Object(&callDestroy{}).Init((*callDestroy).InitWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -972,9 +972,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call interface init", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Provide(func() destroyable { return new(callDestroy) }).Init(destroyable.Init) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -986,16 +986,16 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call interface init with error", func(t *testing.T) { { - c := gs.New() + c := gsioc.New() c.Provide(func() destroyable { return &callDestroy{i: 1} }).Init(destroyable.InitWithError) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "error") } - c := gs.New() + c := gsioc.New() c.Property("int", 0) c.Provide(func() destroyable { return &callDestroy{} }).Init(destroyable.InitWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1005,9 +1005,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested init", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(nestedCallDestroy)).Init((*nestedCallDestroy).Init) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *nestedCallDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1017,11 +1017,11 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested interface init", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&nestedDestroyable{ destroyable: new(callDestroy), }).Init((*nestedDestroyable).Init) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *nestedDestroyable err := p.Get(&d) assert.Nil(t, err) @@ -1036,10 +1036,10 @@ type RecoresCluster struct { } func TestApplicationContext_ValueBincoreng(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cluster *RecoresCluster err := p.Get(&cluster) fmt.Println(cluster) @@ -1051,17 +1051,17 @@ func TestApplicationContext_ValueBincoreng(t *testing.T) { func TestApplicationContext_Collect(t *testing.T) { t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&struct { Events []ServerInterface `autowire:""` }{}) - err := runTest(c, func(ctx gs.Context) {}) + err := runTest(c, func(ctx gsioc.Context) {}) assert.Error(t, err, "no beans collected for \"\"") }) t.Run("", func(t *testing.T) { - c := gs.New() - err := runTest(c, func(ctx gs.Context) { + c := gsioc.New() + err := runTest(c, func(ctx gsioc.Context) { var Events []ServerInterface err := ctx.Get(&Events) assert.Error(t, err, "no beans collected for \"\"") @@ -1070,17 +1070,17 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&struct { Events []ServerInterface `autowire:"?"` }{}) - err := runTest(c, func(ctx gs.Context) {}) + err := runTest(c, func(ctx gsioc.Context) {}) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - c := gs.New() - err := runTest(c, func(ctx gs.Context) { + c := gsioc.New() + err := runTest(c, func(ctx gsioc.Context) { var Events []ServerInterface err := ctx.Get(&Events, "?") assert.Nil(t, err) @@ -1089,11 +1089,11 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("one") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var rcs []*RecoresCluster err := p.Get(&rcs) assert.Nil(t, err) @@ -1104,12 +1104,12 @@ func TestApplicationContext_Collect(t *testing.T) { t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("a").Order(1) c.Object(new(RecoresCluster)).Name("b").Order(2) - intBean := c.Provide(func(p gs.Context) func() { + intBean := c.Provide(func(p gsioc.Context) func() { var rcs []*RecoresCluster err := p.Get(&rcs) @@ -1122,7 +1122,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) } @@ -1229,10 +1229,10 @@ func TestOptionPattern(t *testing.T) { func TestOptionConstructorArg(t *testing.T) { t.Run("option default", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1244,10 +1244,10 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withClassName", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1260,13 +1260,13 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("class_name", "二年级03班") c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gsarg.Option(withStudents)) c.Object(new(Student)).Name("Student1") c.Object(new(Student)).Name("Student2") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1279,7 +1279,7 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents withClassName", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("class_name", "二年级06班") c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, @@ -1291,7 +1291,7 @@ func TestOptionConstructorArg(t *testing.T) { ) c.Object(&Student{}).Name("Student1") c.Object(&Student{}).Name("Student2") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1348,11 +1348,11 @@ type Service struct { func TestApplicationContext_RegisterMethodBean(t *testing.T) { t.Run("method bean", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -1371,11 +1371,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean condition", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)).On(gscond.Not(gscond.OK())) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -1390,11 +1390,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean arg", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) c.Provide((*Server).ConsumerArg, parent, "${i:=9}") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -1412,12 +1412,12 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean wire to other bean", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Provide(NewServerInterface) c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var si ServerInterface err := p.Get(&si) @@ -1462,12 +1462,12 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { } }() - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)).DependsOn("Service") c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) - err := c.Refresh() + err := c.Refresh(false) if err != nil { panic(err) } @@ -1477,10 +1477,10 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean autowire", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) assert.Nil(t, err) @@ -1490,11 +1490,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*Server)(nil)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -1512,20 +1512,20 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type error", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*int)(nil)) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "can't find bean, bean:\"int:\" type:\"\\*gs_test.Server\"") }) t.Run("method bean selector beanId", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "Server") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -1543,11 +1543,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId error", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "NULL") - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "can't find bean, bean:\"NULL\" type:\"\\*gs_test.Server\"") }) } @@ -1563,7 +1563,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { Complex complex64 // `value:"${complex}"` } - c := gs.New() + c := gsioc.New() conf.RegisterConverter(func(v string) (level, error) { if v == "debug" { @@ -1577,7 +1577,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { c.Property("level", "debug") c.Property("complex", "1+i") c.Object(&config) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) fmt.Printf("%+v\n", config) @@ -1599,27 +1599,27 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { // 直接创建的 Object 直接发生循环依赖是没有关系的。 t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Object(new(CircleC)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Provide(func() *CircleC { return new(CircleC) }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(CircleA)) c.Provide(func() *CircleB { return new(CircleB) @@ -1627,12 +1627,12 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Provide(func() *CircleC { return new(CircleC) }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Provide(func(b *CircleB) *CircleA { return new(CircleA) }) @@ -1642,7 +1642,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Provide(func(a *CircleA) *CircleC { return new(CircleC) }) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "found circle autowire") }) } @@ -1711,12 +1711,12 @@ func NewNilVarObj(i interface{}, options ...VarOptionFunc) *VarObj { func TestApplicationContext_RegisterOptionBean(t *testing.T) { t.Run("nil param 0", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewNilVarObj, gsarg.Nil()) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1727,12 +1727,12 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 1", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, "${var.obj}", gsarg.Option(withVar, "v1")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1744,12 +1744,12 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 2", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, gsarg.Value("description"), gsarg.Option(withVar, "v1", "v2")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1762,11 +1762,11 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1776,11 +1776,11 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1", "v2")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1818,9 +1818,9 @@ func TestApplicationContext_Close(t *testing.T) { t.Run("call destroy fn", func(t *testing.T) { called := false - c := gs.New() + c := gsioc.New() c.Object(func() {}).Destroy(func(f func()) { called = true }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) c.Close() @@ -1828,10 +1828,10 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call destroy", func(t *testing.T) { - c := gs.New() + c := gsioc.New() d := new(callDestroy) c.Object(d).Destroy((*callDestroy).Destroy) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1845,10 +1845,10 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gs.New() + c := gsioc.New() d := &callDestroy{i: 1} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1860,10 +1860,10 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - c := gs.New() + c := gsioc.New() d := &callDestroy{} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1875,9 +1875,9 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call interface destroy", func(t *testing.T) { - c := gs.New() + c := gsioc.New() bd := c.Provide(func() destroyable { return new(callDestroy) }).Destroy(destroyable.Destroy) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1892,9 +1892,9 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gs.New() + c := gsioc.New() bd := c.Provide(func() destroyable { return &callDestroy{i: 1} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1907,10 +1907,10 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - c := gs.New() + c := gsioc.New() c.Property("int", 0) bd := c.Provide(func() destroyable { return &callDestroy{} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1935,12 +1935,12 @@ type PtrNestedAutowireBean struct { } func TestApplicationContext_NestedAutowireBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(NestedAutowireBean)) c.Object(&PtrNestedAutowireBean{ SubNestedAutowireBean: new(SubNestedAutowireBean), }) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *NestedAutowireBean err := p.Get(&b) @@ -1977,13 +1977,13 @@ func TestApplicationContext_NestValueField(t *testing.T) { t.Run("private", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("sdk.wx.auto-create", true) c.Property("sdk.wx.enable", true) c.Object(new(wxChannel)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var channel *wxChannel err := p.Get(&channel) assert.Nil(t, err) @@ -1994,11 +1994,11 @@ func TestApplicationContext_NestValueField(t *testing.T) { }) t.Run("public", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("sdk.wx.auto-create", true) c.Property("sdk.wx.enable", true) c.Object(new(WXChannel)) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var channel *WXChannel err := p.Get(&channel) assert.Nil(t, err) @@ -2012,7 +2012,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { func TestApplicationContext_FnArgCollectBean(t *testing.T) { t.Run("interface type", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Provide(newHistoryTeacher("t1")).Name("t1").Export((*Teacher)(nil)) c.Provide(newHistoryTeacher("t2")).Name("t2").Export((*Teacher)(nil)) c.Provide(func(teachers []Teacher) func() { @@ -2024,7 +2024,7 @@ func TestApplicationContext_FnArgCollectBean(t *testing.T) { assert.Equal(t, names, []string{"t1", "t2"}) return func() {} }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) } @@ -2043,9 +2043,9 @@ func (_ *filterImpl) Filter(input string) string { func TestApplicationContext_BeanCache(t *testing.T) { t.Run("not implement interface", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(func() {}).Export((*filter)(nil)) - err := c.Refresh() + err := c.Refresh(false) assert.Error(t, err, "doesn't implement interface gs_test.filter") }) @@ -2056,12 +2056,12 @@ func TestApplicationContext_BeanCache(t *testing.T) { F2 filter `autowire:"f2"` } - c := gs.New() + c := gsioc.New() c.Provide(func() filter { return new(filterImpl) }).Name("f1") c.Object(new(filterImpl)).Export((*filter)(nil)).Name("f2") c.Object(&server) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) } @@ -2077,9 +2077,9 @@ func (i Integer) Value() int { } func TestApplicationContext_IntInterface(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Provide(func() IntInterface { return Integer(5) }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) } @@ -2104,9 +2104,9 @@ func TestApplicationContext_Properties(t *testing.T) { t.Run("array properties", func(t *testing.T) { b := new(ArrayProperties) - c := gs.New() + c := gsioc.New() c.Object(b) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) }) @@ -2119,13 +2119,13 @@ func TestApplicationContext_Properties(t *testing.T) { MapA map[string]string `value:"${map_a:=}"` }{} - c := gs.New() + c := gsioc.New() c.Property("map_a.nba", "nba") c.Property("map_a.cba", "cba") c.Property("int_a", "3") c.Property("int_b", "4") c.Object(&obj) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) assert.Equal(t, obj.Int, 5) @@ -2154,7 +2154,7 @@ func TestApplicationContext_Destroy(t *testing.T) { destroyIndex := 0 destroyArray := []int{0, 0, 0, 0} - c := gs.New() + c := gsioc.New() c.Object(new(FirstDestroy)).Destroy( func(_ *FirstDestroy) { fmt.Println("::FirstDestroy") @@ -2179,7 +2179,7 @@ func TestApplicationContext_Destroy(t *testing.T) { destroyArray[destroyIndex] = 4 destroyIndex++ }) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) c.Close() @@ -2195,9 +2195,9 @@ type ObjFactory struct{} func (factory *ObjFactory) NewObj(i int) *Obj { return &Obj{i: i} } func TestApplicationContext_CreateBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&ObjFactory{}) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { b, err := p.Wire((*ObjFactory).NewObj, gsarg.R1("${i:=5}")) fmt.Println(b, err) }) @@ -2208,7 +2208,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("bean:test_ctx:", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}).On(gscond. OnProfile("test"). @@ -2218,7 +2218,7 @@ func TestDefaultSpringContext(t *testing.T) { On(gscond.OK()), ) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2227,10 +2227,10 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:test", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("spring.profiles.active", "test") c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -2239,10 +2239,10 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:stable", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("spring.profiles.active", "stable") c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2252,14 +2252,14 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("option withClassName Condition", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("president", "CaiYuanPei") c.Property("class_floor", 2) c.Provide(NewClassRoom, gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", ).On(gscond.OnProperty("class_name_enable"))) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2273,7 +2273,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("option withClassName Apply", func(t *testing.T) { onProperty := gscond.OnProperty("class_name_enable") - c := gs.New() + c := gsioc.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gsarg.Option(withClassName, @@ -2281,7 +2281,7 @@ func TestDefaultSpringContext(t *testing.T) { "${class_floor:=3}", ).On(onProperty), ) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2294,11 +2294,11 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("method bean cond", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) c.Provide((*Server).Consumer, parent.ID()).On(gscond.OnProperty("consumer.enable")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var s *Server err := p.Get(&s) @@ -2320,7 +2320,7 @@ func TestDefaultSpringContext(t *testing.T) { // parent := c.Provide(NewServerInterface).On(cond.OnProperty("server.is.nil")) // c.Provide(ServerInterface.Consumer, parent.ID()) // -// c.Refresh() +// c.Refresh(false) // // var s *Server // ok := p.Get(&s) @@ -2332,7 +2332,7 @@ func TestDefaultSpringContext(t *testing.T) { // } func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { - c := gs.New() + c := gsioc.New() c1 := gscond.OnProperty("null", gscond.MatchIfMissing()).Or().OnProfile("test") @@ -2342,7 +2342,7 @@ func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { c.Object(new(BeanTwo)).On(gscond.OnBean("BeanOne")) c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnBean("Null")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var two *BeanTwo err := p.Get(&two) @@ -2356,12 +2356,12 @@ func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { for i := 0; i < 20; i++ { // 测试 Find 无需绑定,不要排序 - c := gs.New() + c := gsioc.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).On(gscond.OnMissingBean("BeanOne")) c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnMissingBean("Null")) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var two *BeanTwo err := p.Get(&two) @@ -2451,7 +2451,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) -// c.Refresh() +// c.Refresh(false) // // c1 := cond.OnBean("BeanOne") // assert.True(t, c1.Matches(c)) @@ -2465,7 +2465,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) -// c.Refresh() +// c.Refresh(false) // // c1 := cond.OnMissingBean("BeanOne") // assert.False(t, c1.Matches(c)) @@ -2483,7 +2483,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Property("bool", false) // c.Property("int", 3) -// c.Refresh() +// c.Refresh(false) // // c1 := cond.OnProperty("int") // assert.True(t, c1.Matches(c)) @@ -2535,7 +2535,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c := gs.New() // c.Property(environ.SpringProfilesActive, "test") -// c.Refresh() +// c.Refresh(false) // // profileCond := cond.OnProfile("test") // assert.True(t, profileCond.Matches(c)) @@ -2557,10 +2557,10 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { func TestApplicationContext_Invoke(t *testing.T) { t.Run("not run", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(func() {}) c.Property("version", "v0.0.1") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { _, _ = p.Invoke(func(f func(), version string) { fmt.Println("version:", version) }, "", "${version}") @@ -2569,11 +2569,11 @@ func TestApplicationContext_Invoke(t *testing.T) { }) t.Run("run", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(func() {}) c.Property("version", "v0.0.1") c.Property("spring.profiles.active", "dev") - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { fn := func(f func(), version string) { fmt.Println("version:", version) } @@ -2589,12 +2589,12 @@ type emptyStructB struct{} func TestEmptyStruct(t *testing.T) { - c := gs.New() + c := gsioc.New() objA := &emptyStructA{} c.Object(objA) objB := &emptyStructB{} c.Object(objB) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) // objA 和 objB 的地址相同但是类型确实不一样。 @@ -2609,11 +2609,11 @@ func TestMapCollection(t *testing.T) { } t.Run("", func(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(&mapValue{"a"}).Name("a").Order(1) c.Object(&mapValue{"b"}).Name("b").Order(2) c.Object(&mapValue{"c"}).Name("c").On(gscond.Not(gscond.OK())) - err := runTest(c, func(p gs.Context) { + err := runTest(c, func(p gsioc.Context) { var vSlice []*mapValue err := p.Get(&vSlice) @@ -2647,7 +2647,7 @@ func newCircularB() *circularB { func TestLazy(t *testing.T) { for i := 0; i < 1; i++ { - c := gs.New() + c := gsioc.New() c.Property("spring.main.allow-circular-references", "true") c.Provide(newCircularA) c.Provide(newCircularB) @@ -2655,7 +2655,7 @@ func TestLazy(t *testing.T) { b *circularB `autowire:""` }{} c.Object(&d) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) assert.NotNil(t, d.b) assert.NotNil(t, d.b.A) @@ -2665,7 +2665,7 @@ func TestLazy(t *testing.T) { type memory struct { } -func (m *memory) OnInit(ctx gs.Context) error { +func (m *memory) OnInit(ctx gsioc.Context) error { fmt.Println("memory.OnInit") return nil } @@ -2678,7 +2678,7 @@ type table struct { _ *memory `autowire:""` } -func (t *table) OnInit(ctx gs.Context) error { +func (t *table) OnInit(ctx gsioc.Context) error { fmt.Println("table.OnInit") return nil } @@ -2688,16 +2688,16 @@ func (t *table) OnDestroy() { } func TestDestroyDependence(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Object(new(memory)) c.Object(new(table)).Name("aaa") c.Object(new(table)).Name("bbb") - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) } type ContextAware struct { - gs.ContextAware + gsioc.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -2705,10 +2705,10 @@ func (c *ContextAware) Echo(str string) string { } func TestContextAware(t *testing.T) { - c := gs.New() + c := gsioc.New() c.Property("prefix", "hello") b := c.Object(new(ContextAware)) - err := c.Refresh() + err := c.Refresh(false) assert.Nil(t, err) a := b.Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 61862e9e..4d0c9bc1 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -22,10 +22,11 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/gsioc" "github.com/go-spring/spring-core/gs/gsutil" ) -var ctx gs.Context +var ctx gsioc.Context // Init 初始化测试环境 func Init() error { diff --git a/gs/gs_version.go b/gs/version/version.go similarity index 97% rename from gs/gs_version.go rename to gs/version/version.go index 3252a874..b65c9fed 100644 --- a/gs/gs_version.go +++ b/gs/version/version.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs +package version const ( Version = "go-spring@v1.1.3" From 5cd2ec7f279d2e73a05b42ab69cbbd32df373ff9 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 30 Dec 2024 08:37:28 +0800 Subject: [PATCH 11/75] code refactor --- gs/{boot.go => gs.go} | 24 +- gs/{boot_test.go => gs_test.go} | 0 gs/gstest/gstest.go | 14 +- gs/internal/gs/gs.go | 1 + gs/{gsapp => internal/gs_app}/app.go | 46 +- .../gs_app}/app_bootstrap.go | 20 +- gs/{gsarg => internal/gs_arg}/arg.go | 24 +- gs/{gsarg => internal/gs_arg}/arg_mock.go | 4 +- gs/{gsarg => internal/gs_arg}/arg_test.go | 26 +- .../gsbean.go => internal/gs_bean/bean.go} | 90 ++-- gs/{gscond => internal/gs_cond}/cond.go | 24 +- gs/{gscond => internal/gs_cond}/cond_mock.go | 4 +- gs/{gscond => internal/gs_cond}/cond_test.go | 2 +- gs/{appconf => internal/gs_conf}/args.go | 2 +- gs/{appconf => internal/gs_conf}/conf.go | 2 +- gs/{appconf => internal/gs_conf}/envs.go | 2 +- gs/{appconf => internal/gs_conf}/envs_test.go | 2 +- gs/{gsioc => internal/gs_ctx}/gs.go | 162 +++---- gs/{gsioc => internal/gs_ctx}/gs_bean.go | 10 +- gs/{gsioc => internal/gs_ctx}/gs_bean_test.go | 20 +- gs/{gsioc => internal/gs_ctx}/gs_context.go | 20 +- .../gs_ctx}/gs_dynamic_test.go | 6 +- gs/{gsioc => internal/gs_ctx}/gs_test.go | 402 +++++++++--------- gs/{gsutil => internal/gs_util}/type.go | 2 +- gs/{gsutil => internal/gs_util}/value.go | 2 +- gs/version/version.go | 22 - 26 files changed, 458 insertions(+), 475 deletions(-) rename gs/{boot.go => gs.go} (69%) rename gs/{boot_test.go => gs_test.go} (100%) create mode 100644 gs/internal/gs/gs.go rename gs/{gsapp => internal/gs_app}/app.go (82%) rename gs/{gsapp => internal/gs_app}/app_bootstrap.go (74%) rename gs/{gsarg => internal/gs_arg}/arg.go (95%) rename gs/{gsarg => internal/gs_arg}/arg_mock.go (97%) rename gs/{gsarg => internal/gs_arg}/arg_test.go (85%) rename gs/{gsbean/gsbean.go => internal/gs_bean/bean.go} (71%) rename gs/{gscond => internal/gs_cond}/cond.go (94%) rename gs/{gscond => internal/gs_cond}/cond_mock.go (97%) rename gs/{gscond => internal/gs_cond}/cond_test.go (99%) rename gs/{appconf => internal/gs_conf}/args.go (98%) rename gs/{appconf => internal/gs_conf}/conf.go (99%) rename gs/{appconf => internal/gs_conf}/envs.go (99%) rename gs/{appconf => internal/gs_conf}/envs_test.go (97%) rename gs/{gsioc => internal/gs_ctx}/gs.go (86%) rename gs/{gsioc => internal/gs_ctx}/gs_bean.go (97%) rename gs/{gsioc => internal/gs_ctx}/gs_bean_test.go (94%) rename gs/{gsioc => internal/gs_ctx}/gs_context.go (86%) rename gs/{gsioc => internal/gs_ctx}/gs_dynamic_test.go (97%) rename gs/{gsioc => internal/gs_ctx}/gs_test.go (88%) rename gs/{gsutil => internal/gs_util}/type.go (99%) rename gs/{gsutil => internal/gs_util}/value.go (99%) delete mode 100644 gs/version/version.go diff --git a/gs/boot.go b/gs/gs.go similarity index 69% rename from gs/boot.go rename to gs/gs.go index d3c105a5..8fcaa7f8 100644 --- a/gs/boot.go +++ b/gs/gs.go @@ -19,16 +19,16 @@ package gs import ( "reflect" - "github.com/go-spring/spring-core/gs/gsapp" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" - "github.com/go-spring/spring-core/gs/gsioc" + "github.com/go-spring/spring-core/gs/internal/gs_app" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) -var app = gsapp.NewApp() +var app = gs_app.NewApp() // Start 启动程序。 -func Start() (gsioc.Context, error) { +func Start() (gs_ctx.Context, error) { return app.Start() } @@ -48,7 +48,7 @@ func ShutDown(msg ...string) { } // Bootstrap 参考 App.Bootstrap 的解释。 -func Bootstrap() *gsapp.Bootstrapper { +func Bootstrap() *gs_app.Bootstrapper { return app.Bootstrap() } @@ -63,16 +63,16 @@ func Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { +func Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { return app.Accept(b) } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *gsbean.BeanDefinition { - return app.Accept(gsioc.NewBean(reflect.ValueOf(i))) +func Object(i interface{}) *gs_bean.BeanDefinition { + return app.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return app.Accept(gsioc.NewBean(ctor, args...)) +func Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { + return app.Accept(gs_ctx.NewBean(ctor, args...)) } diff --git a/gs/boot_test.go b/gs/gs_test.go similarity index 100% rename from gs/boot_test.go rename to gs/gs_test.go diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 4d0c9bc1..2865e616 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -21,12 +21,12 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsioc" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_util" ) -var ctx gsioc.Context +var ctx gs_ctx.Context // Init 初始化测试环境 func Init() error { @@ -65,16 +65,16 @@ func Bind(i interface{}, opts ...conf.BindArg) error { } // Get 获取对象 -func Get(i interface{}, selectors ...gsutil.BeanSelector) error { +func Get(i interface{}, selectors ...gs_util.BeanSelector) error { return ctx.Get(i, selectors...) } // Wire 注入对象 -func Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) { +func Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) { return ctx.Wire(objOrCtor, ctorArgs...) } // Invoke 调用函数 -func Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) { +func Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) { return ctx.Invoke(fn, args...) } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go new file mode 100644 index 00000000..1e8a434c --- /dev/null +++ b/gs/internal/gs/gs.go @@ -0,0 +1 @@ +package gs diff --git a/gs/gsapp/app.go b/gs/internal/gs_app/app.go similarity index 82% rename from gs/gsapp/app.go rename to gs/internal/gs_app/app.go index 6b0a9937..6a3aaea6 100644 --- a/gs/gsapp/app.go +++ b/gs/internal/gs_app/app.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsapp +package gs_app import ( "context" @@ -25,10 +25,14 @@ import ( "strings" "syscall" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" - "github.com/go-spring/spring-core/gs/gsioc" - "github.com/go-spring/spring-core/gs/version" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" +) + +const ( + Version = "go-spring@v1.1.3" + Website = "https://go-spring.com/" ) // SpringBannerVisible 是否显示 banner。 @@ -36,12 +40,12 @@ const SpringBannerVisible = "spring.banner.visible" // AppRunner 命令行启动器接口 type AppRunner interface { - Run(ctx gsioc.Context) + Run(ctx gs_ctx.Context) } // AppEvent 应用运行过程中的事件 type AppEvent interface { - OnAppStart(ctx gsioc.Context) // 应用启动的事件 + OnAppStart(ctx gs_ctx.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -53,7 +57,7 @@ type tempApp struct { type App struct { *tempApp - c gsioc.Container + c gs_ctx.Container b *Bootstrapper exitChan chan struct{} @@ -65,7 +69,7 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { return &App{ - c: gsioc.New(), + c: gs_ctx.New(), tempApp: &tempApp{}, exitChan: make(chan struct{}), } @@ -76,7 +80,7 @@ func (app *App) Banner(banner string) { app.banner = banner } -func (app *App) Start() (gsioc.Context, error) { +func (app *App) Start() (gs_ctx.Context, error) { app.Object(app) @@ -106,23 +110,23 @@ func (app *App) Start() (gsioc.Context, error) { // 执行命令行启动器 for _, r := range app.Runners { - r.Run(app.c.(gsioc.Context)) + r.Run(app.c.(gs_ctx.Context)) } // 通知应用启动事件 for _, event := range app.Events { - event.OnAppStart(app.c.(gsioc.Context)) + event.OnAppStart(app.c.(gs_ctx.Context)) } // 通知应用停止事件 - app.c.(gsioc.Context).Go(func(ctx context.Context) { + app.c.(gs_ctx.Context).Go(func(ctx context.Context) { <-ctx.Done() for _, event := range app.Events { event.OnAppStop(context.Background()) } }) - return app.c.(gsioc.Context), nil + return app.c.(gs_ctx.Context), nil } func (app *App) wait() { @@ -197,13 +201,13 @@ func (app *App) printBanner(banner string) { } var padding []byte - if n := (maxLength - len(version.Version)) / 2; n > 0 { + if n := (maxLength - len(Version)) / 2; n > 0 { padding = make([]byte, n) for i := range padding { padding[i] = ' ' } } - fmt.Println(string(padding) + version.Version + "\n") + fmt.Println(string(padding) + Version + "\n") } // ShutDown 关闭执行器 @@ -235,16 +239,16 @@ func (app *App) Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { +func (app *App) Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { return app.c.Accept(b) } // Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *gsbean.BeanDefinition { - return app.c.Accept(gsioc.NewBean(reflect.ValueOf(i))) +func (app *App) Object(i interface{}) *gs_bean.BeanDefinition { + return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return app.c.Accept(gsioc.NewBean(ctor, args...)) +func (app *App) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { + return app.c.Accept(gs_ctx.NewBean(ctor, args...)) } diff --git a/gs/gsapp/app_bootstrap.go b/gs/internal/gs_app/app_bootstrap.go similarity index 74% rename from gs/gsapp/app_bootstrap.go rename to gs/internal/gs_app/app_bootstrap.go index 396a34d5..1eb4b1bc 100644 --- a/gs/gsapp/app_bootstrap.go +++ b/gs/internal/gs_app/app_bootstrap.go @@ -14,23 +14,23 @@ * limitations under the License. */ -package gsapp +package gs_app import ( "reflect" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" - "github.com/go-spring/spring-core/gs/gsioc" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) type Bootstrapper struct { - c gsioc.Container + c gs_ctx.Container } func newBootstrap() *Bootstrapper { return &Bootstrapper{ - c: gsioc.New(), + c: gs_ctx.New(), } } @@ -45,13 +45,13 @@ func (b *Bootstrapper) Property(key string, value interface{}) { } // Object 参考 Container.Object 的解释。 -func (b *Bootstrapper) Object(i interface{}) *gsbean.BeanDefinition { - return b.c.Accept(gsioc.NewBean(reflect.ValueOf(i))) +func (b *Bootstrapper) Object(i interface{}) *gs_bean.BeanDefinition { + return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *Bootstrapper) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { - return b.c.Accept(gsioc.NewBean(ctor, args...)) +func (b *Bootstrapper) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { + return b.c.Accept(gs_ctx.NewBean(ctor, args...)) } func (b *Bootstrapper) start() error { diff --git a/gs/gsarg/arg.go b/gs/internal/gs_arg/arg.go similarity index 95% rename from gs/gsarg/arg.go rename to gs/internal/gs_arg/arg.go index 4762d209..3eba0230 100644 --- a/gs/gsarg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -17,7 +17,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=arg -source=arg.go -destination=arg_mock.go // Package arg 用于实现函数参数绑定。 -package gsarg +package gs_arg import ( "errors" @@ -25,8 +25,8 @@ import ( "reflect" "runtime" - "github.com/go-spring/spring-core/gs/gscond" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -34,7 +34,7 @@ import ( type Context interface { // Matches returns true when the Condition returns true, // and returns false when the Condition returns false. - Matches(c gscond.Condition) (bool, error) + Matches(c gs_cond.Condition) (bool, error) // Bind binds properties value by the "value" tag. Bind(v reflect.Value, tag string) error // Wire wires dependent beans by the "autowire" tag. @@ -240,16 +240,16 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) return reflect.ValueOf(g.v), nil case *optionArg: return g.call(ctx) - case gsutil.BeanDefinition: + case gs_util.BeanDefinition: tag = g.ID() case string: tag = g default: - tag = gsutil.TypeName(g) + ":" + tag = gs_util.TypeName(g) + ":" } // binds properties value by the "value" tag. - if gsutil.IsValueType(t) { + if gs_util.IsValueType(t) { if tag == "" { tag = "${}" } @@ -261,7 +261,7 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) } // wires dependent beans by the "autowire" tag. - if gsutil.IsBeanReceiver(t) { + if gs_util.IsBeanReceiver(t) { v := reflect.New(t).Elem() if err = ctx.Wire(v, tag); err != nil { return reflect.Value{}, err @@ -275,7 +275,7 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) // optionArg Option 函数的参数绑定。 type optionArg struct { r *Callable - c gscond.Condition + c gs_cond.Condition } // Provide 为 Option 方法绑定运行时参数。 @@ -302,8 +302,8 @@ func Option(fn interface{}, args ...Arg) *optionArg { return &optionArg{r: r} } -// On 设置一个 gscond.Condition 对象。 -func (arg *optionArg) On(c gscond.Condition) *optionArg { +// On 设置一个 gs_cond.Condition 对象。 +func (arg *optionArg) On(c gs_cond.Condition) *optionArg { arg.c = c return arg } @@ -400,7 +400,7 @@ func (r *Callable) Call(ctx Context) ([]reflect.Value, error) { } o := out[n-1] - if gsutil.IsErrorType(o.Type()) { + if gs_util.IsErrorType(o.Type()) { if i := o.Interface(); i != nil { return out[:n-1], i.(error) } diff --git a/gs/gsarg/arg_mock.go b/gs/internal/gs_arg/arg_mock.go similarity index 97% rename from gs/gsarg/arg_mock.go rename to gs/internal/gs_arg/arg_mock.go index afe1936c..4cb1a3ad 100644 --- a/gs/gsarg/arg_mock.go +++ b/gs/internal/gs_arg/arg_mock.go @@ -7,12 +7,12 @@ // // Package arg is a generated GoMock package. -package gsarg +package gs_arg import ( reflect "reflect" - cond "github.com/go-spring/spring-core/gs/gscond" + cond "github.com/go-spring/spring-core/gs/internal/gs_cond" gomock "go.uber.org/mock/gomock" ) diff --git a/gs/gsarg/arg_test.go b/gs/internal/gs_arg/arg_test.go similarity index 85% rename from gs/gsarg/arg_test.go rename to gs/internal/gs_arg/arg_test.go index 0d84672d..91ecf2a7 100644 --- a/gs/gsarg/arg_test.go +++ b/gs/internal/gs_arg/arg_test.go @@ -14,13 +14,13 @@ * limitations under the License. */ -package gsarg_test +package gs_arg_test import ( "reflect" "testing" - "github.com/go-spring/spring-core/gs/gsarg" + "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" ) @@ -30,9 +30,9 @@ func TestBind(t *testing.T) { t.Run("zero argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := gsarg.NewMockContext(ctrl) + ctx := gs_arg.NewMockContext(ctrl) fn := func() {} - c, err := gsarg.Bind(fn, []gsarg.Arg{}, 1) + c, err := gs_arg.Bind(fn, []gs_arg.Arg{}, 1) if err != nil { t.Fatal(err) } @@ -46,13 +46,13 @@ func TestBind(t *testing.T) { t.Run("one value argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := gsarg.NewMockContext(ctrl) + ctx := gs_arg.NewMockContext(ctrl) expectInt := 0 fn := func(i int) { expectInt = i } - c, err := gsarg.Bind(fn, []gsarg.Arg{ - gsarg.Value(3), + c, err := gs_arg.Bind(fn, []gs_arg.Arg{ + gs_arg.Value(3), }, 1) if err != nil { t.Fatal(err) @@ -68,7 +68,7 @@ func TestBind(t *testing.T) { t.Run("one ctx value argument", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := gsarg.NewMockContext(ctrl) + ctx := gs_arg.NewMockContext(ctrl) ctx.EXPECT().Bind(gomock.Any(), "${a.b.c}").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).SetInt(3) return nil @@ -77,7 +77,7 @@ func TestBind(t *testing.T) { fn := func(i int) { expectInt = i } - c, err := gsarg.Bind(fn, []gsarg.Arg{ + c, err := gs_arg.Bind(fn, []gs_arg.Arg{ "${a.b.c}", }, 1) if err != nil { @@ -97,7 +97,7 @@ func TestBind(t *testing.T) { } ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := gsarg.NewMockContext(ctrl) + ctx := gs_arg.NewMockContext(ctrl) ctx.EXPECT().Wire(gomock.Any(), "a").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).Set(reflect.ValueOf(&st{3})) return nil @@ -106,7 +106,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := gsarg.Bind(fn, []gsarg.Arg{ + c, err := gs_arg.Bind(fn, []gs_arg.Arg{ "a", }, 1) if err != nil { @@ -126,7 +126,7 @@ func TestBind(t *testing.T) { } ctrl := gomock.NewController(t) defer ctrl.Finish() - ctx := gsarg.NewMockContext(ctrl) + ctx := gs_arg.NewMockContext(ctrl) ctx.EXPECT().Wire(gomock.Any(), "").DoAndReturn(func(v, tag interface{}) error { v.(reflect.Value).Set(reflect.ValueOf(&st{3})) return nil @@ -135,7 +135,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := gsarg.Bind(fn, []gsarg.Arg{}, 1) + c, err := gs_arg.Bind(fn, []gs_arg.Arg{}, 1) if err != nil { t.Fatal(err) } diff --git a/gs/gsbean/gsbean.go b/gs/internal/gs_bean/bean.go similarity index 71% rename from gs/gsbean/gsbean.go rename to gs/internal/gs_bean/bean.go index f1707e2d..7457882b 100644 --- a/gs/gsbean/gsbean.go +++ b/gs/internal/gs_bean/bean.go @@ -1,4 +1,4 @@ -package gsbean +package gs_bean import ( "errors" @@ -7,9 +7,9 @@ import ( "runtime" "strings" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gscond" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_util" ) type BeanStatus int8 @@ -47,38 +47,38 @@ func GetStatusString(status BeanStatus) string { // BeanDefinition bean 元数据。 type BeanDefinition struct { - V reflect.Value // 值 - T reflect.Type // 类型 - F *gsarg.Callable // 构造函数 + V reflect.Value // 值 + T reflect.Type // 类型 + F *gs_arg.Callable // 构造函数 file string // 注册点所在文件 line int // 注册点所在行数 - name string // 名称 - typeName string // 原始类型的全限定名 - status BeanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond gscond.Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []gsutil.BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 + name string // 名称 + typeName string // 原始类型的全限定名 + status BeanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond gs_cond.Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []gs_util.BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 } -func (d *BeanDefinition) GetName() string { return d.name } -func (d *BeanDefinition) GetTypeName() string { return d.typeName } -func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } -func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } -func (d *BeanDefinition) IsPrimary() bool { return d.primary } -func (d *BeanDefinition) IsMethod() bool { return d.method } -func (d *BeanDefinition) GetCond() gscond.Condition { return d.cond } -func (d *BeanDefinition) GetOrder() float32 { return d.order } -func (d *BeanDefinition) GetInit() interface{} { return d.init } -func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } -func (d *BeanDefinition) GetDepends() []gsutil.BeanSelector { return d.depends } -func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } +func (d *BeanDefinition) GetName() string { return d.name } +func (d *BeanDefinition) GetTypeName() string { return d.typeName } +func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } +func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } +func (d *BeanDefinition) IsPrimary() bool { return d.primary } +func (d *BeanDefinition) IsMethod() bool { return d.method } +func (d *BeanDefinition) GetCond() gs_cond.Condition { return d.cond } +func (d *BeanDefinition) GetOrder() float32 { return d.order } +func (d *BeanDefinition) GetInit() interface{} { return d.init } +func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } +func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } +func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } // Type 返回 bean 的类型。 func (d *BeanDefinition) Type() reflect.Type { @@ -160,7 +160,7 @@ func (d *BeanDefinition) Name(name string) *BeanDefinition { } // On 设置 bean 的 Condition。 -func (d *BeanDefinition) On(cond gscond.Condition) *BeanDefinition { +func (d *BeanDefinition) On(cond gs_cond.Condition) *BeanDefinition { d.cond = cond return d } @@ -172,7 +172,7 @@ func (d *BeanDefinition) Order(order float32) *BeanDefinition { } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { +func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { d.depends = append(d.depends, selectors...) return d } @@ -186,13 +186,13 @@ func (d *BeanDefinition) Primary() *BeanDefinition { // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !gsutil.IsFuncType(fnType) { + if !gs_util.IsFuncType(fnType) { return false } - if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { + if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { return false } - return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) + return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) } // Init 设置 bean 的初始化函数。 @@ -228,7 +228,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { if t, ok := o.(reflect.Type); ok { typ = t } else { // 处理 (*error)(nil) 这种导出形式 - typ = gsutil.Indirect(reflect.TypeOf(o)) + typ = gs_util.Indirect(reflect.TypeOf(o)) } if typ.Kind() != reflect.Interface { return errors.New("only interface type can be exported") @@ -249,7 +249,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { +func NewBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *BeanDefinition { var v reflect.Value var fromValue bool @@ -265,7 +265,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { } t := v.Type() - if !gsutil.IsBeanType(t) { + if !gs_util.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -274,32 +274,32 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { } const skip = 2 - var f *gsarg.Callable + var f *gs_arg.Callable _, file, line, _ := runtime.Caller(skip) // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 if !fromValue && t.Kind() == reflect.Func { - if !gsutil.IsConstructor(t) { + if !gs_util.IsConstructor(t) { t1 := "func(...)bean" t2 := "func(...)(bean, error)" panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) } var err error - f, err = gsarg.Bind(objOrCtor, ctorArgs, skip) + f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) if err != nil { panic(err) } out0 := t.Out(0) v = reflect.New(out0) - if gsutil.IsBeanType(out0) { + if gs_util.IsBeanType(out0) { v = v.Elem() } t = v.Type() - if !gsutil.IsBeanType(t) { + if !gs_util.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -315,7 +315,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 } - if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { + if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { panic(errors.New("bean should be *val but not *ref")) } @@ -331,7 +331,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *BeanDefinition { V: v, F: f, name: name, - typeName: gsutil.TypeName(t), + typeName: gs_util.TypeName(t), status: Default, method: method, file: file, diff --git a/gs/gscond/cond.go b/gs/internal/gs_cond/cond.go similarity index 94% rename from gs/gscond/cond.go rename to gs/internal/gs_cond/cond.go index b379d9bb..853f5829 100755 --- a/gs/gscond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -17,7 +17,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=cond -source=cond.go -destination=cond_mock.go // Package cond provides many conditions used when registering bean. -package gscond +package gs_cond import ( "errors" @@ -27,7 +27,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/expr" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -39,7 +39,7 @@ type Context interface { // returns empty string when the IoC container doesn't have it. Prop(key string, opts ...conf.GetOption) string // Find returns bean definitions that matched with the bean selector. - Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) + Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) } // Condition is used when registering a bean to determine whether it's valid. @@ -126,7 +126,7 @@ func (c *onMissingProperty) Matches(ctx Context) (bool, error) { // onBean is a Condition that returns true when finding more than one beans. type onBean struct { - selector gsutil.BeanSelector + selector gs_util.BeanSelector } func (c *onBean) Matches(ctx Context) (bool, error) { @@ -136,7 +136,7 @@ func (c *onBean) Matches(ctx Context) (bool, error) { // onMissingBean is a Condition that returns true when finding no beans. type onMissingBean struct { - selector gsutil.BeanSelector + selector gs_util.BeanSelector } func (c *onMissingBean) Matches(ctx Context) (bool, error) { @@ -146,7 +146,7 @@ func (c *onMissingBean) Matches(ctx Context) (bool, error) { // onSingleBean is a Condition that returns true when finding only one bean. type onSingleBean struct { - selector gsutil.BeanSelector + selector gs_util.BeanSelector } func (c *onSingleBean) Matches(ctx Context) (bool, error) { @@ -356,34 +356,34 @@ func (c *conditional) OnMissingProperty(name string) *conditional { // OnBean returns a conditional that starts with a Condition that returns true when // finding more than one beans. -func OnBean(selector gsutil.BeanSelector) *conditional { +func OnBean(selector gs_util.BeanSelector) *conditional { return New().OnBean(selector) } // OnBean adds a Condition that returns true when finding more than one beans. -func (c *conditional) OnBean(selector gsutil.BeanSelector) *conditional { +func (c *conditional) OnBean(selector gs_util.BeanSelector) *conditional { return c.On(&onBean{selector: selector}) } // OnMissingBean returns a conditional that starts with a Condition that returns // true when finding no beans. -func OnMissingBean(selector gsutil.BeanSelector) *conditional { +func OnMissingBean(selector gs_util.BeanSelector) *conditional { return New().OnMissingBean(selector) } // OnMissingBean adds a Condition that returns true when finding no beans. -func (c *conditional) OnMissingBean(selector gsutil.BeanSelector) *conditional { +func (c *conditional) OnMissingBean(selector gs_util.BeanSelector) *conditional { return c.On(&onMissingBean{selector: selector}) } // OnSingleBean returns a conditional that starts with a Condition that returns // true when finding only one bean. -func OnSingleBean(selector gsutil.BeanSelector) *conditional { +func OnSingleBean(selector gs_util.BeanSelector) *conditional { return New().OnSingleBean(selector) } // OnSingleBean adds a Condition that returns true when finding only one bean. -func (c *conditional) OnSingleBean(selector gsutil.BeanSelector) *conditional { +func (c *conditional) OnSingleBean(selector gs_util.BeanSelector) *conditional { return c.On(&onSingleBean{selector: selector}) } diff --git a/gs/gscond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go similarity index 97% rename from gs/gscond/cond_mock.go rename to gs/internal/gs_cond/cond_mock.go index e5fbed33..8a250335 100644 --- a/gs/gscond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -7,13 +7,13 @@ // // Package cond is a generated GoMock package. -package gscond +package gs_cond import ( reflect "reflect" conf "github.com/go-spring/spring-core/conf" - gsutil "github.com/go-spring/spring-core/gs/gsutil" + gsutil "github.com/go-spring/spring-core/gs/internal/gs_util" gomock "go.uber.org/mock/gomock" ) diff --git a/gs/gscond/cond_test.go b/gs/internal/gs_cond/cond_test.go similarity index 99% rename from gs/gscond/cond_test.go rename to gs/internal/gs_cond/cond_test.go index 27e25dc9..c7364577 100644 --- a/gs/gscond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gscond_test +package gs_cond_test // func TestOK(t *testing.T) { // ctrl := gomock.NewController(t) diff --git a/gs/appconf/args.go b/gs/internal/gs_conf/args.go similarity index 98% rename from gs/appconf/args.go rename to gs/internal/gs_conf/args.go index 275f6782..2e3d7596 100644 --- a/gs/appconf/args.go +++ b/gs/internal/gs_conf/args.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package appconf +package gs_conf import ( "fmt" diff --git a/gs/appconf/conf.go b/gs/internal/gs_conf/conf.go similarity index 99% rename from gs/appconf/conf.go rename to gs/internal/gs_conf/conf.go index 1b79736a..1a6035a9 100644 --- a/gs/appconf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package appconf +package gs_conf import ( "fmt" diff --git a/gs/appconf/envs.go b/gs/internal/gs_conf/envs.go similarity index 99% rename from gs/appconf/envs.go rename to gs/internal/gs_conf/envs.go index c9b1c2ae..45c8e81c 100644 --- a/gs/appconf/envs.go +++ b/gs/internal/gs_conf/envs.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package appconf +package gs_conf import ( "os" diff --git a/gs/appconf/envs_test.go b/gs/internal/gs_conf/envs_test.go similarity index 97% rename from gs/appconf/envs_test.go rename to gs/internal/gs_conf/envs_test.go index f2a94ea5..147322ce 100644 --- a/gs/appconf/envs_test.go +++ b/gs/internal/gs_conf/envs_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package appconf +package gs_conf import ( "testing" diff --git a/gs/gsioc/gs.go b/gs/internal/gs_ctx/gs.go similarity index 86% rename from gs/gsioc/gs.go rename to gs/internal/gs_ctx/gs.go index 038f8604..754efc35 100755 --- a/gs/gsioc/gs.go +++ b/gs/internal/gs_ctx/gs.go @@ -16,7 +16,7 @@ // Package gsioc 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App // 以及全局 App 对象封装三个部分,可以应用于多种使用场景。 -package gsioc +package gs_ctx import ( "bytes" @@ -31,10 +31,10 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" - "github.com/go-spring/spring-core/gs/gscond" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -55,9 +55,9 @@ type Container interface { Context() context.Context Properties() *dync.Properties Property(key string, value interface{}) - Object(i interface{}) *gsbean.BeanDefinition - Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition - Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition + Object(i interface{}) *gs_bean.BeanDefinition + Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition + Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition OnProperty(key string, fn interface{}) Refresh(autoClear bool) error Close() @@ -78,9 +78,9 @@ type Context interface { Prop(key string, opts ...conf.GetOption) string Resolve(s string) (string, error) Bind(i interface{}, opts ...conf.BindArg) error - Get(i interface{}, selectors ...gsutil.BeanSelector) error - Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) - Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) + Get(i interface{}, selectors ...gs_util.BeanSelector) error + Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) + Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) } @@ -91,9 +91,9 @@ type ContextAware struct { type tempContainer struct { initProperties *conf.Properties - beans []*gsbean.BeanDefinition - beansByName map[string][]*gsbean.BeanDefinition - beansByType map[reflect.Type][]*gsbean.BeanDefinition + beans []*gs_bean.BeanDefinition + beansByName map[string][]*gs_bean.BeanDefinition + beansByType map[reflect.Type][]*gs_bean.BeanDefinition mapOfOnProperty map[string]interface{} } @@ -125,8 +125,8 @@ func New() Container { p: dync.New(), tempContainer: &tempContainer{ initProperties: conf.NewProperties(), - beansByName: make(map[string][]*gsbean.BeanDefinition), - beansByType: make(map[reflect.Type][]*gsbean.BeanDefinition), + beansByName: make(map[string][]*gs_bean.BeanDefinition), + beansByType: make(map[reflect.Type][]*gs_bean.BeanDefinition), mapOfOnProperty: make(map[string]interface{}), }, } @@ -146,7 +146,7 @@ func validOnProperty(fn interface{}) error { if t.Kind() != reflect.Func { return errors.New("fn should be a func(value_type)") } - if t.NumIn() != 1 || !gsutil.IsValueType(t.In(0)) || t.NumOut() != 0 { + if t.NumIn() != 1 || !gs_util.IsValueType(t.In(0)) || t.NumOut() != 0 { return errors.New("fn should be a func(value_type)") } return nil @@ -170,7 +170,7 @@ func (c *container) Property(key string, value interface{}) { c.initProperties.Set(key, value) } -func (c *container) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { +func (c *container) Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } @@ -179,22 +179,22 @@ func (c *container) Accept(b *gsbean.BeanDefinition) *gsbean.BeanDefinition { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Object(i interface{}) *gsbean.BeanDefinition { +func (c *container) Object(i interface{}) *gs_bean.BeanDefinition { return c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Provide(ctor interface{}, args ...gsarg.Arg) *gsbean.BeanDefinition { +func (c *container) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { return c.Accept(NewBean(ctor, args...)) } // destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 type destroyer struct { - current *gsbean.BeanDefinition - earlier []*gsbean.BeanDefinition + current *gs_bean.BeanDefinition + earlier []*gs_bean.BeanDefinition } -func (d *destroyer) foundEarlier(b *gsbean.BeanDefinition) bool { +func (d *destroyer) foundEarlier(b *gs_bean.BeanDefinition) bool { for _, c := range d.earlier { if c == b { return true @@ -204,7 +204,7 @@ func (d *destroyer) foundEarlier(b *gsbean.BeanDefinition) bool { } // after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 -func (d *destroyer) after(b *gsbean.BeanDefinition) { +func (d *destroyer) after(b *gs_bean.BeanDefinition) { if d.foundEarlier(b) { return } @@ -234,7 +234,7 @@ type lazyField struct { type wiringStack struct { destroyers *list.List destroyerMap map[string]*destroyer - beans []*gsbean.BeanDefinition + beans []*gs_bean.BeanDefinition lazyFields []lazyField } @@ -246,7 +246,7 @@ func newWiringStack() *wiringStack { } // pushBack 添加一个即将注入的 bean 。 -func (s *wiringStack) pushBack(b *gsbean.BeanDefinition) { +func (s *wiringStack) pushBack(b *gs_bean.BeanDefinition) { // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) s.beans = append(s.beans, b) } @@ -268,7 +268,7 @@ func (s *wiringStack) path() (path string) { } // saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 -func (s *wiringStack) saveDestroyer(b *gsbean.BeanDefinition) *destroyer { +func (s *wiringStack) saveDestroyer(b *gs_bean.BeanDefinition) *destroyer { d, ok := s.destroyerMap[b.ID()] if !ok { d = &destroyer{current: b} @@ -345,13 +345,13 @@ func (c *container) Refresh(autoClear bool) (err error) { } } - beansById := make(map[string]*gsbean.BeanDefinition) + beansById := make(map[string]*gs_bean.BeanDefinition) { for _, b := range c.beans { - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { continue } - if b.GetStatus() != gsbean.Resolved { + if b.GetStatus() != gs_bean.Resolved { return fmt.Errorf("unexpected status %d", b.GetStatus()) } beanID := b.ID() @@ -412,7 +412,7 @@ func (c *container) Refresh(autoClear bool) (err error) { return nil } -func (c *container) registerBean(b *gsbean.BeanDefinition) { +func (c *container) registerBean(b *gs_bean.BeanDefinition) { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) @@ -423,13 +423,13 @@ func (c *container) registerBean(b *gsbean.BeanDefinition) { } // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 -func (c *container) resolveBean(b *gsbean.BeanDefinition) error { +func (c *container) resolveBean(b *gs_bean.BeanDefinition) error { - if b.GetStatus() >= gsbean.Resolving { + if b.GetStatus() >= gs_bean.Resolving { return nil } - b.SetStatus(gsbean.Resolving) + b.SetStatus(gs_bean.Resolving) // method bean 先确定 parent bean 是否存在 if b.IsMethod() { @@ -450,7 +450,7 @@ func (c *container) resolveBean(b *gsbean.BeanDefinition) error { msg = msg[:len(msg)-2] + "]" return errors.New(msg) } else if n == 0 { - b.SetStatus(gsbean.Deleted) + b.SetStatus(gs_bean.Deleted) return nil } } @@ -459,12 +459,12 @@ func (c *container) resolveBean(b *gsbean.BeanDefinition) error { if ok, err := b.GetCond().Matches(c); err != nil { return err } else if !ok { - b.SetStatus(gsbean.Deleted) + b.SetStatus(gs_bean.Deleted) return nil } } - b.SetStatus(gsbean.Resolved) + b.SetStatus(gs_bean.Resolved) return nil } @@ -512,16 +512,16 @@ func (tag wireTag) String() string { return b.String() } -func toWireTag(selector gsutil.BeanSelector) wireTag { +func toWireTag(selector gs_util.BeanSelector) wireTag { switch s := selector.(type) { case string: return parseWireTag(s) - case gsbean.BeanDefinition: + case gs_bean.BeanDefinition: return parseWireTag(s.ID()) - case *gsbean.BeanDefinition: + case *gs_bean.BeanDefinition: return parseWireTag(s.ID()) default: - return parseWireTag(gsutil.TypeName(s) + ":") + return parseWireTag(gs_util.TypeName(s) + ":") } } @@ -538,18 +538,18 @@ func toWireString(tags []wireTag) string { // findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) findBean(selector gsutil.BeanSelector) ([]*gsbean.BeanDefinition, error) { +func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs_bean.BeanDefinition, error) { - finder := func(fn func(*gsbean.BeanDefinition) bool) ([]*gsbean.BeanDefinition, error) { - var result []*gsbean.BeanDefinition + finder := func(fn func(*gs_bean.BeanDefinition) bool) ([]*gs_bean.BeanDefinition, error) { + var result []*gs_bean.BeanDefinition for _, b := range c.beans { - if b.GetStatus() == gsbean.Resolving || b.GetStatus() == gsbean.Deleted || !fn(b) { + if b.GetStatus() == gs_bean.Resolving || b.GetStatus() == gs_bean.Deleted || !fn(b) { continue } if err := c.resolveBean(b); err != nil { return nil, err } - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { continue } result = append(result, b) @@ -559,9 +559,9 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*gsbean.BeanDefini var t reflect.Type switch st := selector.(type) { - case string, gsbean.BeanDefinition, *gsbean.BeanDefinition: + case string, gs_bean.BeanDefinition, *gs_bean.BeanDefinition: tag := toWireTag(selector) - return finder(func(b *gsbean.BeanDefinition) bool { + return finder(func(b *gs_bean.BeanDefinition) bool { return b.Match(tag.typeName, tag.beanName) }) case reflect.Type: @@ -576,7 +576,7 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*gsbean.BeanDefini } } - return finder(func(b *gsbean.BeanDefinition) bool { + return finder(func(b *gs_bean.BeanDefinition) bool { if b.Type() == t { return true } @@ -592,14 +592,14 @@ func (c *container) findBean(selector gsutil.BeanSelector) ([]*gsbean.BeanDefini // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *container) wireBean(b *gsbean.BeanDefinition, stack *wiringStack) error { +func (c *container) wireBean(b *gs_bean.BeanDefinition, stack *wiringStack) error { - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) } // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.GetStatus() == gsbean.Wired { + if c.state == Refreshed && b.GetStatus() == gs_bean.Wired { return nil } @@ -616,26 +616,26 @@ func (c *container) wireBean(b *gsbean.BeanDefinition, stack *wiringStack) error haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { - d.after(i.Value.(*gsbean.BeanDefinition)) + d.after(i.Value.(*gs_bean.BeanDefinition)) } stack.destroyers.PushBack(b) } stack.pushBack(b) - if b.GetStatus() == gsbean.Creating && b.F != nil { + if b.GetStatus() == gs_bean.Creating && b.F != nil { prev := stack.beans[len(stack.beans)-2] - if prev.GetStatus() == gsbean.Creating { + if prev.GetStatus() == gs_bean.Creating { return errors.New("found circle autowire") } } - if b.GetStatus() >= gsbean.Creating { + if b.GetStatus() >= gs_bean.Creating { stack.popBack() return nil } - b.SetStatus(gsbean.Creating) + b.SetStatus(gs_bean.Creating) // 对当前 bean 的间接依赖项进行注入。 for _, s := range b.GetDepends() { @@ -656,7 +656,7 @@ func (c *container) wireBean(b *gsbean.BeanDefinition, stack *wiringStack) error return err } - b.SetStatus(gsbean.Created) + b.SetStatus(gs_bean.Created) t := v.Type() for _, typ := range b.GetExports() { @@ -684,7 +684,7 @@ func (c *container) wireBean(b *gsbean.BeanDefinition, stack *wiringStack) error } } - b.SetStatus(gsbean.Wired) + b.SetStatus(gs_bean.Wired) stack.popBack() return nil } @@ -694,7 +694,7 @@ type argContext struct { stack *wiringStack } -func (a *argContext) Matches(c gscond.Condition) (bool, error) { +func (a *argContext) Matches(c gs_cond.Condition) (bool, error) { return c.Matches(a.c) } @@ -707,7 +707,7 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { } // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *container) getBeanValue(b *gsbean.BeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *container) getBeanValue(b *gs_bean.BeanDefinition, stack *wiringStack) (reflect.Value, error) { if b.F == nil { return b.Value(), nil @@ -719,9 +719,9 @@ func (c *container) getBeanValue(b *gsbean.BeanDefinition, stack *wiringStack) ( } // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 - if val := out[0]; gsutil.IsBeanType(val.Type()) { + if val := out[0]; gs_util.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 - if !val.IsNil() && val.Kind() == reflect.Interface && gsutil.IsValueType(val.Elem().Type()) { + if !val.IsNil() && val.Kind() == reflect.Interface && gs_util.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) v.Elem().Set(val.Elem()) b.Value().Set(v) @@ -774,7 +774,7 @@ func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar fv := v.Field(i) if !fv.CanInterface() { - fv = gsutil.PatchValue(fv) + fv = gs_util.PatchValue(fv) if !fv.CanInterface() { continue } @@ -882,13 +882,13 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } t := v.Type() - if !gsutil.IsBeanReceiver(t) { + if !gs_util.IsBeanReceiver(t) { return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []*gsbean.BeanDefinition + var foundBeans []*gs_bean.BeanDefinition for _, b := range c.beansByType[t] { - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { continue } if !b.Match(tag.typeName, tag.beanName) { @@ -900,7 +900,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 if t.Kind() == reflect.Interface && tag.beanName != "" { for _, b := range c.beansByName[tag.beanName] { - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { continue } if !b.Type().AssignableTo(t) { @@ -932,7 +932,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []*gsbean.BeanDefinition + var primaryBeans []*gs_bean.BeanDefinition for _, b := range foundBeans { if b.IsPrimary() { @@ -958,7 +958,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result *gsbean.BeanDefinition + var result *gs_bean.BeanDefinition if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -976,7 +976,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*gsbean.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []*gs_bean.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -1006,7 +1006,7 @@ func filterBean(beans []*gsbean.BeanDefinition, tag wireTag, t reflect.Type) (in return -1, fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) } -type byOrder []*gsbean.BeanDefinition +type byOrder []*gs_bean.BeanDefinition func (b byOrder) Len() int { return len(b) } func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } @@ -1020,11 +1020,11 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } et := t.Elem() - if !gsutil.IsBeanReceiver(et) { + if !gs_util.IsBeanReceiver(et) { return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []*gsbean.BeanDefinition + var beans []*gs_bean.BeanDefinition if et.Kind() == reflect.Interface && et.NumMethod() == 0 { beans = c.beans } else { @@ -1032,9 +1032,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } { - var arr []*gsbean.BeanDefinition + var arr []*gs_bean.BeanDefinition for _, b := range beans { - if b.GetStatus() == gsbean.Deleted { + if b.GetStatus() == gs_bean.Deleted { continue } arr = append(arr, b) @@ -1045,9 +1045,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []*gsbean.BeanDefinition - afterAny []*gsbean.BeanDefinition - beforeAny []*gsbean.BeanDefinition + anyBeans []*gs_bean.BeanDefinition + afterAny []*gs_bean.BeanDefinition + beforeAny []*gs_bean.BeanDefinition ) foundAny := false @@ -1076,7 +1076,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]*gsbean.BeanDefinition{}, beans[:index]...) + tmpBeans := append([]*gs_bean.BeanDefinition{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -1085,7 +1085,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*gsbean.BeanDefinition, 0, n) + arr := make([]*gs_bean.BeanDefinition, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) diff --git a/gs/gsioc/gs_bean.go b/gs/internal/gs_ctx/gs_bean.go similarity index 97% rename from gs/gsioc/gs_bean.go rename to gs/internal/gs_ctx/gs_bean.go index 0a87cdc4..1851961a 100755 --- a/gs/gsioc/gs_bean.go +++ b/gs/internal/gs_ctx/gs_bean.go @@ -14,11 +14,11 @@ * limitations under the License. */ -package gsioc +package gs_ctx import ( - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" ) // type beanStatus int8 @@ -351,6 +351,6 @@ type BeanDestroy interface { // } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { - return gsbean.NewBean(objOrCtor, ctorArgs...) +func NewBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *gs_bean.BeanDefinition { + return gs_bean.NewBean(objOrCtor, ctorArgs...) } diff --git a/gs/gsioc/gs_bean_test.go b/gs/internal/gs_ctx/gs_bean_test.go similarity index 94% rename from gs/gsioc/gs_bean_test.go rename to gs/internal/gs_ctx/gs_bean_test.go index 724284ab..19acac4a 100644 --- a/gs/gsioc/gs_bean_test.go +++ b/gs/internal/gs_ctx/gs_bean_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsioc_test +package gs_ctx_test import ( "fmt" @@ -23,18 +23,18 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsbean" - "github.com/go-spring/spring-core/gs/gsioc" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_util" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/stretchr/testify/assert" ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 -func newBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { - return gsioc.NewBean(objOrCtor, ctorArgs...) +func newBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *gs_bean.BeanDefinition { + return gs_ctx.NewBean(objOrCtor, ctorArgs...) } // func TestParseSingletonTag(t *testing.T) { @@ -101,7 +101,7 @@ func TestIsFuncBeanType(t *testing.T) { } for k, v := range data { - ok := gsutil.IsConstructor(k) + ok := gs_util.IsConstructor(k) assert.Equal(t, ok, v) } } @@ -109,7 +109,7 @@ func TestIsFuncBeanType(t *testing.T) { func TestBeanDefinition_Match(t *testing.T) { data := []struct { - bd *gsbean.BeanDefinition + bd *gs_bean.BeanDefinition typeName string beanName string expect bool @@ -184,7 +184,7 @@ func TestObjectBean(t *testing.T) { t.Run("check name && typename", func(t *testing.T) { - data := map[*gsbean.BeanDefinition]struct { + data := map[*gs_bean.BeanDefinition]struct { name string typeName string }{ diff --git a/gs/gsioc/gs_context.go b/gs/internal/gs_ctx/gs_context.go similarity index 86% rename from gs/gsioc/gs_context.go rename to gs/internal/gs_ctx/gs_context.go index 7f11f12b..353461fa 100644 --- a/gs/gsioc/gs_context.go +++ b/gs/internal/gs_ctx/gs_context.go @@ -14,15 +14,15 @@ * limitations under the License. */ -package gsioc +package gs_ctx import ( "errors" "reflect" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_util" ) func (c *container) Keys() []string { @@ -47,12 +47,12 @@ func (c *container) Bind(i interface{}, opts ...conf.BindArg) error { // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的,即未被 // 标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) { +func (c *container) Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) { beans, err := c.findBean(selector) if err != nil { return nil, err } - var ret []gsutil.BeanDefinition + var ret []gs_util.BeanDefinition for _, b := range beans { ret = append(ret, b) } @@ -68,7 +68,7 @@ func (c *container) Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, // 工作模式称为自动模式,否则根据传入的选择器列表进行排序,这种工作模式成为指派模式。 // 该方法和 Find 方法的区别是该方法保证返回的所有 bean 对象都已经完成属性绑定和依 // 赖注入,而 Find 方法只能保证返回的 bean 对象是有效的,即未被标记为删除的。 -func (c *container) Get(i interface{}, selectors ...gsutil.BeanSelector) error { +func (c *container) Get(i interface{}, selectors ...gs_util.BeanSelector) error { if i == nil { return errors.New("i can't be nil") @@ -97,7 +97,7 @@ func (c *container) Get(i interface{}, selectors ...gsutil.BeanSelector) error { // Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 // 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 // 种方式,该函数执行完后都会返回 bean 对象的真实值。 -func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interface{}, error) { +func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) { stack := newWiringStack() @@ -115,9 +115,9 @@ func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gsarg.Arg) (interfac return b.Interface(), nil } -func (c *container) Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, error) { +func (c *container) Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) { - if !gsutil.IsFuncType(reflect.TypeOf(fn)) { + if !gs_util.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") } @@ -129,7 +129,7 @@ func (c *container) Invoke(fn interface{}, args ...gsarg.Arg) ([]interface{}, er // } // }() - r, err := gsarg.Bind(fn, args, 1) + r, err := gs_arg.Bind(fn, args, 1) if err != nil { return nil, err } diff --git a/gs/gsioc/gs_dynamic_test.go b/gs/internal/gs_ctx/gs_dynamic_test.go similarity index 97% rename from gs/gsioc/gs_dynamic_test.go rename to gs/internal/gs_ctx/gs_dynamic_test.go index 678a0c04..c77335f7 100644 --- a/gs/gsioc/gs_dynamic_test.go +++ b/gs/internal/gs_ctx/gs_dynamic_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsioc_test +package gs_ctx_test import ( "encoding/json" @@ -24,7 +24,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/gsioc" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" "github.com/stretchr/testify/assert" ) @@ -45,7 +45,7 @@ func TestDynamic(t *testing.T) { var cfg *DynamicConfig wrapper := new(DynamicConfigWrapper) - c := gsioc.New() + c := gs_ctx.New() c.Provide(func() *DynamicConfig { config := new(DynamicConfig) config.Int.OnValidate(func(v int64) error { diff --git a/gs/gsioc/gs_test.go b/gs/internal/gs_ctx/gs_test.go similarity index 88% rename from gs/gsioc/gs_test.go rename to gs/internal/gs_ctx/gs_test.go index f19b9805..f66e1afa 100755 --- a/gs/gsioc/gs_test.go +++ b/gs/internal/gs_ctx/gs_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsioc_test +package gs_ctx_test import ( "errors" @@ -28,10 +28,10 @@ import ( "time" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs/gsarg" - "github.com/go-spring/spring-core/gs/gscond" - "github.com/go-spring/spring-core/gs/gsioc" - "github.com/go-spring/spring-core/gs/gsutil" + "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_util" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" @@ -40,9 +40,9 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c gsioc.Container, fn func(gsioc.Context)) error { +func runTest(c gs_ctx.Container, fn func(gs_ctx.Context)) error { type PandoraAware struct{} - c.Provide(func(p gsioc.Context) PandoraAware { + c.Provide(func(p gs_ctx.Context) PandoraAware { fn(p) return PandoraAware{} }) @@ -65,7 +65,7 @@ func TestApplicationContext(t *testing.T) { // 自定义数据类型 t.Run("pkg1.SamePkg", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() e := pkg1.SamePkg{} // assert.Panic(t, func() { @@ -81,7 +81,7 @@ func TestApplicationContext(t *testing.T) { }) t.Run("pkg2.SamePkg", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() e := pkg2.SamePkg{} // assert.Panic(t, func() { @@ -141,7 +141,7 @@ type TestObject struct { func TestApplicationContext_AutoWireBeans(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() obj := &TestObject{} c.Object(obj) @@ -149,7 +149,7 @@ func TestApplicationContext_AutoWireBeans(t *testing.T) { b := TestBincoreng{1} c.Object(&b).Name("struct_ptr").Export((*fmt.Stringer)(nil)) - err := runTest(c, func(p gsioc.Context) {}) + err := runTest(c, func(p gs_ctx.Context) {}) assert.Nil(t, err) assert.Equal(t, len(obj.MapTyType), 4) @@ -200,7 +200,7 @@ type Setting struct { } func TestApplicationContext_ValueTag(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("int", int(3)) c.Property("uint", uint(3)) @@ -264,7 +264,7 @@ func (s *PrototypeBeanService) Service(name string) { } func TestApplicationContext_PrototypeBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() greetingService := &GreetingService{} c.Object(greetingService) @@ -324,7 +324,7 @@ type DbConfig struct { } func TestApplicationContext_TypeConverter(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() { p, _ := conf.Load("testdata/config/application.yaml") for _, key := range p.Keys() { @@ -373,7 +373,7 @@ type ProxyGrouper struct { } func TestApplicationContext_NestedBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(MyGrouper)).Export((*Grouper)(nil)) c.Object(new(ProxyGrouper)) err := c.Refresh(false) @@ -390,7 +390,7 @@ type SamePkgHolder struct { } func TestApplicationContext_SameNameBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(SamePkgHolder)) c.Object(&pkg1.SamePkg{}).Export((*Pkg)(nil)) c.Object(&pkg2.SamePkg{}).Export((*Pkg)(nil)) @@ -418,7 +418,7 @@ type DiffPkgHolder struct { } func TestApplicationContext_DiffNameBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&DiffPkgOne{}).Name("same").Export((*Pkg)(nil)) c.Object(&DiffPkgTwo{}).Name("same").Export((*Pkg)(nil)) c.Object(new(DiffPkgHolder)) @@ -428,7 +428,7 @@ func TestApplicationContext_DiffNameBean(t *testing.T) { func TestApplicationContext_LoadProperties(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() p, _ := conf.Load("testdata/config/application.yaml") for _, key := range p.Keys() { @@ -440,7 +440,7 @@ func TestApplicationContext_LoadProperties(t *testing.T) { c.Property(key, p.Get(key)) } - err := runTest(c, func(ctx gsioc.Context) { + err := runTest(c, func(ctx gs_ctx.Context) { assert.Equal(t, ctx.Prop("yaml.list[0]"), "1") assert.Equal(t, ctx.Prop("yaml.list[1]"), "2") assert.Equal(t, ctx.Prop("spring.application.name"), "test") @@ -451,8 +451,8 @@ func TestApplicationContext_LoadProperties(t *testing.T) { func TestApplicationContext_Get(t *testing.T) { t.Run("panic", func(t *testing.T) { - c := gsioc.New() - err := runTest(c, func(p gsioc.Context) { + c := gs_ctx.New() + err := runTest(c, func(p gs_ctx.Context) { { var s fmt.Stringer err := p.Get(s) @@ -468,11 +468,11 @@ func TestApplicationContext_Get(t *testing.T) { }) t.Run("success", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).Export((*Grouper)(nil)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var two *BeanTwo err := p.Get(&two) @@ -617,7 +617,7 @@ func NewPtrStudent(teacher Teacher, room string) *Student { } func TestApplicationContext_RegisterBeanFn(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("room", "Class 3 Grade 1") @@ -633,7 +633,7 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { fmt.Println(teacher.Course()) }).Name("newTeacher") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var st1 *Student err := p.Get(&st1, "st1") @@ -672,9 +672,9 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { func TestApplicationContext_Profile(t *testing.T) { t.Run("bean:_c:", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -683,10 +683,10 @@ func TestApplicationContext_Profile(t *testing.T) { }) t.Run("bean:_c:test", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("spring.profiles.active", "test") c.Object(&BeanZero{5}) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -700,7 +700,7 @@ type BeanFour struct{} func TestApplicationContext_DependsOn(t *testing.T) { t.Run("random", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)) @@ -710,12 +710,12 @@ func TestApplicationContext_DependsOn(t *testing.T) { t.Run("dependsOn", func(t *testing.T) { - dependsOn := []gsutil.BeanSelector{ + dependsOn := []gs_util.BeanSelector{ (*BeanOne)(nil), // 通过类型定义查找 "github.com/go-spring/spring-core/gs/gs_test.BeanZero:BeanZero", } - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)).DependsOn(dependsOn...) @@ -727,7 +727,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { func TestApplicationContext_Primary(t *testing.T) { t.Run("duplicate", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}) c.Object(new(BeanOne)) @@ -737,7 +737,7 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("duplicate", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) // primary 是在多个候选 bean 里面选择,而不是允许同名同类型的两个 bean c.Object(&BeanZero{6}).Primary() @@ -748,11 +748,11 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("not primary", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -762,12 +762,12 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("primary", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}).Name("zero_6").Primary() c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -782,7 +782,7 @@ type FuncObj struct { } func TestDefaultProperties_WireFunc(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(func(int) int { return 6 }) obj := new(FuncObj) c.Object(obj) @@ -827,10 +827,10 @@ func (m localManager) Cluster() string { func TestApplicationContext_RegisterBeanFn2(t *testing.T) { t.Run("ptr manager", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("manager.version", "1.0.0") c.Provide(NewPtrManager) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var m Manager err := p.Get(&m) @@ -846,13 +846,13 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("manager.version", "1.0.0") bd := c.Provide(NewManager) assert.Equal(t, bd.BeanName(), "NewManager") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var m Manager err := p.Get(&m) @@ -866,7 +866,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetError) err := c.Refresh(false) @@ -874,7 +874,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error nil", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetErrorNil) err := c.Refresh(false) @@ -882,7 +882,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return nil", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("manager.version", "1.0.0") c.Provide(NewNullPtrManager) err := c.Refresh(false) @@ -939,9 +939,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(callDestroy)).Init((*callDestroy).Init) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -953,16 +953,16 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init with error", func(t *testing.T) { { - c := gsioc.New() + c := gs_ctx.New() c.Object(&callDestroy{i: 1}).Init((*callDestroy).InitWithError) err := c.Refresh(false) assert.Error(t, err, "error") } - c := gsioc.New() + c := gs_ctx.New() c.Property("int", 0) c.Object(&callDestroy{}).Init((*callDestroy).InitWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -972,9 +972,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call interface init", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Provide(func() destroyable { return new(callDestroy) }).Init(destroyable.Init) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -986,16 +986,16 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call interface init with error", func(t *testing.T) { { - c := gsioc.New() + c := gs_ctx.New() c.Provide(func() destroyable { return &callDestroy{i: 1} }).Init(destroyable.InitWithError) err := c.Refresh(false) assert.Error(t, err, "error") } - c := gsioc.New() + c := gs_ctx.New() c.Property("int", 0) c.Provide(func() destroyable { return &callDestroy{} }).Init(destroyable.InitWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1005,9 +1005,9 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested init", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(nestedCallDestroy)).Init((*nestedCallDestroy).Init) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *nestedCallDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1017,11 +1017,11 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested interface init", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&nestedDestroyable{ destroyable: new(callDestroy), }).Init((*nestedDestroyable).Init) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *nestedDestroyable err := p.Get(&d) assert.Nil(t, err) @@ -1036,10 +1036,10 @@ type RecoresCluster struct { } func TestApplicationContext_ValueBincoreng(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var cluster *RecoresCluster err := p.Get(&cluster) fmt.Println(cluster) @@ -1051,17 +1051,17 @@ func TestApplicationContext_ValueBincoreng(t *testing.T) { func TestApplicationContext_Collect(t *testing.T) { t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&struct { Events []ServerInterface `autowire:""` }{}) - err := runTest(c, func(ctx gsioc.Context) {}) + err := runTest(c, func(ctx gs_ctx.Context) {}) assert.Error(t, err, "no beans collected for \"\"") }) t.Run("", func(t *testing.T) { - c := gsioc.New() - err := runTest(c, func(ctx gsioc.Context) { + c := gs_ctx.New() + err := runTest(c, func(ctx gs_ctx.Context) { var Events []ServerInterface err := ctx.Get(&Events) assert.Error(t, err, "no beans collected for \"\"") @@ -1070,17 +1070,17 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&struct { Events []ServerInterface `autowire:"?"` }{}) - err := runTest(c, func(ctx gsioc.Context) {}) + err := runTest(c, func(ctx gs_ctx.Context) {}) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - c := gsioc.New() - err := runTest(c, func(ctx gsioc.Context) { + c := gs_ctx.New() + err := runTest(c, func(ctx gs_ctx.Context) { var Events []ServerInterface err := ctx.Get(&Events, "?") assert.Nil(t, err) @@ -1089,11 +1089,11 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("one") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var rcs []*RecoresCluster err := p.Get(&rcs) assert.Nil(t, err) @@ -1104,12 +1104,12 @@ func TestApplicationContext_Collect(t *testing.T) { t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("a").Order(1) c.Object(new(RecoresCluster)).Name("b").Order(2) - intBean := c.Provide(func(p gsioc.Context) func() { + intBean := c.Provide(func(p gs_ctx.Context) func() { var rcs []*RecoresCluster err := p.Get(&rcs) @@ -1229,10 +1229,10 @@ func TestOptionPattern(t *testing.T) { func TestOptionConstructorArg(t *testing.T) { t.Run("option default", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1244,10 +1244,10 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withClassName", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("president", "CaiYuanPei") - c.Provide(NewClassRoom, gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1260,13 +1260,13 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("class_name", "二年级03班") c.Property("president", "CaiYuanPei") - c.Provide(NewClassRoom, gsarg.Option(withStudents)) + c.Provide(NewClassRoom, gs_arg.Option(withStudents)) c.Object(new(Student)).Name("Student1") c.Object(new(Student)).Name("Student2") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1279,19 +1279,19 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents withClassName", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("class_name", "二年级06班") c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, - gsarg.Option(withStudents), - gsarg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), - gsarg.Option(withBuilder, gsarg.Provide(func(param string) *ClassBuilder { + gs_arg.Option(withStudents), + gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), + gs_arg.Option(withBuilder, gs_arg.Provide(func(param string) *ClassBuilder { return &ClassBuilder{param: param} - }, gsarg.Value("1"))), + }, gs_arg.Value("1"))), ) c.Object(&Student{}).Name("Student1") c.Object(&Student{}).Name("Student2") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1348,11 +1348,11 @@ type Service struct { func TestApplicationContext_RegisterMethodBean(t *testing.T) { t.Run("method bean", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -1371,11 +1371,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean condition", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") - parent := c.Object(new(Server)).On(gscond.Not(gscond.OK())) + parent := c.Object(new(Server)).On(gs_cond.Not(gs_cond.OK())) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -1390,11 +1390,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean arg", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) c.Provide((*Server).ConsumerArg, parent, "${i:=9}") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -1412,12 +1412,12 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean wire to other bean", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") parent := c.Provide(NewServerInterface) c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var si ServerInterface err := p.Get(&si) @@ -1462,7 +1462,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { } }() - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)).DependsOn("Service") c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") @@ -1477,10 +1477,10 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean autowire", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) assert.Nil(t, err) @@ -1490,11 +1490,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*Server)(nil)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -1512,7 +1512,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type error", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*int)(nil)) @@ -1521,11 +1521,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "Server") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -1543,7 +1543,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId error", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "NULL") @@ -1563,7 +1563,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { Complex complex64 // `value:"${complex}"` } - c := gsioc.New() + c := gs_ctx.New() conf.RegisterConverter(func(v string) (level, error) { if v == "debug" { @@ -1599,7 +1599,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { // 直接创建的 Object 直接发生循环依赖是没有关系的。 t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Object(new(CircleC)) @@ -1608,7 +1608,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Provide(func() *CircleC { @@ -1619,7 +1619,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(CircleA)) c.Provide(func() *CircleB { return new(CircleB) @@ -1632,7 +1632,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Provide(func(b *CircleB) *CircleA { return new(CircleA) }) @@ -1711,12 +1711,12 @@ func NewNilVarObj(i interface{}, options ...VarOptionFunc) *VarObj { func TestApplicationContext_RegisterOptionBean(t *testing.T) { t.Run("nil param 0", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewNilVarObj, gsarg.Nil()) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewNilVarObj, gs_arg.Nil()) + err := runTest(c, func(p gs_ctx.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1727,12 +1727,12 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 1", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewVarObj, "${var.obj}", gsarg.Option(withVar, "v1")) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewVarObj, "${var.obj}", gs_arg.Option(withVar, "v1")) + err := runTest(c, func(p gs_ctx.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1744,12 +1744,12 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 2", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") - c.Provide(NewVarObj, gsarg.Value("description"), gsarg.Option(withVar, "v1", "v2")) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewVarObj, gs_arg.Value("description"), gs_arg.Option(withVar, "v1", "v2")) + err := runTest(c, func(p gs_ctx.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1762,11 +1762,11 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) - c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1")) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1")) + err := runTest(c, func(p gs_ctx.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1776,11 +1776,11 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) - c.Provide(NewVarInterfaceObj, gsarg.Option(withVarInterface, "v1", "v2")) - err := runTest(c, func(p gsioc.Context) { + c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1", "v2")) + err := runTest(c, func(p gs_ctx.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1818,7 +1818,7 @@ func TestApplicationContext_Close(t *testing.T) { t.Run("call destroy fn", func(t *testing.T) { called := false - c := gsioc.New() + c := gs_ctx.New() c.Object(func() {}).Destroy(func(f func()) { called = true }) err := c.Refresh(false) assert.Nil(t, err) @@ -1828,10 +1828,10 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call destroy", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() d := new(callDestroy) c.Object(d).Destroy((*callDestroy).Destroy) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1845,10 +1845,10 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gsioc.New() + c := gs_ctx.New() d := &callDestroy{i: 1} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1860,10 +1860,10 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - c := gsioc.New() + c := gs_ctx.New() d := &callDestroy{} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1875,9 +1875,9 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call interface destroy", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() bd := c.Provide(func() destroyable { return new(callDestroy) }).Destroy(destroyable.Destroy) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1892,9 +1892,9 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gsioc.New() + c := gs_ctx.New() bd := c.Provide(func() destroyable { return &callDestroy{i: 1} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1907,10 +1907,10 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - c := gsioc.New() + c := gs_ctx.New() c.Property("int", 0) bd := c.Provide(func() destroyable { return &callDestroy{} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1935,12 +1935,12 @@ type PtrNestedAutowireBean struct { } func TestApplicationContext_NestedAutowireBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(NestedAutowireBean)) c.Object(&PtrNestedAutowireBean{ SubNestedAutowireBean: new(SubNestedAutowireBean), }) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *NestedAutowireBean err := p.Get(&b) @@ -1977,13 +1977,13 @@ func TestApplicationContext_NestValueField(t *testing.T) { t.Run("private", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("sdk.wx.auto-create", true) c.Property("sdk.wx.enable", true) c.Object(new(wxChannel)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var channel *wxChannel err := p.Get(&channel) assert.Nil(t, err) @@ -1994,11 +1994,11 @@ func TestApplicationContext_NestValueField(t *testing.T) { }) t.Run("public", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("sdk.wx.auto-create", true) c.Property("sdk.wx.enable", true) c.Object(new(WXChannel)) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var channel *WXChannel err := p.Get(&channel) assert.Nil(t, err) @@ -2012,7 +2012,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { func TestApplicationContext_FnArgCollectBean(t *testing.T) { t.Run("interface type", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Provide(newHistoryTeacher("t1")).Name("t1").Export((*Teacher)(nil)) c.Provide(newHistoryTeacher("t2")).Name("t2").Export((*Teacher)(nil)) c.Provide(func(teachers []Teacher) func() { @@ -2043,7 +2043,7 @@ func (_ *filterImpl) Filter(input string) string { func TestApplicationContext_BeanCache(t *testing.T) { t.Run("not implement interface", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(func() {}).Export((*filter)(nil)) err := c.Refresh(false) assert.Error(t, err, "doesn't implement interface gs_test.filter") @@ -2056,7 +2056,7 @@ func TestApplicationContext_BeanCache(t *testing.T) { F2 filter `autowire:"f2"` } - c := gsioc.New() + c := gs_ctx.New() c.Provide(func() filter { return new(filterImpl) }).Name("f1") c.Object(new(filterImpl)).Export((*filter)(nil)).Name("f2") c.Object(&server) @@ -2077,7 +2077,7 @@ func (i Integer) Value() int { } func TestApplicationContext_IntInterface(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Provide(func() IntInterface { return Integer(5) }) err := c.Refresh(false) assert.Nil(t, err) @@ -2104,7 +2104,7 @@ func TestApplicationContext_Properties(t *testing.T) { t.Run("array properties", func(t *testing.T) { b := new(ArrayProperties) - c := gsioc.New() + c := gs_ctx.New() c.Object(b) err := c.Refresh(false) assert.Nil(t, err) @@ -2119,7 +2119,7 @@ func TestApplicationContext_Properties(t *testing.T) { MapA map[string]string `value:"${map_a:=}"` }{} - c := gsioc.New() + c := gs_ctx.New() c.Property("map_a.nba", "nba") c.Property("map_a.cba", "cba") c.Property("int_a", "3") @@ -2154,7 +2154,7 @@ func TestApplicationContext_Destroy(t *testing.T) { destroyIndex := 0 destroyArray := []int{0, 0, 0, 0} - c := gsioc.New() + c := gs_ctx.New() c.Object(new(FirstDestroy)).Destroy( func(_ *FirstDestroy) { fmt.Println("::FirstDestroy") @@ -2195,10 +2195,10 @@ type ObjFactory struct{} func (factory *ObjFactory) NewObj(i int) *Obj { return &Obj{i: i} } func TestApplicationContext_CreateBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&ObjFactory{}) - err := runTest(c, func(p gsioc.Context) { - b, err := p.Wire((*ObjFactory).NewObj, gsarg.R1("${i:=5}")) + err := runTest(c, func(p gs_ctx.Context) { + b, err := p.Wire((*ObjFactory).NewObj, gs_arg.R1("${i:=5}")) fmt.Println(b, err) }) assert.Nil(t, err) @@ -2208,17 +2208,17 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("bean:test_ctx:", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() - c.Object(&BeanZero{5}).On(gscond. + c.Object(&BeanZero{5}).On(gs_cond. OnProfile("test"). And(). OnMissingBean("null"). And(). - On(gscond.OK()), + On(gs_cond.OK()), ) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2227,10 +2227,10 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:test", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("spring.profiles.active", "test") - c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) - err := runTest(c, func(p gsioc.Context) { + c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) + err := runTest(c, func(p gs_ctx.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -2239,10 +2239,10 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:stable", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("spring.profiles.active", "stable") - c.Object(&BeanZero{5}).On(gscond.OnProfile("test")) - err := runTest(c, func(p gsioc.Context) { + c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) + err := runTest(c, func(p gs_ctx.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2252,14 +2252,14 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("option withClassName Condition", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("president", "CaiYuanPei") c.Property("class_floor", 2) - c.Provide(NewClassRoom, gsarg.Option(withClassName, + c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", - ).On(gscond.OnProperty("class_name_enable"))) - err := runTest(c, func(p gsioc.Context) { + ).On(gs_cond.OnProperty("class_name_enable"))) + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2272,16 +2272,16 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("option withClassName Apply", func(t *testing.T) { - onProperty := gscond.OnProperty("class_name_enable") - c := gsioc.New() + onProperty := gs_cond.OnProperty("class_name_enable") + c := gs_ctx.New() c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, - gsarg.Option(withClassName, + gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", ).On(onProperty), ) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2294,11 +2294,11 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("method bean cond", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.ID()).On(gscond.OnProperty("consumer.enable")) - err := runTest(c, func(p gsioc.Context) { + c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) + err := runTest(c, func(p gs_ctx.Context) { var s *Server err := p.Get(&s) @@ -2332,17 +2332,17 @@ func TestDefaultSpringContext(t *testing.T) { // } func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() - c1 := gscond.OnProperty("null", gscond.MatchIfMissing()).Or().OnProfile("test") + c1 := gs_cond.OnProperty("null", gs_cond.MatchIfMissing()).Or().OnProfile("test") - c.Object(&BeanZero{5}).On(gscond.On(c1).And().OnMissingBean("null")) - c.Object(new(BeanOne)).On(gscond.On(c1).And().OnMissingBean("null")) + c.Object(&BeanZero{5}).On(gs_cond.On(c1).And().OnMissingBean("null")) + c.Object(new(BeanOne)).On(gs_cond.On(c1).And().OnMissingBean("null")) - c.Object(new(BeanTwo)).On(gscond.OnBean("BeanOne")) - c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnBean("Null")) + c.Object(new(BeanTwo)).On(gs_cond.OnBean("BeanOne")) + c.Object(new(BeanTwo)).Name("another_two").On(gs_cond.OnBean("Null")) - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { var two *BeanTwo err := p.Get(&two) @@ -2356,12 +2356,12 @@ func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { for i := 0; i < 20; i++ { // 测试 Find 无需绑定,不要排序 - c := gsioc.New() + c := gs_ctx.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) - c.Object(new(BeanTwo)).On(gscond.OnMissingBean("BeanOne")) - c.Object(new(BeanTwo)).Name("another_two").On(gscond.OnMissingBean("Null")) - err := runTest(c, func(p gsioc.Context) { + c.Object(new(BeanTwo)).On(gs_cond.OnMissingBean("BeanOne")) + c.Object(new(BeanTwo)).Name("another_two").On(gs_cond.OnMissingBean("Null")) + err := runTest(c, func(p gs_ctx.Context) { var two *BeanTwo err := p.Get(&two) @@ -2557,10 +2557,10 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { func TestApplicationContext_Invoke(t *testing.T) { t.Run("not run", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(func() {}) c.Property("version", "v0.0.1") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { _, _ = p.Invoke(func(f func(), version string) { fmt.Println("version:", version) }, "", "${version}") @@ -2569,11 +2569,11 @@ func TestApplicationContext_Invoke(t *testing.T) { }) t.Run("run", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(func() {}) c.Property("version", "v0.0.1") c.Property("spring.profiles.active", "dev") - err := runTest(c, func(p gsioc.Context) { + err := runTest(c, func(p gs_ctx.Context) { fn := func(f func(), version string) { fmt.Println("version:", version) } @@ -2589,7 +2589,7 @@ type emptyStructB struct{} func TestEmptyStruct(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() objA := &emptyStructA{} c.Object(objA) objB := &emptyStructB{} @@ -2609,11 +2609,11 @@ func TestMapCollection(t *testing.T) { } t.Run("", func(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(&mapValue{"a"}).Name("a").Order(1) c.Object(&mapValue{"b"}).Name("b").Order(2) - c.Object(&mapValue{"c"}).Name("c").On(gscond.Not(gscond.OK())) - err := runTest(c, func(p gsioc.Context) { + c.Object(&mapValue{"c"}).Name("c").On(gs_cond.Not(gs_cond.OK())) + err := runTest(c, func(p gs_ctx.Context) { var vSlice []*mapValue err := p.Get(&vSlice) @@ -2647,7 +2647,7 @@ func newCircularB() *circularB { func TestLazy(t *testing.T) { for i := 0; i < 1; i++ { - c := gsioc.New() + c := gs_ctx.New() c.Property("spring.main.allow-circular-references", "true") c.Provide(newCircularA) c.Provide(newCircularB) @@ -2665,7 +2665,7 @@ func TestLazy(t *testing.T) { type memory struct { } -func (m *memory) OnInit(ctx gsioc.Context) error { +func (m *memory) OnInit(ctx gs_ctx.Context) error { fmt.Println("memory.OnInit") return nil } @@ -2678,7 +2678,7 @@ type table struct { _ *memory `autowire:""` } -func (t *table) OnInit(ctx gsioc.Context) error { +func (t *table) OnInit(ctx gs_ctx.Context) error { fmt.Println("table.OnInit") return nil } @@ -2688,7 +2688,7 @@ func (t *table) OnDestroy() { } func TestDestroyDependence(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Object(new(memory)) c.Object(new(table)).Name("aaa") c.Object(new(table)).Name("bbb") @@ -2697,7 +2697,7 @@ func TestDestroyDependence(t *testing.T) { } type ContextAware struct { - gsioc.ContextAware + gs_ctx.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -2705,7 +2705,7 @@ func (c *ContextAware) Echo(str string) string { } func TestContextAware(t *testing.T) { - c := gsioc.New() + c := gs_ctx.New() c.Property("prefix", "hello") b := c.Object(new(ContextAware)) err := c.Refresh(false) diff --git a/gs/gsutil/type.go b/gs/internal/gs_util/type.go similarity index 99% rename from gs/gsutil/type.go rename to gs/internal/gs_util/type.go index 547e5778..23d50e6d 100644 --- a/gs/gsutil/type.go +++ b/gs/internal/gs_util/type.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsutil +package gs_util import ( "context" diff --git a/gs/gsutil/value.go b/gs/internal/gs_util/value.go similarity index 99% rename from gs/gsutil/value.go rename to gs/internal/gs_util/value.go index 83c5647a..68320817 100644 --- a/gs/gsutil/value.go +++ b/gs/internal/gs_util/value.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gsutil +package gs_util import ( "reflect" diff --git a/gs/version/version.go b/gs/version/version.go deleted file mode 100644 index b65c9fed..00000000 --- a/gs/version/version.go +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package version - -const ( - Version = "go-spring@v1.1.3" - Website = "https://go-spring.com/" -) From c04216262658248ecf653cbb73e7a6034c2e5a52 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 30 Dec 2024 13:53:11 +0800 Subject: [PATCH 12/75] code refactor --- gs/gs.go | 16 +- gs/gstest/gstest.go | 8 +- gs/internal/gs/bean.go | 351 +++++++++++++++ gs/internal/gs/gs.go | 81 ++++ gs/internal/gs_app/app.go | 25 +- gs/internal/gs_app/app_bootstrap.go | 9 +- gs/internal/gs_arg/arg.go | 63 +-- gs/internal/gs_arg/arg_mock.go | 8 +- gs/internal/gs_arg/arg_test.go | 11 +- gs/internal/gs_bean/bean.go | 665 ++++++++++++++-------------- gs/internal/gs_cond/cond.go | 64 +-- gs/internal/gs_cond/cond_mock.go | 15 +- gs/internal/gs_ctx/gs.go | 169 +++---- gs/internal/gs_ctx/gs_bean.go | 193 ++++---- gs/internal/gs_ctx/gs_bean_test.go | 9 +- gs/internal/gs_ctx/gs_context.go | 5 +- gs/internal/gs_ctx/gs_test.go | 3 +- 17 files changed, 1027 insertions(+), 668 deletions(-) create mode 100644 gs/internal/gs/bean.go diff --git a/gs/gs.go b/gs/gs.go index 8fcaa7f8..403b2150 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -19,16 +19,20 @@ package gs import ( "reflect" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) +type ( + Arg = gs.Arg + Context = gs.Context +) + var app = gs_app.NewApp() // Start 启动程序。 -func Start() (gs_ctx.Context, error) { +func Start() (gs.Context, error) { return app.Start() } @@ -63,16 +67,16 @@ func Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { +func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.Accept(b) } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *gs_bean.BeanDefinition { +func Object(i interface{}) *gs.BeanDefinition { return app.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { +func Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return app.Accept(gs_ctx.NewBean(ctor, args...)) } diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 2865e616..df4fcbb6 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -21,12 +21,10 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" "github.com/go-spring/spring-core/gs/internal/gs_util" ) -var ctx gs_ctx.Context +var ctx gs.Context // Init 初始化测试环境 func Init() error { @@ -70,11 +68,11 @@ func Get(i interface{}, selectors ...gs_util.BeanSelector) error { } // Wire 注入对象 -func Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) { +func Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { return ctx.Wire(objOrCtor, ctorArgs...) } // Invoke 调用函数 -func Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) { +func Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { return ctx.Invoke(fn, args...) } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go new file mode 100644 index 00000000..0b92c105 --- /dev/null +++ b/gs/internal/gs/bean.go @@ -0,0 +1,351 @@ +package gs + +import ( + "errors" + "fmt" + "reflect" + + "github.com/go-spring/spring-core/gs/internal/gs_util" +) + +type BeanStatus int8 + +const ( + Deleted = BeanStatus(-1) // 已删除 + Default = BeanStatus(iota) // 未处理 + Resolving // 正在决议 + Resolved // 已决议 + Creating // 正在创建 + Created // 已创建 + Wired // 注入完成 +) + +func GetStatusString(status BeanStatus) string { + switch status { + case Deleted: + return "Deleted" + case Default: + return "Default" + case Resolving: + return "Resolving" + case Resolved: + return "Resolved" + case Creating: + return "Creating" + case Created: + return "Created" + case Wired: + return "Wired" + default: + return "" + } +} + +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + V reflect.Value // 值 + T reflect.Type // 类型 + F Callable // 构造函数 + + file string // 注册点所在文件 + line int // 注册点所在行数 + + name string // 名称 + typeName string // 原始类型的全限定名 + status BeanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []gs_util.BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 +} + +func (d *BeanDefinition) GetName() string { return d.name } +func (d *BeanDefinition) GetTypeName() string { return d.typeName } +func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } +func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } +func (d *BeanDefinition) IsPrimary() bool { return d.primary } +func (d *BeanDefinition) IsMethod() bool { return d.method } +func (d *BeanDefinition) GetCond() Condition { return d.cond } +func (d *BeanDefinition) GetOrder() float32 { return d.order } +func (d *BeanDefinition) GetInit() interface{} { return d.init } +func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } +func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } +func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } + +// Type 返回 bean 的类型。 +func (d *BeanDefinition) Type() reflect.Type { + return d.T +} + +// Value 返回 bean 的值。 +func (d *BeanDefinition) Value() reflect.Value { + return d.V +} + +// Interface 返回 bean 的真实值。 +func (d *BeanDefinition) Interface() interface{} { + return d.V.Interface() +} + +// ID 返回 bean 的 ID 。 +func (d *BeanDefinition) ID() string { + return d.typeName + ":" + d.name +} + +// BeanName 返回 bean 的名称。 +func (d *BeanDefinition) BeanName() string { + return d.name +} + +// TypeName 返回 bean 的原始类型的全限定名。 +func (d *BeanDefinition) TypeName() string { + return d.typeName +} + +// Created 返回是否已创建。 +func (d *BeanDefinition) Created() bool { + return d.status >= Created +} + +// Wired 返回 bean 是否已经注入。 +func (d *BeanDefinition) Wired() bool { + return d.status == Wired +} + +// FileLine 返回 bean 的注册点。 +func (d *BeanDefinition) FileLine() string { + return fmt.Sprintf("%s:%d", d.file, d.line) +} + +// GetClass 返回 bean 的类型描述。 +func (d *BeanDefinition) GetClass() string { + if d.F == nil { + return "object bean" + } + return "constructor bean" +} + +func (d *BeanDefinition) String() string { + return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) +} + +// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 +func (d *BeanDefinition) Match(typeName string, beanName string) bool { + + typeIsSame := false + if typeName == "" || d.typeName == typeName { + typeIsSame = true + } + + nameIsSame := false + if beanName == "" || d.name == beanName { + nameIsSame = true + } + + return typeIsSame && nameIsSame +} + +// Name 设置 bean 的名称。 +func (d *BeanDefinition) Name(name string) *BeanDefinition { + d.name = name + return d +} + +// On 设置 bean 的 Condition。 +func (d *BeanDefinition) On(cond Condition) *BeanDefinition { + d.cond = cond + return d +} + +// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 +func (d *BeanDefinition) Order(order float32) *BeanDefinition { + d.order = order + return d +} + +// DependsOn 设置 bean 的间接依赖项。 +func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { + d.depends = append(d.depends, selectors...) + return d +} + +// Primary 设置 bean 为主版本。 +func (d *BeanDefinition) Primary() *BeanDefinition { + d.primary = true + return d +} + +// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 +// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 +func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { + if !gs_util.IsFuncType(fnType) { + return false + } + if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { + return false + } + return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) +} + +// Init 设置 bean 的初始化函数。 +func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { + if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { + d.init = fn + return d + } + panic(errors.New("init should be func(bean) or func(bean)error")) +} + +// Destroy 设置 bean 的销毁函数。 +func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { + if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { + d.destroy = fn + return d + } + panic(errors.New("destroy should be func(bean) or func(bean)error")) +} + +// Export 设置 bean 的导出接口。 +func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { + err := d.export(exports...) + if err != nil { + panic(err) + } + return d +} + +func (d *BeanDefinition) export(exports ...interface{}) error { + for _, o := range exports { + var typ reflect.Type + if t, ok := o.(reflect.Type); ok { + typ = t + } else { // 处理 (*error)(nil) 这种导出形式 + typ = gs_util.Indirect(reflect.TypeOf(o)) + } + if typ.Kind() != reflect.Interface { + return errors.New("only interface type can be exported") + } + exported := false + for _, export := range d.exports { + if typ == export { + exported = true + break + } + } + if exported { + continue + } + d.exports = append(d.exports, typ) + } + return nil +} + +//// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +//func NewBean(objOrCtor interface{}, ctorArgs ...Arg) *BeanDefinition { +// +// var v reflect.Value +// var fromValue bool +// var method bool +// var name string +// +// switch i := objOrCtor.(type) { +// case reflect.Value: +// fromValue = true +// v = i +// default: +// v = reflect.ValueOf(i) +// } +// +// t := v.Type() +// if !gs_util.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// if !v.IsValid() || v.IsNil() { +// panic(errors.New("bean can't be nil")) +// } +// +// const skip = 2 +// var f Callable +// _, file, line, _ := runtime.Caller(skip) +// +// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 +// if !fromValue && t.Kind() == reflect.Func { +// +// if !gs_util.IsConstructor(t) { +// t1 := "func(...)bean" +// t2 := "func(...)(bean, error)" +// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) +// } +// +// var err error +// f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) +// if err != nil { +// panic(err) +// } +// +// out0 := t.Out(0) +// v = reflect.New(out0) +// if gs_util.IsBeanType(out0) { +// v = v.Elem() +// } +// +// t = v.Type() +// if !gs_util.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 +// fnPtr := reflect.ValueOf(objOrCtor).Pointer() +// fnInfo := runtime.FuncForPC(fnPtr) +// funcName := fnInfo.Name() +// name = funcName[strings.LastIndex(funcName, "/")+1:] +// name = name[strings.Index(name, ".")+1:] +// if name[0] == '(' { +// name = name[strings.Index(name, ".")+1:] +// } +// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 +// } +// +// if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { +// panic(errors.New("bean should be *val but not *ref")) +// } +// +// // Type.String() 一般返回 *pkg.Type 形式的字符串, +// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 +// if name == "" { +// s := strings.Split(t.String(), ".") +// name = strings.TrimPrefix(s[len(s)-1], "*") +// } +// +// return &BeanDefinition{ +// T: t, +// V: v, +// F: f, +// name: name, +// typeName: gs_util.TypeName(t), +// status: Default, +// method: method, +// file: file, +// line: line, +// } +//} + +// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { + return &BeanDefinition{ + T: t, + V: v, + F: f, + name: name, + typeName: gs_util.TypeName(t), + status: Default, + method: method, + file: file, + line: line, + } +} diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 1e8a434c..aa87951f 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -1 +1,82 @@ package gs + +import ( + "context" + "reflect" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/dync" + "github.com/go-spring/spring-core/gs/internal/gs_util" +) + +// ConditionContext defines some methods of IoC container that conditions use. +type ConditionContext interface { + // Has returns whether the IoC container has a property. + Has(key string) bool + // Prop returns the property's value when the IoC container has it, or + // returns empty string when the IoC container doesn't have it. + Prop(key string, opts ...conf.GetOption) string + // Find returns bean definitions that matched with the bean selector. + Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) +} + +// Condition is used when registering a bean to determine whether it's valid. +type Condition interface { + Matches(ctx ConditionContext) (bool, error) +} + +// ArgContext defines some methods of IoC container that Callable use. +type ArgContext interface { + // Matches returns true when the Condition returns true, + // and returns false when the Condition returns false. + Matches(c Condition) (bool, error) + // Bind binds properties value by the "value" tag. + Bind(v reflect.Value, tag string) error + // Wire wires dependent beans by the "autowire" tag. + Wire(v reflect.Value, tag string) error +} + +// Arg 用于为函数参数提供绑定值。可以是 bean.Selector 类型,表示注入 bean ; +// 可以是 ${X:=Y} 形式的字符串,表示属性绑定或者注入 bean ;可以是 ValueArg +// 类型,表示不从 IoC 容器获取而是用户传入的普通值;可以是 IndexArg 类型,表示 +// 带有下标的参数绑定;可以是 *optionArg 类型,用于为 Option 方法提供参数绑定。 +type Arg interface{} + +type Callable interface { + Arg(i int) (Arg, bool) + In(i int) (reflect.Type, bool) + Call(ctx ArgContext) ([]reflect.Value, error) +} + +type Container interface { + Context() context.Context + Properties() *dync.Properties + Property(key string, value interface{}) + Object(i interface{}) *BeanDefinition + Provide(ctor interface{}, args ...Arg) *BeanDefinition + Accept(b *BeanDefinition) *BeanDefinition + OnProperty(key string, fn interface{}) + Refresh(autoClear bool) error + Close() +} + +// Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 +// 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 +// 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, +// 都可以转换为启动阶段的方案以提高程序的性能。 +// 另一方面,为了统一 Container 和 App 两种启动方式下这些方法的使用方式,需要提取 +// 出一个可共用的接口来,也就是说,无论程序是 Container 方式启动还是 App 方式启动, +// 都可以在需要使用这些方法的地方注入一个 Context 对象而不是 Container 对象或者 +// App 对象,从而实现使用方式的统一。 +type Context interface { + Context() context.Context + Keys() []string + Has(key string) bool + Prop(key string, opts ...conf.GetOption) string + Resolve(s string) (string, error) + Bind(i interface{}, opts ...conf.BindArg) error + Get(i interface{}, selectors ...gs_util.BeanSelector) error + Wire(objOrCtor interface{}, ctorArgs ...Arg) (interface{}, error) + Invoke(fn interface{}, args ...Arg) ([]interface{}, error) + Go(fn func(ctx context.Context)) +} diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 6a3aaea6..05e0dbb4 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -25,8 +25,7 @@ import ( "strings" "syscall" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) @@ -40,12 +39,12 @@ const SpringBannerVisible = "spring.banner.visible" // AppRunner 命令行启动器接口 type AppRunner interface { - Run(ctx gs_ctx.Context) + Run(ctx gs.Context) } // AppEvent 应用运行过程中的事件 type AppEvent interface { - OnAppStart(ctx gs_ctx.Context) // 应用启动的事件 + OnAppStart(ctx gs.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -57,7 +56,7 @@ type tempApp struct { type App struct { *tempApp - c gs_ctx.Container + c gs.Container b *Bootstrapper exitChan chan struct{} @@ -80,7 +79,7 @@ func (app *App) Banner(banner string) { app.banner = banner } -func (app *App) Start() (gs_ctx.Context, error) { +func (app *App) Start() (gs.Context, error) { app.Object(app) @@ -110,23 +109,23 @@ func (app *App) Start() (gs_ctx.Context, error) { // 执行命令行启动器 for _, r := range app.Runners { - r.Run(app.c.(gs_ctx.Context)) + r.Run(app.c.(gs.Context)) } // 通知应用启动事件 for _, event := range app.Events { - event.OnAppStart(app.c.(gs_ctx.Context)) + event.OnAppStart(app.c.(gs.Context)) } // 通知应用停止事件 - app.c.(gs_ctx.Context).Go(func(ctx context.Context) { + app.c.(gs.Context).Go(func(ctx context.Context) { <-ctx.Done() for _, event := range app.Events { event.OnAppStop(context.Background()) } }) - return app.c.(gs_ctx.Context), nil + return app.c.(gs.Context), nil } func (app *App) wait() { @@ -239,16 +238,16 @@ func (app *App) Property(key string, value interface{}) { } // Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { +func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.c.Accept(b) } // Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *gs_bean.BeanDefinition { +func (app *App) Object(i interface{}) *gs.BeanDefinition { return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { +func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return app.c.Accept(gs_ctx.NewBean(ctor, args...)) } diff --git a/gs/internal/gs_app/app_bootstrap.go b/gs/internal/gs_app/app_bootstrap.go index 1eb4b1bc..7f635f43 100644 --- a/gs/internal/gs_app/app_bootstrap.go +++ b/gs/internal/gs_app/app_bootstrap.go @@ -19,13 +19,12 @@ package gs_app import ( "reflect" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) type Bootstrapper struct { - c gs_ctx.Container + c gs.Container } func newBootstrap() *Bootstrapper { @@ -45,12 +44,12 @@ func (b *Bootstrapper) Property(key string, value interface{}) { } // Object 参考 Container.Object 的解释。 -func (b *Bootstrapper) Object(i interface{}) *gs_bean.BeanDefinition { +func (b *Bootstrapper) Object(i interface{}) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *Bootstrapper) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { +func (b *Bootstrapper) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(ctor, args...)) } diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 3eba0230..a249b056 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -25,59 +25,42 @@ import ( "reflect" "runtime" - "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) -// Context defines some methods of IoC container that Callable use. -type Context interface { - // Matches returns true when the Condition returns true, - // and returns false when the Condition returns false. - Matches(c gs_cond.Condition) (bool, error) - // Bind binds properties value by the "value" tag. - Bind(v reflect.Value, tag string) error - // Wire wires dependent beans by the "autowire" tag. - Wire(v reflect.Value, tag string) error -} - -// Arg 用于为函数参数提供绑定值。可以是 bean.Selector 类型,表示注入 bean ; -// 可以是 ${X:=Y} 形式的字符串,表示属性绑定或者注入 bean ;可以是 ValueArg -// 类型,表示不从 IoC 容器获取而是用户传入的普通值;可以是 IndexArg 类型,表示 -// 带有下标的参数绑定;可以是 *optionArg 类型,用于为 Option 方法提供参数绑定。 -type Arg interface{} - // IndexArg is an Arg that has an index. type IndexArg struct { n int - arg Arg + arg gs.Arg } // Index returns an IndexArg. -func Index(n int, arg Arg) IndexArg { +func Index(n int, arg gs.Arg) IndexArg { return IndexArg{n: n, arg: arg} } // R0 returns an IndexArg with index 0. -func R0(arg Arg) IndexArg { return Index(0, arg) } +func R0(arg gs.Arg) IndexArg { return Index(0, arg) } // R1 returns an IndexArg with index 1. -func R1(arg Arg) IndexArg { return Index(1, arg) } +func R1(arg gs.Arg) IndexArg { return Index(1, arg) } // R2 returns an IndexArg with index 2. -func R2(arg Arg) IndexArg { return Index(2, arg) } +func R2(arg gs.Arg) IndexArg { return Index(2, arg) } // R3 returns an IndexArg with index 3. -func R3(arg Arg) IndexArg { return Index(3, arg) } +func R3(arg gs.Arg) IndexArg { return Index(3, arg) } // R4 returns an IndexArg with index 4. -func R4(arg Arg) IndexArg { return Index(4, arg) } +func R4(arg gs.Arg) IndexArg { return Index(4, arg) } // R5 returns an IndexArg with index 5. -func R5(arg Arg) IndexArg { return Index(5, arg) } +func R5(arg gs.Arg) IndexArg { return Index(5, arg) } // R6 returns an IndexArg with index 6. -func R6(arg Arg) IndexArg { return Index(6, arg) } +func R6(arg gs.Arg) IndexArg { return Index(6, arg) } // ValueArg is an Arg that has a value. type ValueArg struct { @@ -97,11 +80,11 @@ func Value(v interface{}) ValueArg { // argList stores the arguments of a function. type argList struct { fnType reflect.Type - args []Arg + args []gs.Arg } // newArgList returns a new argList. -func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { +func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { fixedArgCount := fnType.NumIn() if fnType.IsVariadic() { @@ -116,7 +99,7 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { return ok }() - fnArgs := make([]Arg, fixedArgCount) + fnArgs := make([]gs.Arg, fixedArgCount) if len(args) > 0 { switch arg := args[0].(type) { @@ -178,7 +161,7 @@ func newArgList(fnType reflect.Type, args []Arg) (*argList, error) { } // get returns all processed Args value. fileLine is the binding position of Callable. -func (r *argList) get(ctx Context, fileLine string) ([]reflect.Value, error) { +func (r *argList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, error) { fnType := r.fnType numIn := fnType.NumIn() @@ -207,7 +190,7 @@ func (r *argList) get(ctx Context, fileLine string) ([]reflect.Value, error) { return result, nil } -func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) (reflect.Value, error) { +func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine string) (reflect.Value, error) { var ( err error @@ -275,11 +258,11 @@ func (r *argList) getArg(ctx Context, arg Arg, t reflect.Type, fileLine string) // optionArg Option 函数的参数绑定。 type optionArg struct { r *Callable - c gs_cond.Condition + c gs.Condition } // Provide 为 Option 方法绑定运行时参数。 -func Provide(fn interface{}, args ...Arg) *Callable { +func Provide(fn interface{}, args ...gs.Arg) *Callable { r, err := Bind(fn, args, 1) if err != nil { panic(err) @@ -288,7 +271,7 @@ func Provide(fn interface{}, args ...Arg) *Callable { } // Option 返回 Option 函数的参数绑定。 -func Option(fn interface{}, args ...Arg) *optionArg { +func Option(fn interface{}, args ...gs.Arg) *optionArg { t := reflect.TypeOf(fn) if t.Kind() != reflect.Func || t.NumOut() != 1 { @@ -303,12 +286,12 @@ func Option(fn interface{}, args ...Arg) *optionArg { } // On 设置一个 gs_cond.Condition 对象。 -func (arg *optionArg) On(c gs_cond.Condition) *optionArg { +func (arg *optionArg) On(c gs.Condition) *optionArg { arg.c = c return arg } -func (arg *optionArg) call(ctx Context) (reflect.Value, error) { +func (arg *optionArg) call(ctx gs.ArgContext) (reflect.Value, error) { var ( ok bool @@ -351,7 +334,7 @@ type Callable struct { // Bind returns a Callable that wrappers a function and its binding arguments. // The argument skip is the number of frames to skip over. -func Bind(fn interface{}, args []Arg, skip int) (*Callable, error) { +func Bind(fn interface{}, args []gs.Arg, skip int) (*Callable, error) { fnType := reflect.TypeOf(fn) argList, err := newArgList(fnType, args) @@ -370,7 +353,7 @@ func Bind(fn interface{}, args []Arg, skip int) (*Callable, error) { } // Arg returns the ith binding argument. -func (r *Callable) Arg(i int) (Arg, bool) { +func (r *Callable) Arg(i int) (gs.Arg, bool) { if i >= len(r.argList.args) { return nil, false } @@ -386,7 +369,7 @@ func (r *Callable) In(i int) (reflect.Type, bool) { // Call invokes the function with its binding arguments processed in the IoC // container. If the function returns an error, then the Call returns it. -func (r *Callable) Call(ctx Context) ([]reflect.Value, error) { +func (r *Callable) Call(ctx gs.ArgContext) ([]reflect.Value, error) { in, err := r.argList.get(ctx, r.fileLine) if err != nil { diff --git a/gs/internal/gs_arg/arg_mock.go b/gs/internal/gs_arg/arg_mock.go index 4cb1a3ad..9f6faf35 100644 --- a/gs/internal/gs_arg/arg_mock.go +++ b/gs/internal/gs_arg/arg_mock.go @@ -10,10 +10,10 @@ package gs_arg import ( - reflect "reflect" + "reflect" - cond "github.com/go-spring/spring-core/gs/internal/gs_cond" - gomock "go.uber.org/mock/gomock" + "github.com/go-spring/spring-core/gs/internal/gs" + "go.uber.org/mock/gomock" ) // MockContext is a mock of Context interface. @@ -55,7 +55,7 @@ func (mr *MockContextMockRecorder) Bind(v, tag any) *gomock.Call { } // Matches mocks base method. -func (m *MockContext) Matches(c cond.Condition) (bool, error) { +func (m *MockContext) Matches(c gs.Condition) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Matches", c) ret0, _ := ret[0].(bool) diff --git a/gs/internal/gs_arg/arg_test.go b/gs/internal/gs_arg/arg_test.go index 91ecf2a7..fb590d2e 100644 --- a/gs/internal/gs_arg/arg_test.go +++ b/gs/internal/gs_arg/arg_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" @@ -32,7 +33,7 @@ func TestBind(t *testing.T) { defer ctrl.Finish() ctx := gs_arg.NewMockContext(ctrl) fn := func() {} - c, err := gs_arg.Bind(fn, []gs_arg.Arg{}, 1) + c, err := gs_arg.Bind(fn, []gs.Arg{}, 1) if err != nil { t.Fatal(err) } @@ -51,7 +52,7 @@ func TestBind(t *testing.T) { fn := func(i int) { expectInt = i } - c, err := gs_arg.Bind(fn, []gs_arg.Arg{ + c, err := gs_arg.Bind(fn, []gs.Arg{ gs_arg.Value(3), }, 1) if err != nil { @@ -77,7 +78,7 @@ func TestBind(t *testing.T) { fn := func(i int) { expectInt = i } - c, err := gs_arg.Bind(fn, []gs_arg.Arg{ + c, err := gs_arg.Bind(fn, []gs.Arg{ "${a.b.c}", }, 1) if err != nil { @@ -106,7 +107,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := gs_arg.Bind(fn, []gs_arg.Arg{ + c, err := gs_arg.Bind(fn, []gs.Arg{ "a", }, 1) if err != nil { @@ -135,7 +136,7 @@ func TestBind(t *testing.T) { fn := func(v *st) { expectInt = v.i } - c, err := gs_arg.Bind(fn, []gs_arg.Arg{}, 1) + c, err := gs_arg.Bind(fn, []gs.Arg{}, 1) if err != nil { t.Fatal(err) } diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index 7457882b..db264ffb 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -1,340 +1,329 @@ package gs_bean -import ( - "errors" - "fmt" - "reflect" - "runtime" - "strings" - - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_cond" - "github.com/go-spring/spring-core/gs/internal/gs_util" -) - -type BeanStatus int8 - -const ( - Deleted = BeanStatus(-1) // 已删除 - Default = BeanStatus(iota) // 未处理 - Resolving // 正在决议 - Resolved // 已决议 - Creating // 正在创建 - Created // 已创建 - Wired // 注入完成 -) - -func GetStatusString(status BeanStatus) string { - switch status { - case Deleted: - return "Deleted" - case Default: - return "Default" - case Resolving: - return "Resolving" - case Resolved: - return "Resolved" - case Creating: - return "Creating" - case Created: - return "Created" - case Wired: - return "Wired" - default: - return "" - } -} - -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - V reflect.Value // 值 - T reflect.Type // 类型 - F *gs_arg.Callable // 构造函数 - - file string // 注册点所在文件 - line int // 注册点所在行数 - - name string // 名称 - typeName string // 原始类型的全限定名 - status BeanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond gs_cond.Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []gs_util.BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 -} - -func (d *BeanDefinition) GetName() string { return d.name } -func (d *BeanDefinition) GetTypeName() string { return d.typeName } -func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } -func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } -func (d *BeanDefinition) IsPrimary() bool { return d.primary } -func (d *BeanDefinition) IsMethod() bool { return d.method } -func (d *BeanDefinition) GetCond() gs_cond.Condition { return d.cond } -func (d *BeanDefinition) GetOrder() float32 { return d.order } -func (d *BeanDefinition) GetInit() interface{} { return d.init } -func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } -func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } -func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } - -// Type 返回 bean 的类型。 -func (d *BeanDefinition) Type() reflect.Type { - return d.T -} - -// Value 返回 bean 的值。 -func (d *BeanDefinition) Value() reflect.Value { - return d.V -} - -// Interface 返回 bean 的真实值。 -func (d *BeanDefinition) Interface() interface{} { - return d.V.Interface() -} - -// ID 返回 bean 的 ID 。 -func (d *BeanDefinition) ID() string { - return d.typeName + ":" + d.name -} - -// BeanName 返回 bean 的名称。 -func (d *BeanDefinition) BeanName() string { - return d.name -} - -// TypeName 返回 bean 的原始类型的全限定名。 -func (d *BeanDefinition) TypeName() string { - return d.typeName -} - -// Created 返回是否已创建。 -func (d *BeanDefinition) Created() bool { - return d.status >= Created -} - -// Wired 返回 bean 是否已经注入。 -func (d *BeanDefinition) Wired() bool { - return d.status == Wired -} - -// FileLine 返回 bean 的注册点。 -func (d *BeanDefinition) FileLine() string { - return fmt.Sprintf("%s:%d", d.file, d.line) -} - -// GetClass 返回 bean 的类型描述。 -func (d *BeanDefinition) GetClass() string { - if d.F == nil { - return "object bean" - } - return "constructor bean" -} - -func (d *BeanDefinition) String() string { - return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) -} - -// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 -func (d *BeanDefinition) Match(typeName string, beanName string) bool { - - typeIsSame := false - if typeName == "" || d.typeName == typeName { - typeIsSame = true - } - - nameIsSame := false - if beanName == "" || d.name == beanName { - nameIsSame = true - } - - return typeIsSame && nameIsSame -} - -// Name 设置 bean 的名称。 -func (d *BeanDefinition) Name(name string) *BeanDefinition { - d.name = name - return d -} - -// On 设置 bean 的 Condition。 -func (d *BeanDefinition) On(cond gs_cond.Condition) *BeanDefinition { - d.cond = cond - return d -} - -// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 -func (d *BeanDefinition) Order(order float32) *BeanDefinition { - d.order = order - return d -} - -// DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { - d.depends = append(d.depends, selectors...) - return d -} - -// Primary 设置 bean 为主版本。 -func (d *BeanDefinition) Primary() *BeanDefinition { - d.primary = true - return d -} - -// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 -// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 -func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !gs_util.IsFuncType(fnType) { - return false - } - if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { - return false - } - return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) -} - -// Init 设置 bean 的初始化函数。 -func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { - if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.init = fn - return d - } - panic(errors.New("init should be func(bean) or func(bean)error")) -} - -// Destroy 设置 bean 的销毁函数。 -func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { - if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.destroy = fn - return d - } - panic(errors.New("destroy should be func(bean) or func(bean)error")) -} - -// Export 设置 bean 的导出接口。 -func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { - err := d.export(exports...) - if err != nil { - panic(err) - } - return d -} - -func (d *BeanDefinition) export(exports ...interface{}) error { - for _, o := range exports { - var typ reflect.Type - if t, ok := o.(reflect.Type); ok { - typ = t - } else { // 处理 (*error)(nil) 这种导出形式 - typ = gs_util.Indirect(reflect.TypeOf(o)) - } - if typ.Kind() != reflect.Interface { - return errors.New("only interface type can be exported") - } - exported := false - for _, export := range d.exports { - if typ == export { - exported = true - break - } - } - if exported { - continue - } - d.exports = append(d.exports, typ) - } - return nil -} - -// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *BeanDefinition { - - var v reflect.Value - var fromValue bool - var method bool - var name string - - switch i := objOrCtor.(type) { - case reflect.Value: - fromValue = true - v = i - default: - v = reflect.ValueOf(i) - } - - t := v.Type() - if !gs_util.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - if !v.IsValid() || v.IsNil() { - panic(errors.New("bean can't be nil")) - } - - const skip = 2 - var f *gs_arg.Callable - _, file, line, _ := runtime.Caller(skip) - - // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 - if !fromValue && t.Kind() == reflect.Func { - - if !gs_util.IsConstructor(t) { - t1 := "func(...)bean" - t2 := "func(...)(bean, error)" - panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) - } - - var err error - f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) - if err != nil { - panic(err) - } - - out0 := t.Out(0) - v = reflect.New(out0) - if gs_util.IsBeanType(out0) { - v = v.Elem() - } - - t = v.Type() - if !gs_util.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 - fnPtr := reflect.ValueOf(objOrCtor).Pointer() - fnInfo := runtime.FuncForPC(fnPtr) - funcName := fnInfo.Name() - name = funcName[strings.LastIndex(funcName, "/")+1:] - name = name[strings.Index(name, ".")+1:] - if name[0] == '(' { - name = name[strings.Index(name, ".")+1:] - } - method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 - } - - if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { - panic(errors.New("bean should be *val but not *ref")) - } - - // Type.String() 一般返回 *pkg.Type 形式的字符串, - // 我们只取最后的类型名,如有需要请自定义 bean 名称。 - if name == "" { - s := strings.Split(t.String(), ".") - name = strings.TrimPrefix(s[len(s)-1], "*") - } - - return &BeanDefinition{ - T: t, - V: v, - F: f, - name: name, - typeName: gs_util.TypeName(t), - status: Default, - method: method, - file: file, - line: line, - } -} +// +//type BeanStatus int8 +// +//const ( +// Deleted = BeanStatus(-1) // 已删除 +// Default = BeanStatus(iota) // 未处理 +// Resolving // 正在决议 +// Resolved // 已决议 +// Creating // 正在创建 +// Created // 已创建 +// Wired // 注入完成 +//) +// +//func GetStatusString(status BeanStatus) string { +// switch status { +// case Deleted: +// return "Deleted" +// case Default: +// return "Default" +// case Resolving: +// return "Resolving" +// case Resolved: +// return "Resolved" +// case Creating: +// return "Creating" +// case Created: +// return "Created" +// case Wired: +// return "Wired" +// default: +// return "" +// } +//} +// +//// BeanDefinition bean 元数据。 +//type BeanDefinition struct { +// V reflect.Value // 值 +// T reflect.Type // 类型 +// F *gs_arg.Callable // 构造函数 +// +// file string // 注册点所在文件 +// line int // 注册点所在行数 +// +// name string // 名称 +// typeName string // 原始类型的全限定名 +// status BeanStatus // 状态 +// primary bool // 是否为主版本 +// method bool // 是否为成员方法 +// cond gs.Condition // 判断条件 +// order float32 // 收集时的顺序 +// init interface{} // 初始化函数 +// destroy interface{} // 销毁函数 +// depends []gs_util.BeanSelector // 间接依赖项 +// exports []reflect.Type // 导出的接口 +//} +// +//func (d *BeanDefinition) GetName() string { return d.name } +//func (d *BeanDefinition) GetTypeName() string { return d.typeName } +//func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } +//func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } +//func (d *BeanDefinition) IsPrimary() bool { return d.primary } +//func (d *BeanDefinition) IsMethod() bool { return d.method } +//func (d *BeanDefinition) GetCond() gs.Condition { return d.cond } +//func (d *BeanDefinition) GetOrder() float32 { return d.order } +//func (d *BeanDefinition) GetInit() interface{} { return d.init } +//func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } +//func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } +//func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } +// +//// Type 返回 bean 的类型。 +//func (d *BeanDefinition) Type() reflect.Type { +// return d.T +//} +// +//// Value 返回 bean 的值。 +//func (d *BeanDefinition) Value() reflect.Value { +// return d.V +//} +// +//// Interface 返回 bean 的真实值。 +//func (d *BeanDefinition) Interface() interface{} { +// return d.V.Interface() +//} +// +//// ID 返回 bean 的 ID 。 +//func (d *BeanDefinition) ID() string { +// return d.typeName + ":" + d.name +//} +// +//// BeanName 返回 bean 的名称。 +//func (d *BeanDefinition) BeanName() string { +// return d.name +//} +// +//// TypeName 返回 bean 的原始类型的全限定名。 +//func (d *BeanDefinition) TypeName() string { +// return d.typeName +//} +// +//// Created 返回是否已创建。 +//func (d *BeanDefinition) Created() bool { +// return d.status >= Created +//} +// +//// Wired 返回 bean 是否已经注入。 +//func (d *BeanDefinition) Wired() bool { +// return d.status == Wired +//} +// +//// FileLine 返回 bean 的注册点。 +//func (d *BeanDefinition) FileLine() string { +// return fmt.Sprintf("%s:%d", d.file, d.line) +//} +// +//// GetClass 返回 bean 的类型描述。 +//func (d *BeanDefinition) GetClass() string { +// if d.F == nil { +// return "object bean" +// } +// return "constructor bean" +//} +// +//func (d *BeanDefinition) String() string { +// return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) +//} +// +//// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 +//func (d *BeanDefinition) Match(typeName string, beanName string) bool { +// +// typeIsSame := false +// if typeName == "" || d.typeName == typeName { +// typeIsSame = true +// } +// +// nameIsSame := false +// if beanName == "" || d.name == beanName { +// nameIsSame = true +// } +// +// return typeIsSame && nameIsSame +//} +// +//// Name 设置 bean 的名称。 +//func (d *BeanDefinition) Name(name string) *BeanDefinition { +// d.name = name +// return d +//} +// +//// On 设置 bean 的 Condition。 +//func (d *BeanDefinition) On(cond gs.Condition) *BeanDefinition { +// d.cond = cond +// return d +//} +// +//// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 +//func (d *BeanDefinition) Order(order float32) *BeanDefinition { +// d.order = order +// return d +//} +// +//// DependsOn 设置 bean 的间接依赖项。 +//func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { +// d.depends = append(d.depends, selectors...) +// return d +//} +// +//// Primary 设置 bean 为主版本。 +//func (d *BeanDefinition) Primary() *BeanDefinition { +// d.primary = true +// return d +//} +// +//// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 +//// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 +//func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { +// if !gs_util.IsFuncType(fnType) { +// return false +// } +// if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { +// return false +// } +// return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) +//} +// +//// Init 设置 bean 的初始化函数。 +//func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { +// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { +// d.init = fn +// return d +// } +// panic(errors.New("init should be func(bean) or func(bean)error")) +//} +// +//// Destroy 设置 bean 的销毁函数。 +//func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { +// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { +// d.destroy = fn +// return d +// } +// panic(errors.New("destroy should be func(bean) or func(bean)error")) +//} +// +//// Export 设置 bean 的导出接口。 +//func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { +// err := d.export(exports...) +// if err != nil { +// panic(err) +// } +// return d +//} +// +//func (d *BeanDefinition) export(exports ...interface{}) error { +// for _, o := range exports { +// var typ reflect.Type +// if t, ok := o.(reflect.Type); ok { +// typ = t +// } else { // 处理 (*error)(nil) 这种导出形式 +// typ = gs_util.Indirect(reflect.TypeOf(o)) +// } +// if typ.Kind() != reflect.Interface { +// return errors.New("only interface type can be exported") +// } +// exported := false +// for _, export := range d.exports { +// if typ == export { +// exported = true +// break +// } +// } +// if exported { +// continue +// } +// d.exports = append(d.exports, typ) +// } +// return nil +//} +// +//// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +//func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanDefinition { +// +// var v reflect.Value +// var fromValue bool +// var method bool +// var name string +// +// switch i := objOrCtor.(type) { +// case reflect.Value: +// fromValue = true +// v = i +// default: +// v = reflect.ValueOf(i) +// } +// +// t := v.Type() +// if !gs_util.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// if !v.IsValid() || v.IsNil() { +// panic(errors.New("bean can't be nil")) +// } +// +// const skip = 2 +// var f *gs_arg.Callable +// _, file, line, _ := runtime.Caller(skip) +// +// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 +// if !fromValue && t.Kind() == reflect.Func { +// +// if !gs_util.IsConstructor(t) { +// t1 := "func(...)bean" +// t2 := "func(...)(bean, error)" +// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) +// } +// +// var err error +// f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) +// if err != nil { +// panic(err) +// } +// +// out0 := t.Out(0) +// v = reflect.New(out0) +// if gs_util.IsBeanType(out0) { +// v = v.Elem() +// } +// +// t = v.Type() +// if !gs_util.IsBeanType(t) { +// panic(errors.New("bean must be ref type")) +// } +// +// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 +// fnPtr := reflect.ValueOf(objOrCtor).Pointer() +// fnInfo := runtime.FuncForPC(fnPtr) +// funcName := fnInfo.Name() +// name = funcName[strings.LastIndex(funcName, "/")+1:] +// name = name[strings.Index(name, ".")+1:] +// if name[0] == '(' { +// name = name[strings.Index(name, ".")+1:] +// } +// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 +// } +// +// if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { +// panic(errors.New("bean should be *val but not *ref")) +// } +// +// // Type.String() 一般返回 *pkg.Type 形式的字符串, +// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 +// if name == "" { +// s := strings.Split(t.String(), ".") +// name = strings.TrimPrefix(s[len(s)-1], "*") +// } +// +// return &BeanDefinition{ +// T: t, +// V: v, +// F: f, +// name: name, +// typeName: gs_util.TypeName(t), +// status: Default, +// method: method, +// file: file, +// line: line, +// } +//} diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index 853f5829..40f60f9a 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -25,52 +25,36 @@ import ( "strconv" "strings" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/expr" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) -// Context defines some methods of IoC container that conditions use. -type Context interface { - // Has returns whether the IoC container has a property. - Has(key string) bool - // Prop returns the property's value when the IoC container has it, or - // returns empty string when the IoC container doesn't have it. - Prop(key string, opts ...conf.GetOption) string - // Find returns bean definitions that matched with the bean selector. - Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) -} - -// Condition is used when registering a bean to determine whether it's valid. -type Condition interface { - Matches(ctx Context) (bool, error) -} - -type FuncCond func(ctx Context) (bool, error) +type FuncCond func(ctx gs.ConditionContext) (bool, error) -func (c FuncCond) Matches(ctx Context) (bool, error) { +func (c FuncCond) Matches(ctx gs.ConditionContext) (bool, error) { return c(ctx) } // OK returns a Condition that always returns true. -func OK() Condition { - return FuncCond(func(ctx Context) (bool, error) { +func OK() gs.Condition { + return FuncCond(func(ctx gs.ConditionContext) (bool, error) { return true, nil }) } // not is a Condition that negating to another. type not struct { - c Condition + c gs.Condition } // Not returns a Condition that negating to another. -func Not(c Condition) Condition { +func Not(c gs.Condition) gs.Condition { return ¬{c: c} } -func (c *not) Matches(ctx Context) (bool, error) { +func (c *not) Matches(ctx gs.ConditionContext) (bool, error) { ok, err := c.c.Matches(ctx) return !ok, err } @@ -82,7 +66,7 @@ type onProperty struct { matchIfMissing bool } -func (c *onProperty) Matches(ctx Context) (bool, error) { +func (c *onProperty) Matches(ctx gs.ConditionContext) (bool, error) { if !ctx.Has(c.name) { return c.matchIfMissing, nil @@ -120,7 +104,7 @@ type onMissingProperty struct { name string } -func (c *onMissingProperty) Matches(ctx Context) (bool, error) { +func (c *onMissingProperty) Matches(ctx gs.ConditionContext) (bool, error) { return !ctx.Has(c.name), nil } @@ -129,7 +113,7 @@ type onBean struct { selector gs_util.BeanSelector } -func (c *onBean) Matches(ctx Context) (bool, error) { +func (c *onBean) Matches(ctx gs.ConditionContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) > 0, err } @@ -139,7 +123,7 @@ type onMissingBean struct { selector gs_util.BeanSelector } -func (c *onMissingBean) Matches(ctx Context) (bool, error) { +func (c *onMissingBean) Matches(ctx gs.ConditionContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) == 0, err } @@ -149,7 +133,7 @@ type onSingleBean struct { selector gs_util.BeanSelector } -func (c *onSingleBean) Matches(ctx Context) (bool, error) { +func (c *onSingleBean) Matches(ctx gs.ConditionContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) == 1, err } @@ -159,7 +143,7 @@ type onExpression struct { expression string } -func (c *onExpression) Matches(ctx Context) (bool, error) { +func (c *onExpression) Matches(ctx gs.ConditionContext) (bool, error) { return false, util.UnimplementedMethod } @@ -175,15 +159,15 @@ const ( // group is a Condition implemented by operation of Condition(s). type group struct { op Operator - cond []Condition + cond []gs.Condition } // Group returns a Condition implemented by operation of Condition(s). -func Group(op Operator, cond ...Condition) Condition { +func Group(op Operator, cond ...gs.Condition) gs.Condition { return &group{op: op, cond: cond} } -func (g *group) Matches(ctx Context) (bool, error) { +func (g *group) Matches(ctx gs.ConditionContext) (bool, error) { if len(g.cond) == 0 { return false, errors.New("no condition in group") @@ -224,12 +208,12 @@ func (g *group) Matches(ctx Context) (bool, error) { // node is a Condition implemented by link of Condition(s). type node struct { - cond Condition + cond gs.Condition op Operator next *node } -func (n *node) Matches(ctx Context) (bool, error) { +func (n *node) Matches(ctx gs.ConditionContext) (bool, error) { if n.cond == nil { return true, nil @@ -276,7 +260,7 @@ func New() *conditional { return &conditional{head: n, curr: n} } -func (c *conditional) Matches(ctx Context) (bool, error) { +func (c *conditional) Matches(ctx gs.ConditionContext) (bool, error) { return c.head.Matches(ctx) } @@ -299,12 +283,12 @@ func (c *conditional) And() *conditional { } // On returns a conditional that starts with one Condition. -func On(cond Condition) *conditional { +func On(cond gs.Condition) *conditional { return New().On(cond) } // On adds one Condition. -func (c *conditional) On(cond Condition) *conditional { +func (c *conditional) On(cond gs.Condition) *conditional { if c.curr.cond != nil { c.And() } @@ -400,12 +384,12 @@ func (c *conditional) OnExpression(expression string) *conditional { // OnMatches returns a conditional that starts with a Condition that returns true // when function returns true. -func OnMatches(fn func(ctx Context) (bool, error)) *conditional { +func OnMatches(fn func(ctx gs.ConditionContext) (bool, error)) *conditional { return New().OnMatches(fn) } // OnMatches adds a Condition that returns true when function returns true. -func (c *conditional) OnMatches(fn func(ctx Context) (bool, error)) *conditional { +func (c *conditional) OnMatches(fn func(ctx gs.ConditionContext) (bool, error)) *conditional { return c.On(FuncCond(fn)) } diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index 8a250335..e38ce4e6 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -10,11 +10,12 @@ package gs_cond import ( - reflect "reflect" + "reflect" - conf "github.com/go-spring/spring-core/conf" - gsutil "github.com/go-spring/spring-core/gs/internal/gs_util" - gomock "go.uber.org/mock/gomock" + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_util" + "go.uber.org/mock/gomock" ) // MockContext is a mock of Context interface. @@ -42,10 +43,10 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector gsutil.BeanSelector) ([]gsutil.BeanDefinition, error) { +func (m *MockContext) Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]gsutil.BeanDefinition) + ret0, _ := ret[0].([]gs_util.BeanDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -114,7 +115,7 @@ func (m *MockCondition) EXPECT() *MockConditionMockRecorder { } // Matches mocks base method. -func (m *MockCondition) Matches(ctx Context) (bool, error) { +func (m *MockCondition) Matches(ctx gs.ConditionContext) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Matches", ctx) ret0, _ := ret[0].(bool) diff --git a/gs/internal/gs_ctx/gs.go b/gs/internal/gs_ctx/gs.go index 754efc35..05b0b163 100755 --- a/gs/internal/gs_ctx/gs.go +++ b/gs/internal/gs_ctx/gs.go @@ -31,9 +31,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" - "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -48,52 +46,19 @@ const ( ) var ( - contextType = reflect.TypeOf((*Context)(nil)).Elem() + contextType = reflect.TypeOf((*gs.Context)(nil)).Elem() ) -type Container interface { - Context() context.Context - Properties() *dync.Properties - Property(key string, value interface{}) - Object(i interface{}) *gs_bean.BeanDefinition - Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition - Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition - OnProperty(key string, fn interface{}) - Refresh(autoClear bool) error - Close() -} - -// Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 -// 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 -// 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, -// 都可以转换为启动阶段的方案以提高程序的性能。 -// 另一方面,为了统一 Container 和 App 两种启动方式下这些方法的使用方式,需要提取 -// 出一个可共用的接口来,也就是说,无论程序是 Container 方式启动还是 App 方式启动, -// 都可以在需要使用这些方法的地方注入一个 Context 对象而不是 Container 对象或者 -// App 对象,从而实现使用方式的统一。 -type Context interface { - Context() context.Context - Keys() []string - Has(key string) bool - Prop(key string, opts ...conf.GetOption) string - Resolve(s string) (string, error) - Bind(i interface{}, opts ...conf.BindArg) error - Get(i interface{}, selectors ...gs_util.BeanSelector) error - Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) - Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) - Go(fn func(ctx context.Context)) -} - // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { - GSContext Context `autowire:""` + GSContext gs.Context `autowire:""` } type tempContainer struct { initProperties *conf.Properties - beans []*gs_bean.BeanDefinition - beansByName map[string][]*gs_bean.BeanDefinition - beansByType map[reflect.Type][]*gs_bean.BeanDefinition + beans []*gs.BeanDefinition + beansByName map[string][]*gs.BeanDefinition + beansByType map[reflect.Type][]*gs.BeanDefinition mapOfOnProperty map[string]interface{} } @@ -117,7 +82,7 @@ type container struct { } // New 创建 IoC 容器。 -func New() Container { +func New() *container { ctx, cancel := context.WithCancel(context.Background()) return &container{ ctx: ctx, @@ -125,8 +90,8 @@ func New() Container { p: dync.New(), tempContainer: &tempContainer{ initProperties: conf.NewProperties(), - beansByName: make(map[string][]*gs_bean.BeanDefinition), - beansByType: make(map[reflect.Type][]*gs_bean.BeanDefinition), + beansByName: make(map[string][]*gs.BeanDefinition), + beansByType: make(map[reflect.Type][]*gs.BeanDefinition), mapOfOnProperty: make(map[string]interface{}), }, } @@ -170,7 +135,7 @@ func (c *container) Property(key string, value interface{}) { c.initProperties.Set(key, value) } -func (c *container) Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { +func (c *container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } @@ -179,22 +144,22 @@ func (c *container) Accept(b *gs_bean.BeanDefinition) *gs_bean.BeanDefinition { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Object(i interface{}) *gs_bean.BeanDefinition { +func (c *container) Object(i interface{}) *gs.BeanDefinition { return c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Provide(ctor interface{}, args ...gs_arg.Arg) *gs_bean.BeanDefinition { +func (c *container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return c.Accept(NewBean(ctor, args...)) } // destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 type destroyer struct { - current *gs_bean.BeanDefinition - earlier []*gs_bean.BeanDefinition + current *gs.BeanDefinition + earlier []*gs.BeanDefinition } -func (d *destroyer) foundEarlier(b *gs_bean.BeanDefinition) bool { +func (d *destroyer) foundEarlier(b *gs.BeanDefinition) bool { for _, c := range d.earlier { if c == b { return true @@ -204,7 +169,7 @@ func (d *destroyer) foundEarlier(b *gs_bean.BeanDefinition) bool { } // after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 -func (d *destroyer) after(b *gs_bean.BeanDefinition) { +func (d *destroyer) after(b *gs.BeanDefinition) { if d.foundEarlier(b) { return } @@ -234,7 +199,7 @@ type lazyField struct { type wiringStack struct { destroyers *list.List destroyerMap map[string]*destroyer - beans []*gs_bean.BeanDefinition + beans []*gs.BeanDefinition lazyFields []lazyField } @@ -246,7 +211,7 @@ func newWiringStack() *wiringStack { } // pushBack 添加一个即将注入的 bean 。 -func (s *wiringStack) pushBack(b *gs_bean.BeanDefinition) { +func (s *wiringStack) pushBack(b *gs.BeanDefinition) { // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) s.beans = append(s.beans, b) } @@ -268,7 +233,7 @@ func (s *wiringStack) path() (path string) { } // saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 -func (s *wiringStack) saveDestroyer(b *gs_bean.BeanDefinition) *destroyer { +func (s *wiringStack) saveDestroyer(b *gs.BeanDefinition) *destroyer { d, ok := s.destroyerMap[b.ID()] if !ok { d = &destroyer{current: b} @@ -322,7 +287,7 @@ func (c *container) Refresh(autoClear bool) (err error) { c.p.Refresh(c.initProperties) // start := time.Now() - c.Object(c).Export((*Context)(nil)) + c.Object(c).Export((*gs.Context)(nil)) for key, f := range c.mapOfOnProperty { t := reflect.TypeOf(f) @@ -345,13 +310,13 @@ func (c *container) Refresh(autoClear bool) (err error) { } } - beansById := make(map[string]*gs_bean.BeanDefinition) + beansById := make(map[string]*gs.BeanDefinition) { for _, b := range c.beans { - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { continue } - if b.GetStatus() != gs_bean.Resolved { + if b.GetStatus() != gs.Resolved { return fmt.Errorf("unexpected status %d", b.GetStatus()) } beanID := b.ID() @@ -412,7 +377,7 @@ func (c *container) Refresh(autoClear bool) (err error) { return nil } -func (c *container) registerBean(b *gs_bean.BeanDefinition) { +func (c *container) registerBean(b *gs.BeanDefinition) { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) @@ -423,13 +388,13 @@ func (c *container) registerBean(b *gs_bean.BeanDefinition) { } // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 -func (c *container) resolveBean(b *gs_bean.BeanDefinition) error { +func (c *container) resolveBean(b *gs.BeanDefinition) error { - if b.GetStatus() >= gs_bean.Resolving { + if b.GetStatus() >= gs.Resolving { return nil } - b.SetStatus(gs_bean.Resolving) + b.SetStatus(gs.Resolving) // method bean 先确定 parent bean 是否存在 if b.IsMethod() { @@ -450,7 +415,7 @@ func (c *container) resolveBean(b *gs_bean.BeanDefinition) error { msg = msg[:len(msg)-2] + "]" return errors.New(msg) } else if n == 0 { - b.SetStatus(gs_bean.Deleted) + b.SetStatus(gs.Deleted) return nil } } @@ -459,12 +424,12 @@ func (c *container) resolveBean(b *gs_bean.BeanDefinition) error { if ok, err := b.GetCond().Matches(c); err != nil { return err } else if !ok { - b.SetStatus(gs_bean.Deleted) + b.SetStatus(gs.Deleted) return nil } } - b.SetStatus(gs_bean.Resolved) + b.SetStatus(gs.Resolved) return nil } @@ -516,9 +481,9 @@ func toWireTag(selector gs_util.BeanSelector) wireTag { switch s := selector.(type) { case string: return parseWireTag(s) - case gs_bean.BeanDefinition: + case gs.BeanDefinition: return parseWireTag(s.ID()) - case *gs_bean.BeanDefinition: + case *gs.BeanDefinition: return parseWireTag(s.ID()) default: return parseWireTag(gs_util.TypeName(s) + ":") @@ -538,18 +503,18 @@ func toWireString(tags []wireTag) string { // findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs_bean.BeanDefinition, error) { +func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs.BeanDefinition, error) { - finder := func(fn func(*gs_bean.BeanDefinition) bool) ([]*gs_bean.BeanDefinition, error) { - var result []*gs_bean.BeanDefinition + finder := func(fn func(*gs.BeanDefinition) bool) ([]*gs.BeanDefinition, error) { + var result []*gs.BeanDefinition for _, b := range c.beans { - if b.GetStatus() == gs_bean.Resolving || b.GetStatus() == gs_bean.Deleted || !fn(b) { + if b.GetStatus() == gs.Resolving || b.GetStatus() == gs.Deleted || !fn(b) { continue } if err := c.resolveBean(b); err != nil { return nil, err } - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { continue } result = append(result, b) @@ -559,9 +524,9 @@ func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs_bean.BeanDefi var t reflect.Type switch st := selector.(type) { - case string, gs_bean.BeanDefinition, *gs_bean.BeanDefinition: + case string, gs.BeanDefinition, *gs.BeanDefinition: tag := toWireTag(selector) - return finder(func(b *gs_bean.BeanDefinition) bool { + return finder(func(b *gs.BeanDefinition) bool { return b.Match(tag.typeName, tag.beanName) }) case reflect.Type: @@ -576,7 +541,7 @@ func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs_bean.BeanDefi } } - return finder(func(b *gs_bean.BeanDefinition) bool { + return finder(func(b *gs.BeanDefinition) bool { if b.Type() == t { return true } @@ -592,14 +557,14 @@ func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs_bean.BeanDefi // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *container) wireBean(b *gs_bean.BeanDefinition, stack *wiringStack) error { +func (c *container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) } // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.GetStatus() == gs_bean.Wired { + if c.state == Refreshed && b.GetStatus() == gs.Wired { return nil } @@ -616,26 +581,26 @@ func (c *container) wireBean(b *gs_bean.BeanDefinition, stack *wiringStack) erro haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { - d.after(i.Value.(*gs_bean.BeanDefinition)) + d.after(i.Value.(*gs.BeanDefinition)) } stack.destroyers.PushBack(b) } stack.pushBack(b) - if b.GetStatus() == gs_bean.Creating && b.F != nil { + if b.GetStatus() == gs.Creating && b.F != nil { prev := stack.beans[len(stack.beans)-2] - if prev.GetStatus() == gs_bean.Creating { + if prev.GetStatus() == gs.Creating { return errors.New("found circle autowire") } } - if b.GetStatus() >= gs_bean.Creating { + if b.GetStatus() >= gs.Creating { stack.popBack() return nil } - b.SetStatus(gs_bean.Creating) + b.SetStatus(gs.Creating) // 对当前 bean 的间接依赖项进行注入。 for _, s := range b.GetDepends() { @@ -656,7 +621,7 @@ func (c *container) wireBean(b *gs_bean.BeanDefinition, stack *wiringStack) erro return err } - b.SetStatus(gs_bean.Created) + b.SetStatus(gs.Created) t := v.Type() for _, typ := range b.GetExports() { @@ -684,7 +649,7 @@ func (c *container) wireBean(b *gs_bean.BeanDefinition, stack *wiringStack) erro } } - b.SetStatus(gs_bean.Wired) + b.SetStatus(gs.Wired) stack.popBack() return nil } @@ -694,7 +659,7 @@ type argContext struct { stack *wiringStack } -func (a *argContext) Matches(c gs_cond.Condition) (bool, error) { +func (a *argContext) Matches(c gs.Condition) (bool, error) { return c.Matches(a.c) } @@ -707,7 +672,7 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { } // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *container) getBeanValue(b *gs_bean.BeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { if b.F == nil { return b.Value(), nil @@ -886,9 +851,9 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []*gs_bean.BeanDefinition + var foundBeans []*gs.BeanDefinition for _, b := range c.beansByType[t] { - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { continue } if !b.Match(tag.typeName, tag.beanName) { @@ -900,7 +865,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 if t.Kind() == reflect.Interface && tag.beanName != "" { for _, b := range c.beansByName[tag.beanName] { - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { continue } if !b.Type().AssignableTo(t) { @@ -932,7 +897,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []*gs_bean.BeanDefinition + var primaryBeans []*gs.BeanDefinition for _, b := range foundBeans { if b.IsPrimary() { @@ -958,7 +923,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result *gs_bean.BeanDefinition + var result *gs.BeanDefinition if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -976,7 +941,7 @@ func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*gs_bean.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []*gs.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -1006,7 +971,7 @@ func filterBean(beans []*gs_bean.BeanDefinition, tag wireTag, t reflect.Type) (i return -1, fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) } -type byOrder []*gs_bean.BeanDefinition +type byOrder []*gs.BeanDefinition func (b byOrder) Len() int { return len(b) } func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } @@ -1024,7 +989,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []*gs_bean.BeanDefinition + var beans []*gs.BeanDefinition if et.Kind() == reflect.Interface && et.NumMethod() == 0 { beans = c.beans } else { @@ -1032,9 +997,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } { - var arr []*gs_bean.BeanDefinition + var arr []*gs.BeanDefinition for _, b := range beans { - if b.GetStatus() == gs_bean.Deleted { + if b.GetStatus() == gs.Deleted { continue } arr = append(arr, b) @@ -1045,9 +1010,9 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []*gs_bean.BeanDefinition - afterAny []*gs_bean.BeanDefinition - beforeAny []*gs_bean.BeanDefinition + anyBeans []*gs.BeanDefinition + afterAny []*gs.BeanDefinition + beforeAny []*gs.BeanDefinition ) foundAny := false @@ -1076,7 +1041,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]*gs_bean.BeanDefinition{}, beans[:index]...) + tmpBeans := append([]*gs.BeanDefinition{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -1085,7 +1050,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*gs_bean.BeanDefinition, 0, n) + arr := make([]*gs.BeanDefinition, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) diff --git a/gs/internal/gs_ctx/gs_bean.go b/gs/internal/gs_ctx/gs_bean.go index 1851961a..b19e991b 100755 --- a/gs/internal/gs_ctx/gs_bean.go +++ b/gs/internal/gs_ctx/gs_bean.go @@ -17,8 +17,15 @@ package gs_ctx import ( + "errors" + "fmt" + "reflect" + "runtime" + "strings" + + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_util" ) // type beanStatus int8 @@ -59,7 +66,7 @@ import ( // } type BeanInit interface { - OnInit(ctx Context) error + OnInit(ctx gs.Context) error } type BeanDestroy interface { @@ -258,99 +265,95 @@ type BeanDestroy interface { // } // return nil // } -// -// // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -// func NewBean(objOrCtor interface{}, ctorArgs ...gsarg.Arg) *gsbean.BeanDefinition { -// -// var v reflect.Value -// var fromValue bool -// var method bool -// var name string -// -// switch i := objOrCtor.(type) { -// case reflect.Value: -// fromValue = true -// v = i -// default: -// v = reflect.ValueOf(i) -// } -// -// t := v.Type() -// if !gsutil.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// if !v.IsValid() || v.IsNil() { -// panic(errors.New("bean can't be nil")) -// } -// -// const skip = 2 -// var f *gsarg.Callable -// _, file, line, _ := runtime.Caller(skip) -// -// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 -// if !fromValue && t.Kind() == reflect.Func { -// -// if !gsutil.IsConstructor(t) { -// t1 := "func(...)bean" -// t2 := "func(...)(bean, error)" -// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) -// } -// -// var err error -// f, err = gsarg.Bind(objOrCtor, ctorArgs, skip) -// if err != nil { -// panic(err) -// } -// -// out0 := t.Out(0) -// v = reflect.New(out0) -// if gsutil.IsBeanType(out0) { -// v = v.Elem() -// } -// -// t = v.Type() -// if !gsutil.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 -// fnPtr := reflect.ValueOf(objOrCtor).Pointer() -// fnInfo := runtime.FuncForPC(fnPtr) -// funcName := fnInfo.Name() -// name = funcName[strings.LastIndex(funcName, "/")+1:] -// name = name[strings.Index(name, ".")+1:] -// if name[0] == '(' { -// name = name[strings.Index(name, ".")+1:] -// } -// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 -// } -// -// if t.Kind() == reflect.Ptr && !gsutil.IsValueType(t.Elem()) { -// panic(errors.New("bean should be *val but not *ref")) -// } -// -// // Type.String() 一般返回 *pkg.Type 形式的字符串, -// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 -// if name == "" { -// s := strings.Split(t.String(), ".") -// name = strings.TrimPrefix(s[len(s)-1], "*") -// } -// -// return &gsbean.BeanDefinition{ -// t: t, -// v: v, -// f: f, -// name: name, -// typeName: gsutil.TypeName(t), -// status: gsbean.Default, -// method: method, -// file: file, -// line: line, -// } -// } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *gs_bean.BeanDefinition { - return gs_bean.NewBean(objOrCtor, ctorArgs...) +func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { + + var v reflect.Value + var fromValue bool + var method bool + var name string + + switch i := objOrCtor.(type) { + case reflect.Value: + fromValue = true + v = i + default: + v = reflect.ValueOf(i) + } + + t := v.Type() + if !gs_util.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + if !v.IsValid() || v.IsNil() { + panic(errors.New("bean can't be nil")) + } + + const skip = 2 + var f gs.Callable + _, file, line, _ := runtime.Caller(skip) + + // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 + if !fromValue && t.Kind() == reflect.Func { + + if !gs_util.IsConstructor(t) { + t1 := "func(...)bean" + t2 := "func(...)(bean, error)" + panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) + } + + var err error + f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) + if err != nil { + panic(err) + } + + out0 := t.Out(0) + v = reflect.New(out0) + if gs_util.IsBeanType(out0) { + v = v.Elem() + } + + t = v.Type() + if !gs_util.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 + fnPtr := reflect.ValueOf(objOrCtor).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + funcName := fnInfo.Name() + name = funcName[strings.LastIndex(funcName, "/")+1:] + name = name[strings.Index(name, ".")+1:] + if name[0] == '(' { + name = name[strings.Index(name, ".")+1:] + } + method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 + } + + if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { + panic(errors.New("bean should be *val but not *ref")) + } + + // Type.String() 一般返回 *pkg.Type 形式的字符串, + // 我们只取最后的类型名,如有需要请自定义 bean 名称。 + if name == "" { + s := strings.Split(t.String(), ".") + name = strings.TrimPrefix(s[len(s)-1], "*") + } + + return gs.NewBean(t, v, f, name, method, file, line) + //return &gs.BeanDefinition{ + // t: t, + // v: v, + // f: f, + // name: name, + // typeName: gs_util.TypeName(t), + // status: gs.Default, + // method: method, + // file: file, + // line: line, + //} } diff --git a/gs/internal/gs_ctx/gs_bean_test.go b/gs/internal/gs_ctx/gs_bean_test.go index 19acac4a..d89ee6fd 100644 --- a/gs/internal/gs_ctx/gs_bean_test.go +++ b/gs/internal/gs_ctx/gs_bean_test.go @@ -23,8 +23,7 @@ import ( "reflect" "testing" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_ctx" "github.com/go-spring/spring-core/gs/internal/gs_util" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" @@ -33,7 +32,7 @@ import ( ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 -func newBean(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) *gs_bean.BeanDefinition { +func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { return gs_ctx.NewBean(objOrCtor, ctorArgs...) } @@ -109,7 +108,7 @@ func TestIsFuncBeanType(t *testing.T) { func TestBeanDefinition_Match(t *testing.T) { data := []struct { - bd *gs_bean.BeanDefinition + bd *gs.BeanDefinition typeName string beanName string expect bool @@ -184,7 +183,7 @@ func TestObjectBean(t *testing.T) { t.Run("check name && typename", func(t *testing.T) { - data := map[*gs_bean.BeanDefinition]struct { + data := map[*gs.BeanDefinition]struct { name string typeName string }{ diff --git a/gs/internal/gs_ctx/gs_context.go b/gs/internal/gs_ctx/gs_context.go index 353461fa..795e2f11 100644 --- a/gs/internal/gs_ctx/gs_context.go +++ b/gs/internal/gs_ctx/gs_context.go @@ -21,6 +21,7 @@ import ( "reflect" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_util" ) @@ -97,7 +98,7 @@ func (c *container) Get(i interface{}, selectors ...gs_util.BeanSelector) error // Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 // 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 // 种方式,该函数执行完后都会返回 bean 对象的真实值。 -func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interface{}, error) { +func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { stack := newWiringStack() @@ -115,7 +116,7 @@ func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs_arg.Arg) (interfa return b.Interface(), nil } -func (c *container) Invoke(fn interface{}, args ...gs_arg.Arg) ([]interface{}, error) { +func (c *container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { if !gs_util.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") diff --git a/gs/internal/gs_ctx/gs_test.go b/gs/internal/gs_ctx/gs_test.go index f66e1afa..6480ab7a 100755 --- a/gs/internal/gs_ctx/gs_test.go +++ b/gs/internal/gs_ctx/gs_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_ctx" @@ -40,7 +41,7 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c gs_ctx.Container, fn func(gs_ctx.Context)) error { +func runTest(c gs.Container, fn func(gs_ctx.Context)) error { type PandoraAware struct{} c.Provide(func(p gs_ctx.Context) PandoraAware { fn(p) From f10f6af78c563b5b2d00c40136d1c7fc5a01f6c8 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 30 Dec 2024 19:49:38 +0800 Subject: [PATCH 13/75] code refactor --- gs/gs.go | 10 +- gs/gstest/gstest.go | 3 +- gs/internal/gs/bean.go | 48 +-- gs/internal/gs/gs.go | 22 +- gs/internal/gs_app/app.go | 15 +- gs/internal/gs_app/app_bootstrap.go | 7 +- gs/internal/gs_arg/arg.go | 5 +- gs/internal/gs_cond/cond.go | 19 +- gs/internal/gs_cond/cond_mock.go | 5 +- gs/internal/gs_ctx/gs.go | 81 +++-- gs/internal/gs_ctx/gs_bean.go | 3 +- gs/internal/gs_ctx/gs_bean_test.go | 8 +- gs/internal/gs_ctx/gs_context.go | 28 +- gs/internal/gs_ctx/gs_test.go | 472 +++++++++++++++++++--------- gs/internal/gs_util/type.go | 97 ++---- 15 files changed, 457 insertions(+), 366 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 403b2150..4648bb76 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -25,8 +25,9 @@ import ( ) type ( - Arg = gs.Arg - Context = gs.Context + Arg = gs.Arg + Context = gs.Context + BeanSelector = gs.BeanSelector ) var app = gs_app.NewApp() @@ -61,11 +62,6 @@ func OnProperty(key string, fn interface{}) { app.OnProperty(key, fn) } -// Property 参考 Container.Property 的解释。 -func Property(key string, value interface{}) { - app.Property(key, value) -} - // Accept 参考 Container.Accept 的解释。 func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.Accept(b) diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index df4fcbb6..53992111 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -21,7 +21,6 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs" - "github.com/go-spring/spring-core/gs/internal/gs_util" ) var ctx gs.Context @@ -63,7 +62,7 @@ func Bind(i interface{}, opts ...conf.BindArg) error { } // Get 获取对象 -func Get(i interface{}, selectors ...gs_util.BeanSelector) error { +func Get(i interface{}, selectors ...gs.BeanSelector) error { return ctx.Get(i, selectors...) } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 0b92c105..0dcc84b3 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -50,31 +50,31 @@ type BeanDefinition struct { file string // 注册点所在文件 line int // 注册点所在行数 - name string // 名称 - typeName string // 原始类型的全限定名 - status BeanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []gs_util.BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 + name string // 名称 + typeName string // 原始类型的全限定名 + status BeanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 } -func (d *BeanDefinition) GetName() string { return d.name } -func (d *BeanDefinition) GetTypeName() string { return d.typeName } -func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } -func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } -func (d *BeanDefinition) IsPrimary() bool { return d.primary } -func (d *BeanDefinition) IsMethod() bool { return d.method } -func (d *BeanDefinition) GetCond() Condition { return d.cond } -func (d *BeanDefinition) GetOrder() float32 { return d.order } -func (d *BeanDefinition) GetInit() interface{} { return d.init } -func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } -func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } -func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } +func (d *BeanDefinition) GetName() string { return d.name } +func (d *BeanDefinition) GetTypeName() string { return d.typeName } +func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } +func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } +func (d *BeanDefinition) IsPrimary() bool { return d.primary } +func (d *BeanDefinition) IsMethod() bool { return d.method } +func (d *BeanDefinition) GetCond() Condition { return d.cond } +func (d *BeanDefinition) GetOrder() float32 { return d.order } +func (d *BeanDefinition) GetInit() interface{} { return d.init } +func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } +func (d *BeanDefinition) GetDepends() []BeanSelector { return d.depends } +func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } // Type 返回 bean 的类型。 func (d *BeanDefinition) Type() reflect.Type { @@ -168,7 +168,7 @@ func (d *BeanDefinition) Order(order float32) *BeanDefinition { } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { +func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition { d.depends = append(d.depends, selectors...) return d } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index aa87951f..380f262c 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -5,10 +5,12 @@ import ( "reflect" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/internal/gs_util" ) +// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as +// `(*error)(nil)`, or a BeanDefinition value. +type BeanSelector interface{} + // ConditionContext defines some methods of IoC container that conditions use. type ConditionContext interface { // Has returns whether the IoC container has a property. @@ -17,7 +19,7 @@ type ConditionContext interface { // returns empty string when the IoC container doesn't have it. Prop(key string, opts ...conf.GetOption) string // Find returns bean definitions that matched with the bean selector. - Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) + Find(selector BeanSelector) ([]*BeanDefinition, error) } // Condition is used when registering a bean to determine whether it's valid. @@ -48,18 +50,6 @@ type Callable interface { Call(ctx ArgContext) ([]reflect.Value, error) } -type Container interface { - Context() context.Context - Properties() *dync.Properties - Property(key string, value interface{}) - Object(i interface{}) *BeanDefinition - Provide(ctor interface{}, args ...Arg) *BeanDefinition - Accept(b *BeanDefinition) *BeanDefinition - OnProperty(key string, fn interface{}) - Refresh(autoClear bool) error - Close() -} - // Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 // 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 // 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, @@ -75,7 +65,7 @@ type Context interface { Prop(key string, opts ...conf.GetOption) string Resolve(s string) (string, error) Bind(i interface{}, opts ...conf.BindArg) error - Get(i interface{}, selectors ...gs_util.BeanSelector) error + Get(i interface{}, selectors ...BeanSelector) error Wire(objOrCtor interface{}, ctorArgs ...Arg) (interface{}, error) Invoke(fn interface{}, args ...Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 05e0dbb4..d75a6d38 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -56,7 +56,7 @@ type tempApp struct { type App struct { *tempApp - c gs.Container + c *gs_ctx.Container b *Bootstrapper exitChan chan struct{} @@ -109,23 +109,23 @@ func (app *App) Start() (gs.Context, error) { // 执行命令行启动器 for _, r := range app.Runners { - r.Run(app.c.(gs.Context)) + r.Run(app.c) } // 通知应用启动事件 for _, event := range app.Events { - event.OnAppStart(app.c.(gs.Context)) + event.OnAppStart(app.c) } // 通知应用停止事件 - app.c.(gs.Context).Go(func(ctx context.Context) { + app.c.Go(func(ctx context.Context) { <-ctx.Done() for _, event := range app.Events { event.OnAppStop(context.Background()) } }) - return app.c.(gs.Context), nil + return app.c, nil } func (app *App) wait() { @@ -232,11 +232,6 @@ func (app *App) OnProperty(key string, fn interface{}) { app.c.OnProperty(key, fn) } -// Property 参考 Container.Property 的解释。 -func (app *App) Property(key string, value interface{}) { - app.c.Property(key, value) -} - // Accept 参考 Container.Accept 的解释。 func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.c.Accept(b) diff --git a/gs/internal/gs_app/app_bootstrap.go b/gs/internal/gs_app/app_bootstrap.go index 7f635f43..ccfc0559 100644 --- a/gs/internal/gs_app/app_bootstrap.go +++ b/gs/internal/gs_app/app_bootstrap.go @@ -24,7 +24,7 @@ import ( ) type Bootstrapper struct { - c gs.Container + c *gs_ctx.Container } func newBootstrap() *Bootstrapper { @@ -38,11 +38,6 @@ func (b *Bootstrapper) OnProperty(key string, fn interface{}) { b.c.OnProperty(key, fn) } -// Property 参考 Container.Property 的解释。 -func (b *Bootstrapper) Property(key string, value interface{}) { - b.c.Property(key, value) -} - // Object 参考 Container.Object 的解释。 func (b *Bootstrapper) Object(i interface{}) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index a249b056..e9b49500 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -25,6 +25,7 @@ import ( "reflect" "runtime" + "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" @@ -223,7 +224,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return reflect.ValueOf(g.v), nil case *optionArg: return g.call(ctx) - case gs_util.BeanDefinition: + case *gs.BeanDefinition: tag = g.ID() case string: tag = g @@ -232,7 +233,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine } // binds properties value by the "value" tag. - if gs_util.IsValueType(t) { + if conf.IsValueType(t) { if tag == "" { tag = "${}" } diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index 40f60f9a..6d895faf 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -27,7 +27,6 @@ import ( "github.com/go-spring/spring-core/expr" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -110,7 +109,7 @@ func (c *onMissingProperty) Matches(ctx gs.ConditionContext) (bool, error) { // onBean is a Condition that returns true when finding more than one beans. type onBean struct { - selector gs_util.BeanSelector + selector gs.BeanSelector } func (c *onBean) Matches(ctx gs.ConditionContext) (bool, error) { @@ -120,7 +119,7 @@ func (c *onBean) Matches(ctx gs.ConditionContext) (bool, error) { // onMissingBean is a Condition that returns true when finding no beans. type onMissingBean struct { - selector gs_util.BeanSelector + selector gs.BeanSelector } func (c *onMissingBean) Matches(ctx gs.ConditionContext) (bool, error) { @@ -130,7 +129,7 @@ func (c *onMissingBean) Matches(ctx gs.ConditionContext) (bool, error) { // onSingleBean is a Condition that returns true when finding only one bean. type onSingleBean struct { - selector gs_util.BeanSelector + selector gs.BeanSelector } func (c *onSingleBean) Matches(ctx gs.ConditionContext) (bool, error) { @@ -340,34 +339,34 @@ func (c *conditional) OnMissingProperty(name string) *conditional { // OnBean returns a conditional that starts with a Condition that returns true when // finding more than one beans. -func OnBean(selector gs_util.BeanSelector) *conditional { +func OnBean(selector gs.BeanSelector) *conditional { return New().OnBean(selector) } // OnBean adds a Condition that returns true when finding more than one beans. -func (c *conditional) OnBean(selector gs_util.BeanSelector) *conditional { +func (c *conditional) OnBean(selector gs.BeanSelector) *conditional { return c.On(&onBean{selector: selector}) } // OnMissingBean returns a conditional that starts with a Condition that returns // true when finding no beans. -func OnMissingBean(selector gs_util.BeanSelector) *conditional { +func OnMissingBean(selector gs.BeanSelector) *conditional { return New().OnMissingBean(selector) } // OnMissingBean adds a Condition that returns true when finding no beans. -func (c *conditional) OnMissingBean(selector gs_util.BeanSelector) *conditional { +func (c *conditional) OnMissingBean(selector gs.BeanSelector) *conditional { return c.On(&onMissingBean{selector: selector}) } // OnSingleBean returns a conditional that starts with a Condition that returns // true when finding only one bean. -func OnSingleBean(selector gs_util.BeanSelector) *conditional { +func OnSingleBean(selector gs.BeanSelector) *conditional { return New().OnSingleBean(selector) } // OnSingleBean adds a Condition that returns true when finding only one bean. -func (c *conditional) OnSingleBean(selector gs_util.BeanSelector) *conditional { +func (c *conditional) OnSingleBean(selector gs.BeanSelector) *conditional { return c.On(&onSingleBean{selector: selector}) } diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index e38ce4e6..38be6baf 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -14,7 +14,6 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_util" "go.uber.org/mock/gomock" ) @@ -43,10 +42,10 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) { +func (m *MockContext) Find(selector gs.BeanSelector) ([]gs.BeanDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]gs_util.BeanDefinition) + ret0, _ := ret[0].([]gs.BeanDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/gs/internal/gs_ctx/gs.go b/gs/internal/gs_ctx/gs.go index 05b0b163..f938b00a 100755 --- a/gs/internal/gs_ctx/gs.go +++ b/gs/internal/gs_ctx/gs.go @@ -55,21 +55,20 @@ type ContextAware struct { } type tempContainer struct { - initProperties *conf.Properties beans []*gs.BeanDefinition beansByName map[string][]*gs.BeanDefinition beansByType map[reflect.Type][]*gs.BeanDefinition mapOfOnProperty map[string]interface{} } -// container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion +// Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC // 容器在实现时比如 Spring 还引入了对属性 property 的处理。通常大家会用依赖注入统 // 述上面两种概念,但实际上使用属性绑定来描述对 property 的处理会更加合适,因此 // go-spring 严格区分了这两种概念,在描述对 bean 的处理时要么单独使用依赖注入或属 // 性绑定,要么同时使用依赖注入和属性绑定。 -type container struct { +type Container struct { *tempContainer ctx context.Context cancel context.CancelFunc @@ -82,14 +81,13 @@ type container struct { } // New 创建 IoC 容器。 -func New() *container { +func New() *Container { ctx, cancel := context.WithCancel(context.Background()) - return &container{ + return &Container{ ctx: ctx, cancel: cancel, p: dync.New(), tempContainer: &tempContainer{ - initProperties: conf.NewProperties(), beansByName: make(map[string][]*gs.BeanDefinition), beansByType: make(map[reflect.Type][]*gs.BeanDefinition), mapOfOnProperty: make(map[string]interface{}), @@ -98,11 +96,11 @@ func New() *container { } // Context 返回 IoC 容器的 ctx 对象。 -func (c *container) Context() context.Context { +func (c *Container) Context() context.Context { return c.ctx } -func (c *container) Properties() *dync.Properties { +func (c *Container) Properties() *dync.Properties { return c.p } @@ -111,14 +109,14 @@ func validOnProperty(fn interface{}) error { if t.Kind() != reflect.Func { return errors.New("fn should be a func(value_type)") } - if t.NumIn() != 1 || !gs_util.IsValueType(t.In(0)) || t.NumOut() != 0 { + if t.NumIn() != 1 || !conf.IsValueType(t.In(0)) || t.NumOut() != 0 { return errors.New("fn should be a func(value_type)") } return nil } // OnProperty 当 key 对应的属性值准备好后发送一个通知。 -func (c *container) OnProperty(key string, fn interface{}) { +func (c *Container) OnProperty(key string, fn interface{}) { err := validOnProperty(fn) if err != nil { panic(err) @@ -126,16 +124,7 @@ func (c *container) OnProperty(key string, fn interface{}) { c.mapOfOnProperty[key] = fn } -// Property 设置 key 对应的属性值,如果 key 对应的属性值已经存在则 Set 方法会 -// 覆盖旧值。Set 方法除了支持 string 类型的属性值,还支持 int、uint、bool 等 -// 其他基础数据类型的属性值。特殊情况下,Set 方法也支持 slice 、map 与基础数据 -// 类型组合构成的属性值,其处理方式是将组合结构层层展开,可以将组合结构看成一棵树, -// 那么叶子结点的路径就是属性的 key,叶子结点的值就是属性的值。 -func (c *container) Property(key string, value interface{}) { - c.initProperties.Set(key, value) -} - -func (c *container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { +func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } @@ -144,12 +133,12 @@ func (c *container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Object(i interface{}) *gs.BeanDefinition { +func (c *Container) Object(i interface{}) *gs.BeanDefinition { return c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { +func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return c.Accept(NewBean(ctor, args...)) } @@ -273,19 +262,21 @@ func (s *wiringStack) sortDestroyers() []func() { return ret } -func (c *container) clear() { +func (c *Container) clear() { c.tempContainer = nil } -func (c *container) Refresh(autoClear bool) (err error) { +func (c *Container) RefreshProperties(p *conf.Properties) error { + return c.p.Refresh(p) +} + +func (c *Container) Refresh(autoClear bool) (err error) { if c.state != Unrefreshed { - return errors.New("container already refreshed") + return errors.New("Container already refreshed") } c.state = RefreshInit - c.p.Refresh(c.initProperties) - // start := time.Now() c.Object(c).Export((*gs.Context)(nil)) @@ -373,11 +364,11 @@ func (c *container) Refresh(autoClear bool) (err error) { c.clear() } - // c.logger.Info("container refreshed successfully") + // c.logger.Info("Container refreshed successfully") return nil } -func (c *container) registerBean(b *gs.BeanDefinition) { +func (c *Container) registerBean(b *gs.BeanDefinition) { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) @@ -388,7 +379,7 @@ func (c *container) registerBean(b *gs.BeanDefinition) { } // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 -func (c *container) resolveBean(b *gs.BeanDefinition) error { +func (c *Container) resolveBean(b *gs.BeanDefinition) error { if b.GetStatus() >= gs.Resolving { return nil @@ -477,7 +468,7 @@ func (tag wireTag) String() string { return b.String() } -func toWireTag(selector gs_util.BeanSelector) wireTag { +func toWireTag(selector gs.BeanSelector) wireTag { switch s := selector.(type) { case string: return parseWireTag(s) @@ -503,7 +494,7 @@ func toWireString(tags []wireTag) string { // findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs.BeanDefinition, error) { +func (c *Container) findBean(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { finder := func(fn func(*gs.BeanDefinition) bool) ([]*gs.BeanDefinition, error) { var result []*gs.BeanDefinition @@ -557,7 +548,7 @@ func (c *container) findBean(selector gs_util.BeanSelector) ([]*gs.BeanDefinitio // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { +func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { if b.GetStatus() == gs.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) @@ -655,7 +646,7 @@ func (c *container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { } type argContext struct { - c *container + c *Container stack *wiringStack } @@ -672,7 +663,7 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { } // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { if b.F == nil { return b.Value(), nil @@ -686,7 +677,7 @@ func (c *container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (refl // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 if val := out[0]; gs_util.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 - if !val.IsNil() && val.Kind() == reflect.Interface && gs_util.IsValueType(val.Elem().Type()) { + if !val.IsNil() && val.Kind() == reflect.Interface && conf.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) v.Elem().Set(val.Elem()) b.Value().Set(v) @@ -710,7 +701,7 @@ func (c *container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (refl } // wireBeanValue 对 v 进行属性绑定和依赖注入,v 在传入时应该是一个已经初始化的值。 -func (c *container) wireBeanValue(v reflect.Value, t reflect.Type, stack *wiringStack) error { +func (c *Container) wireBeanValue(v reflect.Value, t reflect.Type, stack *wiringStack) error { if v.Kind() == reflect.Ptr { v = v.Elem() @@ -732,7 +723,7 @@ func (c *container) wireBeanValue(v reflect.Value, t reflect.Type, stack *wiring } // wireStruct 对结构体进行依赖注入,需要注意的是这里不需要进行属性绑定。 -func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *wiringStack) error { +func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *wiringStack) error { for i := 0; i < t.NumField(); i++ { ft := t.Field(i) @@ -800,7 +791,7 @@ func (c *container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar return nil } -func (c *container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { +func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { // tag 预处理,可能通过属性值进行指定。 if strings.HasPrefix(tag, "${") { @@ -824,7 +815,7 @@ func (c *container) wireByTag(v reflect.Value, tag string, stack *wiringStack) e return c.autowire(v, tags, tag == "?", stack) } -func (c *container) autowire(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { +func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { switch v.Kind() { case reflect.Map, reflect.Slice, reflect.Array: return c.collectBeans(v, tags, nullable, stack) @@ -840,7 +831,7 @@ func (c *container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } // getBean 获取 tag 对应的 bean 然后赋值给 v,因此 v 应该是一个未初始化的值。 -func (c *container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) error { +func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) error { if !v.IsValid() { return fmt.Errorf("receiver must be ref type, bean:%q", tag) @@ -977,7 +968,7 @@ func (b byOrder) Len() int { return len(b) } func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } func (b byOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { +func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { t := v.Type() if t.Kind() != reflect.Slice && t.Kind() != reflect.Map { @@ -1096,7 +1087,7 @@ func (c *container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, // Close 关闭容器,此方法必须在 Refresh 之后调用。该方法会触发 ctx 的 Done 信 // 号,然后等待所有 goroutine 结束,最后按照被依赖先销毁的原则执行所有的销毁函数。 -func (c *container) Close() { +func (c *Container) Close() { c.cancel() c.wg.Wait() @@ -1107,12 +1098,12 @@ func (c *container) Close() { f() } - // c.logger.Info("container closed") + // c.logger.Info("Container closed") } // Go 创建安全可等待的 goroutine,fn 要求的 ctx 对象由 IoC 容器提供,当 IoC 容 // 器关闭时 ctx会 发出 Done 信号, fn 在接收到此信号后应当立即退出。 -func (c *container) Go(fn func(ctx context.Context)) { +func (c *Container) Go(fn func(ctx context.Context)) { c.wg.Add(1) go func() { defer c.wg.Done() diff --git a/gs/internal/gs_ctx/gs_bean.go b/gs/internal/gs_ctx/gs_bean.go index b19e991b..b7462618 100755 --- a/gs/internal/gs_ctx/gs_bean.go +++ b/gs/internal/gs_ctx/gs_bean.go @@ -23,6 +23,7 @@ import ( "runtime" "strings" + "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_util" @@ -333,7 +334,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 } - if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { + if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { panic(errors.New("bean should be *val but not *ref")) } diff --git a/gs/internal/gs_ctx/gs_bean_test.go b/gs/internal/gs_ctx/gs_bean_test.go index d89ee6fd..e344435e 100644 --- a/gs/internal/gs_ctx/gs_bean_test.go +++ b/gs/internal/gs_ctx/gs_bean_test.go @@ -193,7 +193,7 @@ func TestObjectBean(t *testing.T) { newBean(newHistoryTeacher("")): { "historyTeacher", - "github.com/go-spring/spring-core/gs/gs_test.historyTeacher", + "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.historyTeacher", }, newBean(new(pkg2.SamePkg)): { @@ -217,10 +217,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.Type().String(), "*gs_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_ctx_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.Type().String(), "*gs_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_ctx_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -236,7 +236,7 @@ func TestConstructorBean(t *testing.T) { interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.Type().String(), "gs_test.Teacher") + assert.Equal(t, bd.Type().String(), "gs_ctx_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_ctx/gs_context.go b/gs/internal/gs_ctx/gs_context.go index 795e2f11..7d90180e 100644 --- a/gs/internal/gs_ctx/gs_context.go +++ b/gs/internal/gs_ctx/gs_context.go @@ -26,38 +26,30 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_util" ) -func (c *container) Keys() []string { +func (c *Container) Keys() []string { return c.p.Keys() } -func (c *container) Has(key string) bool { +func (c *Container) Has(key string) bool { return c.p.Has(key) } -func (c *container) Prop(key string, opts ...conf.GetOption) string { +func (c *Container) Prop(key string, opts ...conf.GetOption) string { return c.p.Get(key, opts...) } -func (c *container) Resolve(s string) (string, error) { +func (c *Container) Resolve(s string) (string, error) { return c.p.Resolve(s) } -func (c *container) Bind(i interface{}, opts ...conf.BindArg) error { +func (c *Container) Bind(i interface{}, opts ...conf.BindArg) error { return c.p.Bind(i, opts...) } // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的,即未被 // 标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *container) Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinition, error) { - beans, err := c.findBean(selector) - if err != nil { - return nil, err - } - var ret []gs_util.BeanDefinition - for _, b := range beans { - ret = append(ret, b) - } - return ret, nil +func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { + return c.findBean(selector) } // Get 根据类型和选择器获取符合条件的 bean 对象。当 i 是一个基础类型的 bean 接收 @@ -69,7 +61,7 @@ func (c *container) Find(selector gs_util.BeanSelector) ([]gs_util.BeanDefinitio // 工作模式称为自动模式,否则根据传入的选择器列表进行排序,这种工作模式成为指派模式。 // 该方法和 Find 方法的区别是该方法保证返回的所有 bean 对象都已经完成属性绑定和依 // 赖注入,而 Find 方法只能保证返回的 bean 对象是有效的,即未被标记为删除的。 -func (c *container) Get(i interface{}, selectors ...gs_util.BeanSelector) error { +func (c *Container) Get(i interface{}, selectors ...gs.BeanSelector) error { if i == nil { return errors.New("i can't be nil") @@ -98,7 +90,7 @@ func (c *container) Get(i interface{}, selectors ...gs_util.BeanSelector) error // Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 // 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 // 种方式,该函数执行完后都会返回 bean 对象的真实值。 -func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { +func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { stack := newWiringStack() @@ -116,7 +108,7 @@ func (c *container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} return b.Interface(), nil } -func (c *container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { +func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { if !gs_util.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") diff --git a/gs/internal/gs_ctx/gs_test.go b/gs/internal/gs_ctx/gs_test.go index 6480ab7a..29a1dd60 100755 --- a/gs/internal/gs_ctx/gs_test.go +++ b/gs/internal/gs_ctx/gs_test.go @@ -32,7 +32,6 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_ctx" - "github.com/go-spring/spring-core/gs/internal/gs_util" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" @@ -41,9 +40,9 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c gs.Container, fn func(gs_ctx.Context)) error { +func runTest(c *gs_ctx.Container, fn func(gs.Context)) error { type PandoraAware struct{} - c.Provide(func(p gs_ctx.Context) PandoraAware { + c.Provide(func(p gs.Context) PandoraAware { fn(p) return PandoraAware{} }) @@ -150,7 +149,7 @@ func TestApplicationContext_AutoWireBeans(t *testing.T) { b := TestBincoreng{1} c.Object(&b).Name("struct_ptr").Export((*fmt.Stringer)(nil)) - err := runTest(c, func(p gs_ctx.Context) {}) + err := runTest(c, func(p gs.Context) {}) assert.Nil(t, err) assert.Equal(t, len(obj.MapTyType), 4) @@ -203,25 +202,29 @@ type Setting struct { func TestApplicationContext_ValueTag(t *testing.T) { c := gs_ctx.New() - c.Property("int", int(3)) - c.Property("uint", uint(3)) - c.Property("float", float32(3)) - c.Property("complex", complex(3, 0)) - c.Property("string", "3") - c.Property("bool", true) + p := conf.NewProperties() + p.Set("int", int(3)) + p.Set("uint", uint(3)) + p.Set("float", float32(3)) + p.Set("complex", complex(3, 0)) + p.Set("string", "3") + p.Set("bool", true) setting := &Setting{} c.Object(setting) - c.Property("sub.int", int(4)) - c.Property("sub.sub.int", int(5)) - c.Property("sub_sub.int", int(6)) + p.Set("sub.int", int(4)) + p.Set("sub.sub.int", int(5)) + p.Set("sub_sub.int", int(6)) - c.Property("int_slice", []int{1, 2}) - c.Property("string_slice", []string{"1", "2"}) + p.Set("int_slice", []int{1, 2}) + p.Set("string_slice", []string{"1", "2"}) // c.Property("float_slice", []float64{1, 2}) - err := c.Refresh(false) + err := c.RefreshProperties(p) + assert.Nil(t, err) + + err = c.Refresh(false) assert.Nil(t, err) fmt.Printf("%+v\n", setting) @@ -325,29 +328,28 @@ type DbConfig struct { } func TestApplicationContext_TypeConverter(t *testing.T) { + prop, _ := conf.Load("../../testdata/config/application.yaml") + c := gs_ctx.New() - { - p, _ := conf.Load("testdata/config/application.yaml") - for _, key := range p.Keys() { - c.Property(key, p.Get(key)) - } - } b := &EnvEnumBean{} c.Object(b) - c.Property("env.type", "test") + prop.Set("env.type", "test") p := &PointBean{} c.Object(p) conf.RegisterConverter(PointConverter) - c.Property("point", "(7,5)") + prop.Set("point", "(7,5)") dbConfig := &DbConfig{} c.Object(dbConfig) - err := c.Refresh(false) + err := c.RefreshProperties(prop) + assert.Nil(t, err) + + err = c.Refresh(false) assert.Nil(t, err) assert.Equal(t, b.EnvType, ENV_TEST) @@ -415,7 +417,7 @@ func (d *DiffPkgTwo) Package() { type DiffPkgHolder struct { // Pkg `autowire:"same"` // 如果两个 Object 不小心重名了,也会找到多个符合条件的 Object - Pkg `autowire:"github.com/go-spring/spring-core/gs/gs_test.DiffPkgTwo:same"` + Pkg `autowire:"github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.DiffPkgTwo:same"` } func TestApplicationContext_DiffNameBean(t *testing.T) { @@ -431,17 +433,15 @@ func TestApplicationContext_LoadProperties(t *testing.T) { c := gs_ctx.New() - p, _ := conf.Load("testdata/config/application.yaml") + prop, _ := conf.Load("../../testdata/config/application.yaml") + p, _ := conf.Load("../../testdata/config/application.properties") for _, key := range p.Keys() { - c.Property(key, p.Get(key)) + prop.Set(key, p.Get(key)) } - p, _ = conf.Load("testdata/config/application.properties") - for _, key := range p.Keys() { - c.Property(key, p.Get(key)) - } + c.RefreshProperties(prop) - err := runTest(c, func(ctx gs_ctx.Context) { + err := runTest(c, func(ctx gs.Context) { assert.Equal(t, ctx.Prop("yaml.list[0]"), "1") assert.Equal(t, ctx.Prop("yaml.list[1]"), "2") assert.Equal(t, ctx.Prop("spring.application.name"), "test") @@ -453,7 +453,7 @@ func TestApplicationContext_Get(t *testing.T) { t.Run("panic", func(t *testing.T) { c := gs_ctx.New() - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { { var s fmt.Stringer err := p.Get(s) @@ -473,7 +473,7 @@ func TestApplicationContext_Get(t *testing.T) { c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).Export((*Grouper)(nil)) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var two *BeanTwo err := p.Get(&two) @@ -507,10 +507,10 @@ func TestApplicationContext_Get(t *testing.T) { err = p.Get(&grouper, ":BeanTwo") assert.Nil(t, err) - err = p.Get(&two, "github.com/go-spring/spring-core/gs/gs_test.BeanTwo:BeanTwo") + err = p.Get(&two, "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.BeanTwo:BeanTwo") assert.Nil(t, err) - err = p.Get(&grouper, "github.com/go-spring/spring-core/gs/gs_test.BeanTwo:BeanTwo") + err = p.Get(&grouper, "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.BeanTwo:BeanTwo") assert.Nil(t, err) err = p.Get(&two, "xxx:BeanTwo") @@ -618,9 +618,10 @@ func NewPtrStudent(teacher Teacher, room string) *Student { } func TestApplicationContext_RegisterBeanFn(t *testing.T) { - c := gs_ctx.New() + prop := conf.NewProperties() + prop.Set("room", "Class 3 Grade 1") - c.Property("room", "Class 3 Grade 1") + c := gs_ctx.New() // 用接口注册时实际使用的是原始类型 c.Object(Teacher(newHistoryTeacher(""))).Export((*Teacher)(nil)) @@ -634,7 +635,8 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { fmt.Println(teacher.Course()) }).Name("newTeacher") - err := runTest(c, func(p gs_ctx.Context) { + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var st1 *Student err := p.Get(&st1, "st1") @@ -675,7 +677,7 @@ func TestApplicationContext_Profile(t *testing.T) { t.Run("bean:_c:", func(t *testing.T) { c := gs_ctx.New() c.Object(&BeanZero{5}) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -684,10 +686,14 @@ func TestApplicationContext_Profile(t *testing.T) { }) t.Run("bean:_c:test", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("spring.profiles.active", "test") + c := gs_ctx.New() - c.Property("spring.profiles.active", "test") c.Object(&BeanZero{5}) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -711,7 +717,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { t.Run("dependsOn", func(t *testing.T) { - dependsOn := []gs_util.BeanSelector{ + dependsOn := []gs.BeanSelector{ (*BeanOne)(nil), // 通过类型定义查找 "github.com/go-spring/spring-core/gs/gs_test.BeanZero:BeanZero", } @@ -753,7 +759,7 @@ func TestApplicationContext_Primary(t *testing.T) { c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -768,7 +774,7 @@ func TestApplicationContext_Primary(t *testing.T) { c.Object(&BeanZero{6}).Name("zero_6").Primary() c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var b *BeanTwo err := p.Get(&b) assert.Nil(t, err) @@ -828,10 +834,14 @@ func (m localManager) Cluster() string { func TestApplicationContext_RegisterBeanFn2(t *testing.T) { t.Run("ptr manager", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("manager.version", "1.0.0") + c := gs_ctx.New() - c.Property("manager.version", "1.0.0") c.Provide(NewPtrManager) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var m Manager err := p.Get(&m) @@ -847,13 +857,16 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("manager.version", "1.0.0") + c := gs_ctx.New() - c.Property("manager.version", "1.0.0") + c.RefreshProperties(prop) bd := c.Provide(NewManager) assert.Equal(t, bd.BeanName(), "NewManager") - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var m Manager err := p.Get(&m) @@ -867,25 +880,35 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("manager.version", "1.0.0") c := gs_ctx.New() - c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetError) + c.RefreshProperties(prop) err := c.Refresh(false) assert.Error(t, err, "gs_test.go:\\d* error") }) t.Run("manager return error nil", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("manager.version", "1.0.0") + c := gs_ctx.New() - c.Property("manager.version", "1.0.0") c.Provide(NewManagerRetErrorNil) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) }) t.Run("manager return nil", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("manager.version", "1.0.0") + c := gs_ctx.New() - c.Property("manager.version", "1.0.0") c.Provide(NewNullPtrManager) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Error(t, err, "return nil") }) @@ -942,7 +965,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { c := gs_ctx.New() c.Object(new(callDestroy)).Init((*callDestroy).Init) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -960,10 +983,14 @@ func TestRegisterBean_InitFunc(t *testing.T) { assert.Error(t, err, "error") } + prop := conf.NewProperties() + prop.Set("int", 0) + c := gs_ctx.New() - c.Property("int", 0) c.Object(&callDestroy{}).Init((*callDestroy).InitWithError) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -975,7 +1002,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call interface init", func(t *testing.T) { c := gs_ctx.New() c.Provide(func() destroyable { return new(callDestroy) }).Init(destroyable.Init) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -993,10 +1020,14 @@ func TestRegisterBean_InitFunc(t *testing.T) { assert.Error(t, err, "error") } + prop := conf.NewProperties() + prop.Set("int", 0) + c := gs_ctx.New() - c.Property("int", 0) c.Provide(func() destroyable { return &callDestroy{} }).Init(destroyable.InitWithError) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1008,7 +1039,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call nested init", func(t *testing.T) { c := gs_ctx.New() c.Object(new(nestedCallDestroy)).Init((*nestedCallDestroy).Init) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *nestedCallDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1022,7 +1053,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { c.Object(&nestedDestroyable{ destroyable: new(callDestroy), }).Init((*nestedDestroyable).Init) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *nestedDestroyable err := p.Get(&d) assert.Nil(t, err) @@ -1037,10 +1068,14 @@ type RecoresCluster struct { } func TestApplicationContext_ValueBincoreng(t *testing.T) { + prop := conf.NewProperties() + prop.Set("redis.endpoints", "redis://localhost:6379") + c := gs_ctx.New() - c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cluster *RecoresCluster err := p.Get(&cluster) fmt.Println(cluster) @@ -1056,13 +1091,13 @@ func TestApplicationContext_Collect(t *testing.T) { c.Object(&struct { Events []ServerInterface `autowire:""` }{}) - err := runTest(c, func(ctx gs_ctx.Context) {}) + err := runTest(c, func(ctx gs.Context) {}) assert.Error(t, err, "no beans collected for \"\"") }) t.Run("", func(t *testing.T) { c := gs_ctx.New() - err := runTest(c, func(ctx gs_ctx.Context) { + err := runTest(c, func(ctx gs.Context) { var Events []ServerInterface err := ctx.Get(&Events) assert.Error(t, err, "no beans collected for \"\"") @@ -1075,13 +1110,13 @@ func TestApplicationContext_Collect(t *testing.T) { c.Object(&struct { Events []ServerInterface `autowire:"?"` }{}) - err := runTest(c, func(ctx gs_ctx.Context) {}) + err := runTest(c, func(ctx gs.Context) {}) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { c := gs_ctx.New() - err := runTest(c, func(ctx gs_ctx.Context) { + err := runTest(c, func(ctx gs.Context) { var Events []ServerInterface err := ctx.Get(&Events, "?") assert.Nil(t, err) @@ -1090,11 +1125,15 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("redis.endpoints", "redis://localhost:6379") + c := gs_ctx.New() - c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("one") c.Object(new(RecoresCluster)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var rcs []*RecoresCluster err := p.Get(&rcs) assert.Nil(t, err) @@ -1104,13 +1143,14 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("redis.endpoints", "redis://localhost:6379") c := gs_ctx.New() - c.Property("redis.endpoints", "redis://localhost:6379") c.Object(new(RecoresCluster)).Name("a").Order(1) c.Object(new(RecoresCluster)).Name("b").Order(2) - intBean := c.Provide(func(p gs_ctx.Context) func() { + intBean := c.Provide(func(p gs.Context) func() { var rcs []*RecoresCluster err := p.Get(&rcs) @@ -1123,6 +1163,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) }) @@ -1230,10 +1271,14 @@ func TestOptionPattern(t *testing.T) { func TestOptionConstructorArg(t *testing.T) { t.Run("option default", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("president", "CaiYuanPei") + c := gs_ctx.New() - c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1245,10 +1290,14 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withClassName", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("president", "CaiYuanPei") + c := gs_ctx.New() - c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1261,13 +1310,17 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("class_name", "二年级03班") + prop.Set("president", "CaiYuanPei") + c := gs_ctx.New() - c.Property("class_name", "二年级03班") - c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gs_arg.Option(withStudents)) c.Object(new(Student)).Name("Student1") c.Object(new(Student)).Name("Student2") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1280,9 +1333,11 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents withClassName", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("class_name", "二年级06班") + prop.Set("president", "CaiYuanPei") + c := gs_ctx.New() - c.Property("class_name", "二年级06班") - c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gs_arg.Option(withStudents), gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), @@ -1292,7 +1347,9 @@ func TestOptionConstructorArg(t *testing.T) { ) c.Object(&Student{}).Name("Student1") c.Object(&Student{}).Name("Student2") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -1349,11 +1406,15 @@ type Service struct { func TestApplicationContext_RegisterMethodBean(t *testing.T) { t.Run("method bean", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -1372,11 +1433,15 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean condition", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Object(new(Server)).On(gs_cond.Not(gs_cond.OK())) bd := c.Provide((*Server).Consumer, parent) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -1391,11 +1456,15 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean arg", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) c.Provide((*Server).ConsumerArg, parent, "${i:=9}") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -1413,12 +1482,16 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean wire to other bean", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Provide(NewServerInterface) c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var si ServerInterface err := p.Get(&si) @@ -1463,11 +1536,14 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { } }() + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Object(new(Server)).DependsOn("Service") c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) + c.RefreshProperties(prop) err := c.Refresh(false) if err != nil { panic(err) @@ -1478,10 +1554,14 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean autowire", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") c.Object(new(Server)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) assert.Nil(t, err) @@ -1491,11 +1571,15 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*Server)(nil)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -1513,20 +1597,28 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type error", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*int)(nil)) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Error(t, err, "can't find bean, bean:\"int:\" type:\"\\*gs_test.Server\"") }) t.Run("method bean selector beanId", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "Server") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -1544,10 +1636,14 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId error", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "NULL") + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Error(t, err, "can't find bean, bean:\"NULL\" type:\"\\*gs_test.Server\"") }) @@ -1573,11 +1669,15 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { return 0, errors.New("error level") }) - c.Property("time", "2018-12-20>>2006-01-02") - c.Property("duration", "1h") - c.Property("level", "debug") - c.Property("complex", "1+i") + prop := conf.NewProperties() + prop.Set("time", "2018-12-20") + prop.Set("duration", "1h") + prop.Set("level", "debug") + prop.Set("complex", "1+i") + c.Object(&config) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) @@ -1712,12 +1812,16 @@ func NewNilVarObj(i interface{}, options ...VarOptionFunc) *VarObj { func TestApplicationContext_RegisterOptionBean(t *testing.T) { t.Run("nil param 0", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("var.obj", "description") + c := gs_ctx.New() - c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewNilVarObj, gs_arg.Nil()) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1728,12 +1832,16 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 1", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("var.obj", "description") + c := gs_ctx.New() - c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, "${var.obj}", gs_arg.Option(withVar, "v1")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1745,12 +1853,16 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 2", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("var.obj", "description") + c := gs_ctx.New() - c.Property("var.obj", "description") c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, gs_arg.Value("description"), gs_arg.Option(withVar, "v1", "v2")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var obj *VarObj err := p.Get(&obj) assert.Nil(t, err) @@ -1767,7 +1879,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1")) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1781,7 +1893,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1", "v2")) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var obj *VarInterfaceObj err := p.Get(&obj) assert.Nil(t, err) @@ -1832,7 +1944,7 @@ func TestApplicationContext_Close(t *testing.T) { c := gs_ctx.New() d := new(callDestroy) c.Object(d).Destroy((*callDestroy).Destroy) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1849,7 +1961,7 @@ func TestApplicationContext_Close(t *testing.T) { c := gs_ctx.New() d := &callDestroy{i: 1} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1864,7 +1976,7 @@ func TestApplicationContext_Close(t *testing.T) { c := gs_ctx.New() d := &callDestroy{} c.Object(d).Destroy((*callDestroy).DestroyWithError) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d *callDestroy err := p.Get(&d) assert.Nil(t, err) @@ -1878,7 +1990,7 @@ func TestApplicationContext_Close(t *testing.T) { t.Run("call interface destroy", func(t *testing.T) { c := gs_ctx.New() bd := c.Provide(func() destroyable { return new(callDestroy) }).Destroy(destroyable.Destroy) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1895,7 +2007,7 @@ func TestApplicationContext_Close(t *testing.T) { { c := gs_ctx.New() bd := c.Provide(func() destroyable { return &callDestroy{i: 1} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1908,10 +2020,14 @@ func TestApplicationContext_Close(t *testing.T) { // nil { + prop := conf.NewProperties() + prop.Set("int", 0) + c := gs_ctx.New() - c.Property("int", 0) bd := c.Provide(func() destroyable { return &callDestroy{} }).Destroy(destroyable.DestroyWithError) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var d destroyable err := p.Get(&d) assert.Nil(t, err) @@ -1941,7 +2057,7 @@ func TestApplicationContext_NestedAutowireBean(t *testing.T) { c.Object(&PtrNestedAutowireBean{ SubNestedAutowireBean: new(SubNestedAutowireBean), }) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var b *NestedAutowireBean err := p.Get(&b) @@ -1978,13 +2094,15 @@ func TestApplicationContext_NestValueField(t *testing.T) { t.Run("private", func(t *testing.T) { - c := gs_ctx.New() - - c.Property("sdk.wx.auto-create", true) - c.Property("sdk.wx.enable", true) + prop := conf.NewProperties() + prop.Set("sdk.wx.auto-create", true) + prop.Set("sdk.wx.enable", true) + c := gs_ctx.New() c.Object(new(wxChannel)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var channel *wxChannel err := p.Get(&channel) assert.Nil(t, err) @@ -1995,11 +2113,15 @@ func TestApplicationContext_NestValueField(t *testing.T) { }) t.Run("public", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("sdk.wx.auto-create", true) + prop.Set("sdk.wx.enable", true) + c := gs_ctx.New() - c.Property("sdk.wx.auto-create", true) - c.Property("sdk.wx.enable", true) c.Object(new(WXChannel)) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var channel *WXChannel err := p.Get(&channel) assert.Nil(t, err) @@ -2120,12 +2242,16 @@ func TestApplicationContext_Properties(t *testing.T) { MapA map[string]string `value:"${map_a:=}"` }{} + prop := conf.NewProperties() + prop.Set("map_a.nba", "nba") + prop.Set("map_a.cba", "cba") + prop.Set("int_a", "3") + prop.Set("int_b", "4") + c := gs_ctx.New() - c.Property("map_a.nba", "nba") - c.Property("map_a.cba", "cba") - c.Property("int_a", "3") - c.Property("int_b", "4") c.Object(&obj) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) @@ -2198,7 +2324,7 @@ func (factory *ObjFactory) NewObj(i int) *Obj { return &Obj{i: i} } func TestApplicationContext_CreateBean(t *testing.T) { c := gs_ctx.New() c.Object(&ObjFactory{}) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { b, err := p.Wire((*ObjFactory).NewObj, gs_arg.R1("${i:=5}")) fmt.Println(b, err) }) @@ -2219,7 +2345,7 @@ func TestDefaultSpringContext(t *testing.T) { On(gs_cond.OK()), ) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2228,10 +2354,14 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:test", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("spring.profiles.active", "test") + c := gs_ctx.New() - c.Property("spring.profiles.active", "test") c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) assert.Nil(t, err) @@ -2240,10 +2370,14 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:stable", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("spring.profiles.active", "stable") + c := gs_ctx.New() - c.Property("spring.profiles.active", "stable") c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var b *BeanZero err := p.Get(&b) assert.Error(t, err, "can't find bean, bean:\"\"") @@ -2253,14 +2387,18 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("option withClassName Condition", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("president", "CaiYuanPei") + prop.Set("class_floor", 2) + c := gs_ctx.New() - c.Property("president", "CaiYuanPei") - c.Property("class_floor", 2) c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", ).On(gs_cond.OnProperty("class_name_enable"))) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2273,16 +2411,20 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("option withClassName Apply", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("president", "CaiYuanPei") + onProperty := gs_cond.OnProperty("class_name_enable") c := gs_ctx.New() - c.Property("president", "CaiYuanPei") c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", ).On(onProperty), ) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var cls *ClassRoom err := p.Get(&cls) assert.Nil(t, err) @@ -2295,11 +2437,15 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("method bean cond", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("server.version", "1.0.0") + c := gs_ctx.New() - c.Property("server.version", "1.0.0") parent := c.Object(new(Server)) c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { var s *Server err := p.Get(&s) @@ -2343,7 +2489,7 @@ func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { c.Object(new(BeanTwo)).On(gs_cond.OnBean("BeanOne")) c.Object(new(BeanTwo)).Name("another_two").On(gs_cond.OnBean("Null")) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var two *BeanTwo err := p.Get(&two) @@ -2362,7 +2508,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { c.Object(new(BeanOne)) c.Object(new(BeanTwo)).On(gs_cond.OnMissingBean("BeanOne")) c.Object(new(BeanTwo)).Name("another_two").On(gs_cond.OnMissingBean("Null")) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var two *BeanTwo err := p.Get(&two) @@ -2558,10 +2704,14 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { func TestApplicationContext_Invoke(t *testing.T) { t.Run("not run", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("version", "v0.0.1") + c := gs_ctx.New() c.Object(func() {}) - c.Property("version", "v0.0.1") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { _, _ = p.Invoke(func(f func(), version string) { fmt.Println("version:", version) }, "", "${version}") @@ -2570,11 +2720,15 @@ func TestApplicationContext_Invoke(t *testing.T) { }) t.Run("run", func(t *testing.T) { + prop := conf.NewProperties() + prop.Set("version", "v0.0.1") + prop.Set("spring.profiles.active", "dev") + c := gs_ctx.New() c.Object(func() {}) - c.Property("version", "v0.0.1") - c.Property("spring.profiles.active", "dev") - err := runTest(c, func(p gs_ctx.Context) { + + c.RefreshProperties(prop) + err := runTest(c, func(p gs.Context) { fn := func(f func(), version string) { fmt.Println("version:", version) } @@ -2614,7 +2768,7 @@ func TestMapCollection(t *testing.T) { c.Object(&mapValue{"a"}).Name("a").Order(1) c.Object(&mapValue{"b"}).Name("b").Order(2) c.Object(&mapValue{"c"}).Name("c").On(gs_cond.Not(gs_cond.OK())) - err := runTest(c, func(p gs_ctx.Context) { + err := runTest(c, func(p gs.Context) { var vSlice []*mapValue err := p.Get(&vSlice) @@ -2648,14 +2802,18 @@ func newCircularB() *circularB { func TestLazy(t *testing.T) { for i := 0; i < 1; i++ { + prop := conf.NewProperties() + prop.Set("spring.main.allow-circular-references", "true") + c := gs_ctx.New() - c.Property("spring.main.allow-circular-references", "true") c.Provide(newCircularA) c.Provide(newCircularB) d := struct { b *circularB `autowire:""` }{} c.Object(&d) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) assert.NotNil(t, d.b) @@ -2666,7 +2824,7 @@ func TestLazy(t *testing.T) { type memory struct { } -func (m *memory) OnInit(ctx gs_ctx.Context) error { +func (m *memory) OnInit(ctx gs.Context) error { fmt.Println("memory.OnInit") return nil } @@ -2679,7 +2837,7 @@ type table struct { _ *memory `autowire:""` } -func (t *table) OnInit(ctx gs_ctx.Context) error { +func (t *table) OnInit(ctx gs.Context) error { fmt.Println("table.OnInit") return nil } @@ -2706,9 +2864,13 @@ func (c *ContextAware) Echo(str string) string { } func TestContextAware(t *testing.T) { + prop := conf.NewProperties() + prop.Set("prefix", "hello") + c := gs_ctx.New() - c.Property("prefix", "hello") b := c.Object(new(ContextAware)) + + c.RefreshProperties(prop) err := c.Refresh(false) assert.Nil(t, err) a := b.Interface().(*ContextAware) diff --git a/gs/internal/gs_util/type.go b/gs/internal/gs_util/type.go index 23d50e6d..dd3981e9 100644 --- a/gs/internal/gs_util/type.go +++ b/gs/internal/gs_util/type.go @@ -55,35 +55,6 @@ func TypeName(i interface{}) string { return typ.String() // the path of built-in type is empty } -// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as -// `(*error)(nil)`, or a BeanDefinition value. -type BeanSelector interface{} - -// A BeanDefinition describes a bean whose lifecycle is managed by IoC container. -type BeanDefinition interface { - Type() reflect.Type - Value() reflect.Value - Interface() interface{} - ID() string - BeanName() string - TypeName() string - Created() bool - Wired() bool -} - -// Converter converts string value into user-defined value. It should be function -// type, and its prototype is func(string)(type,error). -type Converter interface{} - -// IsConverter returns whether `t` is a converter type. -func IsConverter(t reflect.Type) bool { - return IsFuncType(t) && - t.NumIn() == 1 && - t.In(0).Kind() == reflect.String && - t.NumOut() == 2 && - (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) -} - // IsFuncType returns whether `t` is func type. func IsFuncType(t reflect.Type) bool { return t.Kind() == reflect.Func @@ -134,40 +105,40 @@ func HasReceiver(t reflect.Type, receiver reflect.Value) bool { return receiver.Type().Implements(t0) } -// IsPrimitiveValueType returns whether `t` is the primitive value type which only is -// int, unit, float, bool, string and complex. -func IsPrimitiveValueType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Complex64, reflect.Complex128: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Bool: - return true - } - return false -} - -// IsValueType returns whether the input type is the primitive value type and their -// composite type including array, slice, map and struct, such as []int, [3]string, -// []string, map[int]int, map[string]string, etc. -func IsValueType(t reflect.Type) bool { - fn := func(t reflect.Type) bool { - return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct - } - switch t.Kind() { - case reflect.Map, reflect.Slice, reflect.Array: - return fn(t.Elem()) - default: - return fn(t) - } -} +//// IsPrimitiveValueType returns whether `t` is the primitive value type which only is +//// int, unit, float, bool, string and complex. +//func IsPrimitiveValueType(t reflect.Type) bool { +// switch t.Kind() { +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// return true +// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: +// return true +// case reflect.Complex64, reflect.Complex128: +// return true +// case reflect.Float32, reflect.Float64: +// return true +// case reflect.String: +// return true +// case reflect.Bool: +// return true +// } +// return false +//} +// +//// IsValueType returns whether the input type is the primitive value type and their +//// composite type including array, slice, map and struct, such as []int, [3]string, +//// []string, map[int]int, map[string]string, etc. +//func IsValueType(t reflect.Type) bool { +// fn := func(t reflect.Type) bool { +// return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct +// } +// switch t.Kind() { +// case reflect.Map, reflect.Slice, reflect.Array: +// return fn(t.Elem()) +// default: +// return fn(t) +// } +//} // IsBeanType returns whether `t` is a bean type. func IsBeanType(t reflect.Type) bool { From fd79878667b769ebc97285df5b49e48d82b5fa18 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 30 Dec 2024 22:27:04 +0800 Subject: [PATCH 14/75] add Group method --- dync/dync.go | 38 +- gs/gs.go | 20 +- .../gs_bean_test.go => gs/bean_test.go} | 56 ++- gs/internal/gs/gs.go | 20 +- gs/internal/gs_app/app.go | 5 +- gs/internal/gs_app/app_bootstrap.go | 5 +- gs/internal/gs_bean/bean.go | 329 ---------------- gs/internal/gs_cond/cond.go | 30 +- gs/internal/gs_cond/cond_mock.go | 2 +- gs/internal/gs_ctx/gs.go | 245 ++++++++++-- gs/internal/gs_ctx/gs_bean.go | 360 ------------------ gs/internal/gs_ctx/gs_context.go | 140 ------- gs/internal/gs_ctx/gs_dynamic_test.go | 158 -------- gs/internal/gs_ctx/gs_test.go | 154 ++++++++ gs/internal/gs_util/type.go | 35 -- 15 files changed, 480 insertions(+), 1117 deletions(-) rename gs/internal/{gs_ctx/gs_bean_test.go => gs/bean_test.go} (88%) delete mode 100644 gs/internal/gs_bean/bean.go delete mode 100755 gs/internal/gs_ctx/gs_bean.go delete mode 100644 gs/internal/gs_ctx/gs_context.go delete mode 100644 gs/internal/gs_ctx/gs_dynamic_test.go diff --git a/dync/dync.go b/dync/dync.go index bec0b1a1..59406ee6 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -54,25 +54,29 @@ func (p *Properties) load() *conf.Properties { return p.value.Load().(*conf.Properties) } -func (p *Properties) Keys() []string { - return p.load().Keys() +func (p *Properties) Data() conf.ReadOnlyProperties { + return p.load() } -func (p *Properties) Has(key string) bool { - return p.load().Has(key) -} - -func (p *Properties) Get(key string, opts ...conf.GetOption) string { - return p.load().Get(key, opts...) -} - -func (p *Properties) Resolve(s string) (string, error) { - return p.load().Resolve(s) -} - -func (p *Properties) Bind(i interface{}, opts ...conf.BindArg) error { - return p.load().Bind(i, opts...) -} +// func (p *Properties) Keys() []string { +// return p.load().Keys() +// } +// +// func (p *Properties) Has(key string) bool { +// return p.load().Has(key) +// } +// +// func (p *Properties) Get(key string, opts ...conf.GetOption) string { +// return p.load().Get(key, opts...) +// } +// +// func (p *Properties) Resolve(s string) (string, error) { +// return p.load().Resolve(s) +// } +// +// func (p *Properties) Bind(i interface{}, opts ...conf.BindArg) error { +// return p.load().Bind(i, opts...) +// } func (p *Properties) Update(m map[string]interface{}) error { diff --git a/gs/gs.go b/gs/gs.go index 4648bb76..02977415 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -28,6 +28,7 @@ type ( Arg = gs.Arg Context = gs.Context BeanSelector = gs.BeanSelector + GroupFunc = gs.GroupFunc ) var app = gs_app.NewApp() @@ -57,16 +58,6 @@ func Bootstrap() *gs_app.Bootstrapper { return app.Bootstrap() } -// OnProperty 参考 App.OnProperty 的解释。 -func OnProperty(key string, fn interface{}) { - app.OnProperty(key, fn) -} - -// Accept 参考 Container.Accept 的解释。 -func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { - return app.Accept(b) -} - // Object 参考 Container.Object 的解释。 func Object(i interface{}) *gs.BeanDefinition { return app.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) @@ -76,3 +67,12 @@ func Object(i interface{}) *gs.BeanDefinition { func Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return app.Accept(gs_ctx.NewBean(ctor, args...)) } + +// Accept 参考 Container.Accept 的解释。 +func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { + return app.Accept(b) +} + +func Group(fn GroupFunc) { + app.Group(fn) +} diff --git a/gs/internal/gs_ctx/gs_bean_test.go b/gs/internal/gs/bean_test.go similarity index 88% rename from gs/internal/gs_ctx/gs_bean_test.go rename to gs/internal/gs/bean_test.go index e344435e..9ac29a54 100644 --- a/gs/internal/gs_ctx/gs_bean_test.go +++ b/gs/internal/gs/bean_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_ctx_test +package gs_test import ( "fmt" @@ -134,6 +134,52 @@ func TestBeanDefinition_Match(t *testing.T) { } } +type Teacher interface { + Course() string +} + +type historyTeacher struct { + name string +} + +func newHistoryTeacher(name string) *historyTeacher { + return &historyTeacher{name: name} +} + +func newTeacher(course string, name string) Teacher { + switch course { + case "history": + return &historyTeacher{name: name} + default: + return nil + } +} + +func (t *historyTeacher) Course() string { + return "history" +} + +type Student struct { + Teacher Teacher + Room string +} + +// 入参可以进行注入或者属性绑定,返回值可以是 struct、map、slice、func 等。 +func NewStudent(teacher Teacher, room string) Student { + return Student{ + Teacher: teacher, + Room: room, + } +} + +// 入参可以进行注入或者属性绑定,返回值可以是 struct、map、slice、func 等。 +func NewPtrStudent(teacher Teacher, room string) *Student { + return &Student{ + Teacher: teacher, + Room: room, + } +} + type BeanZero struct { Int int } @@ -193,7 +239,7 @@ func TestObjectBean(t *testing.T) { newBean(newHistoryTeacher("")): { "historyTeacher", - "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.historyTeacher", + "github.com/go-spring/spring-core/gs/internal/gs/gs_test.historyTeacher", }, newBean(new(pkg2.SamePkg)): { @@ -217,10 +263,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.Type().String(), "*gs_ctx_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.Type().String(), "*gs_ctx_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -236,7 +282,7 @@ func TestConstructorBean(t *testing.T) { interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.Type().String(), "gs_ctx_test.Teacher") + assert.Equal(t, bd.Type().String(), "gs_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 380f262c..cb427d46 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -11,8 +11,8 @@ import ( // `(*error)(nil)`, or a BeanDefinition value. type BeanSelector interface{} -// ConditionContext defines some methods of IoC container that conditions use. -type ConditionContext interface { +// CondContext defines some methods of IoC container that conditions use. +type CondContext interface { // Has returns whether the IoC container has a property. Has(key string) bool // Prop returns the property's value when the IoC container has it, or @@ -24,9 +24,15 @@ type ConditionContext interface { // Condition is used when registering a bean to determine whether it's valid. type Condition interface { - Matches(ctx ConditionContext) (bool, error) + Matches(ctx CondContext) (bool, error) } +// Arg 用于为函数参数提供绑定值。可以是 bean.Selector 类型,表示注入 bean ; +// 可以是 ${X:=Y} 形式的字符串,表示属性绑定或者注入 bean ;可以是 ValueArg +// 类型,表示不从 IoC 容器获取而是用户传入的普通值;可以是 IndexArg 类型,表示 +// 带有下标的参数绑定;可以是 *optionArg 类型,用于为 Option 方法提供参数绑定。 +type Arg interface{} + // ArgContext defines some methods of IoC container that Callable use. type ArgContext interface { // Matches returns true when the Condition returns true, @@ -38,18 +44,14 @@ type ArgContext interface { Wire(v reflect.Value, tag string) error } -// Arg 用于为函数参数提供绑定值。可以是 bean.Selector 类型,表示注入 bean ; -// 可以是 ${X:=Y} 形式的字符串,表示属性绑定或者注入 bean ;可以是 ValueArg -// 类型,表示不从 IoC 容器获取而是用户传入的普通值;可以是 IndexArg 类型,表示 -// 带有下标的参数绑定;可以是 *optionArg 类型,用于为 Option 方法提供参数绑定。 -type Arg interface{} - type Callable interface { Arg(i int) (Arg, bool) In(i int) (reflect.Type, bool) Call(ctx ArgContext) ([]reflect.Value, error) } +type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) + // Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 // 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 // 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index d75a6d38..2b9b5418 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -227,9 +227,8 @@ func (app *App) Bootstrap() *Bootstrapper { return app.b } -// OnProperty 当 key 对应的属性值准备好后发送一个通知。 -func (app *App) OnProperty(key string, fn interface{}) { - app.c.OnProperty(key, fn) +func (app *App) Group(fn gs.GroupFunc) { + app.c.Group(fn) } // Accept 参考 Container.Accept 的解释。 diff --git a/gs/internal/gs_app/app_bootstrap.go b/gs/internal/gs_app/app_bootstrap.go index ccfc0559..3f50661d 100644 --- a/gs/internal/gs_app/app_bootstrap.go +++ b/gs/internal/gs_app/app_bootstrap.go @@ -33,9 +33,8 @@ func newBootstrap() *Bootstrapper { } } -// OnProperty 参考 App.OnProperty 的解释。 -func (b *Bootstrapper) OnProperty(key string, fn interface{}) { - b.c.OnProperty(key, fn) +func (b *Bootstrapper) Group(fn gs.GroupFunc) { + b.c.Group(fn) } // Object 参考 Container.Object 的解释。 diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go deleted file mode 100644 index db264ffb..00000000 --- a/gs/internal/gs_bean/bean.go +++ /dev/null @@ -1,329 +0,0 @@ -package gs_bean - -// -//type BeanStatus int8 -// -//const ( -// Deleted = BeanStatus(-1) // 已删除 -// Default = BeanStatus(iota) // 未处理 -// Resolving // 正在决议 -// Resolved // 已决议 -// Creating // 正在创建 -// Created // 已创建 -// Wired // 注入完成 -//) -// -//func GetStatusString(status BeanStatus) string { -// switch status { -// case Deleted: -// return "Deleted" -// case Default: -// return "Default" -// case Resolving: -// return "Resolving" -// case Resolved: -// return "Resolved" -// case Creating: -// return "Creating" -// case Created: -// return "Created" -// case Wired: -// return "Wired" -// default: -// return "" -// } -//} -// -//// BeanDefinition bean 元数据。 -//type BeanDefinition struct { -// V reflect.Value // 值 -// T reflect.Type // 类型 -// F *gs_arg.Callable // 构造函数 -// -// file string // 注册点所在文件 -// line int // 注册点所在行数 -// -// name string // 名称 -// typeName string // 原始类型的全限定名 -// status BeanStatus // 状态 -// primary bool // 是否为主版本 -// method bool // 是否为成员方法 -// cond gs.Condition // 判断条件 -// order float32 // 收集时的顺序 -// init interface{} // 初始化函数 -// destroy interface{} // 销毁函数 -// depends []gs_util.BeanSelector // 间接依赖项 -// exports []reflect.Type // 导出的接口 -//} -// -//func (d *BeanDefinition) GetName() string { return d.name } -//func (d *BeanDefinition) GetTypeName() string { return d.typeName } -//func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } -//func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } -//func (d *BeanDefinition) IsPrimary() bool { return d.primary } -//func (d *BeanDefinition) IsMethod() bool { return d.method } -//func (d *BeanDefinition) GetCond() gs.Condition { return d.cond } -//func (d *BeanDefinition) GetOrder() float32 { return d.order } -//func (d *BeanDefinition) GetInit() interface{} { return d.init } -//func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } -//func (d *BeanDefinition) GetDepends() []gs_util.BeanSelector { return d.depends } -//func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } -// -//// Type 返回 bean 的类型。 -//func (d *BeanDefinition) Type() reflect.Type { -// return d.T -//} -// -//// Value 返回 bean 的值。 -//func (d *BeanDefinition) Value() reflect.Value { -// return d.V -//} -// -//// Interface 返回 bean 的真实值。 -//func (d *BeanDefinition) Interface() interface{} { -// return d.V.Interface() -//} -// -//// ID 返回 bean 的 ID 。 -//func (d *BeanDefinition) ID() string { -// return d.typeName + ":" + d.name -//} -// -//// BeanName 返回 bean 的名称。 -//func (d *BeanDefinition) BeanName() string { -// return d.name -//} -// -//// TypeName 返回 bean 的原始类型的全限定名。 -//func (d *BeanDefinition) TypeName() string { -// return d.typeName -//} -// -//// Created 返回是否已创建。 -//func (d *BeanDefinition) Created() bool { -// return d.status >= Created -//} -// -//// Wired 返回 bean 是否已经注入。 -//func (d *BeanDefinition) Wired() bool { -// return d.status == Wired -//} -// -//// FileLine 返回 bean 的注册点。 -//func (d *BeanDefinition) FileLine() string { -// return fmt.Sprintf("%s:%d", d.file, d.line) -//} -// -//// GetClass 返回 bean 的类型描述。 -//func (d *BeanDefinition) GetClass() string { -// if d.F == nil { -// return "object bean" -// } -// return "constructor bean" -//} -// -//func (d *BeanDefinition) String() string { -// return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) -//} -// -//// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 -//func (d *BeanDefinition) Match(typeName string, beanName string) bool { -// -// typeIsSame := false -// if typeName == "" || d.typeName == typeName { -// typeIsSame = true -// } -// -// nameIsSame := false -// if beanName == "" || d.name == beanName { -// nameIsSame = true -// } -// -// return typeIsSame && nameIsSame -//} -// -//// Name 设置 bean 的名称。 -//func (d *BeanDefinition) Name(name string) *BeanDefinition { -// d.name = name -// return d -//} -// -//// On 设置 bean 的 Condition。 -//func (d *BeanDefinition) On(cond gs.Condition) *BeanDefinition { -// d.cond = cond -// return d -//} -// -//// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 -//func (d *BeanDefinition) Order(order float32) *BeanDefinition { -// d.order = order -// return d -//} -// -//// DependsOn 设置 bean 的间接依赖项。 -//func (d *BeanDefinition) DependsOn(selectors ...gs_util.BeanSelector) *BeanDefinition { -// d.depends = append(d.depends, selectors...) -// return d -//} -// -//// Primary 设置 bean 为主版本。 -//func (d *BeanDefinition) Primary() *BeanDefinition { -// d.primary = true -// return d -//} -// -//// validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 -//// 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 -//func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { -// if !gs_util.IsFuncType(fnType) { -// return false -// } -// if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { -// return false -// } -// return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) -//} -// -//// Init 设置 bean 的初始化函数。 -//func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { -// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { -// d.init = fn -// return d -// } -// panic(errors.New("init should be func(bean) or func(bean)error")) -//} -// -//// Destroy 设置 bean 的销毁函数。 -//func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { -// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { -// d.destroy = fn -// return d -// } -// panic(errors.New("destroy should be func(bean) or func(bean)error")) -//} -// -//// Export 设置 bean 的导出接口。 -//func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { -// err := d.export(exports...) -// if err != nil { -// panic(err) -// } -// return d -//} -// -//func (d *BeanDefinition) export(exports ...interface{}) error { -// for _, o := range exports { -// var typ reflect.Type -// if t, ok := o.(reflect.Type); ok { -// typ = t -// } else { // 处理 (*error)(nil) 这种导出形式 -// typ = gs_util.Indirect(reflect.TypeOf(o)) -// } -// if typ.Kind() != reflect.Interface { -// return errors.New("only interface type can be exported") -// } -// exported := false -// for _, export := range d.exports { -// if typ == export { -// exported = true -// break -// } -// } -// if exported { -// continue -// } -// d.exports = append(d.exports, typ) -// } -// return nil -//} -// -//// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -//func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanDefinition { -// -// var v reflect.Value -// var fromValue bool -// var method bool -// var name string -// -// switch i := objOrCtor.(type) { -// case reflect.Value: -// fromValue = true -// v = i -// default: -// v = reflect.ValueOf(i) -// } -// -// t := v.Type() -// if !gs_util.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// if !v.IsValid() || v.IsNil() { -// panic(errors.New("bean can't be nil")) -// } -// -// const skip = 2 -// var f *gs_arg.Callable -// _, file, line, _ := runtime.Caller(skip) -// -// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 -// if !fromValue && t.Kind() == reflect.Func { -// -// if !gs_util.IsConstructor(t) { -// t1 := "func(...)bean" -// t2 := "func(...)(bean, error)" -// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) -// } -// -// var err error -// f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) -// if err != nil { -// panic(err) -// } -// -// out0 := t.Out(0) -// v = reflect.New(out0) -// if gs_util.IsBeanType(out0) { -// v = v.Elem() -// } -// -// t = v.Type() -// if !gs_util.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 -// fnPtr := reflect.ValueOf(objOrCtor).Pointer() -// fnInfo := runtime.FuncForPC(fnPtr) -// funcName := fnInfo.Name() -// name = funcName[strings.LastIndex(funcName, "/")+1:] -// name = name[strings.Index(name, ".")+1:] -// if name[0] == '(' { -// name = name[strings.Index(name, ".")+1:] -// } -// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 -// } -// -// if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { -// panic(errors.New("bean should be *val but not *ref")) -// } -// -// // Type.String() 一般返回 *pkg.Type 形式的字符串, -// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 -// if name == "" { -// s := strings.Split(t.String(), ".") -// name = strings.TrimPrefix(s[len(s)-1], "*") -// } -// -// return &BeanDefinition{ -// T: t, -// V: v, -// F: f, -// name: name, -// typeName: gs_util.TypeName(t), -// status: Default, -// method: method, -// file: file, -// line: line, -// } -//} diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index 6d895faf..f07f1918 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -30,15 +30,15 @@ import ( "github.com/go-spring/spring-core/util" ) -type FuncCond func(ctx gs.ConditionContext) (bool, error) +type FuncCond func(ctx gs.CondContext) (bool, error) -func (c FuncCond) Matches(ctx gs.ConditionContext) (bool, error) { +func (c FuncCond) Matches(ctx gs.CondContext) (bool, error) { return c(ctx) } // OK returns a Condition that always returns true. func OK() gs.Condition { - return FuncCond(func(ctx gs.ConditionContext) (bool, error) { + return FuncCond(func(ctx gs.CondContext) (bool, error) { return true, nil }) } @@ -53,7 +53,7 @@ func Not(c gs.Condition) gs.Condition { return ¬{c: c} } -func (c *not) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *not) Matches(ctx gs.CondContext) (bool, error) { ok, err := c.c.Matches(ctx) return !ok, err } @@ -65,7 +65,7 @@ type onProperty struct { matchIfMissing bool } -func (c *onProperty) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onProperty) Matches(ctx gs.CondContext) (bool, error) { if !ctx.Has(c.name) { return c.matchIfMissing, nil @@ -103,7 +103,7 @@ type onMissingProperty struct { name string } -func (c *onMissingProperty) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onMissingProperty) Matches(ctx gs.CondContext) (bool, error) { return !ctx.Has(c.name), nil } @@ -112,7 +112,7 @@ type onBean struct { selector gs.BeanSelector } -func (c *onBean) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onBean) Matches(ctx gs.CondContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) > 0, err } @@ -122,7 +122,7 @@ type onMissingBean struct { selector gs.BeanSelector } -func (c *onMissingBean) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onMissingBean) Matches(ctx gs.CondContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) == 0, err } @@ -132,7 +132,7 @@ type onSingleBean struct { selector gs.BeanSelector } -func (c *onSingleBean) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onSingleBean) Matches(ctx gs.CondContext) (bool, error) { beans, err := ctx.Find(c.selector) return len(beans) == 1, err } @@ -142,7 +142,7 @@ type onExpression struct { expression string } -func (c *onExpression) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *onExpression) Matches(ctx gs.CondContext) (bool, error) { return false, util.UnimplementedMethod } @@ -166,7 +166,7 @@ func Group(op Operator, cond ...gs.Condition) gs.Condition { return &group{op: op, cond: cond} } -func (g *group) Matches(ctx gs.ConditionContext) (bool, error) { +func (g *group) Matches(ctx gs.CondContext) (bool, error) { if len(g.cond) == 0 { return false, errors.New("no condition in group") @@ -212,7 +212,7 @@ type node struct { next *node } -func (n *node) Matches(ctx gs.ConditionContext) (bool, error) { +func (n *node) Matches(ctx gs.CondContext) (bool, error) { if n.cond == nil { return true, nil @@ -259,7 +259,7 @@ func New() *conditional { return &conditional{head: n, curr: n} } -func (c *conditional) Matches(ctx gs.ConditionContext) (bool, error) { +func (c *conditional) Matches(ctx gs.CondContext) (bool, error) { return c.head.Matches(ctx) } @@ -383,12 +383,12 @@ func (c *conditional) OnExpression(expression string) *conditional { // OnMatches returns a conditional that starts with a Condition that returns true // when function returns true. -func OnMatches(fn func(ctx gs.ConditionContext) (bool, error)) *conditional { +func OnMatches(fn func(ctx gs.CondContext) (bool, error)) *conditional { return New().OnMatches(fn) } // OnMatches adds a Condition that returns true when function returns true. -func (c *conditional) OnMatches(fn func(ctx gs.ConditionContext) (bool, error)) *conditional { +func (c *conditional) OnMatches(fn func(ctx gs.CondContext) (bool, error)) *conditional { return c.On(FuncCond(fn)) } diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index 38be6baf..ca101045 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -114,7 +114,7 @@ func (m *MockCondition) EXPECT() *MockConditionMockRecorder { } // Matches mocks base method. -func (m *MockCondition) Matches(ctx gs.ConditionContext) (bool, error) { +func (m *MockCondition) Matches(ctx gs.CondContext) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Matches", ctx) ret0, _ := ret[0].(bool) diff --git a/gs/internal/gs_ctx/gs.go b/gs/internal/gs_ctx/gs.go index f938b00a..05b5a431 100755 --- a/gs/internal/gs_ctx/gs.go +++ b/gs/internal/gs_ctx/gs.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "reflect" + "runtime" "sort" "strings" "sync" @@ -32,6 +33,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -55,10 +57,10 @@ type ContextAware struct { } type tempContainer struct { - beans []*gs.BeanDefinition - beansByName map[string][]*gs.BeanDefinition - beansByType map[reflect.Type][]*gs.BeanDefinition - mapOfOnProperty map[string]interface{} + beans []*gs.BeanDefinition + beansByName map[string][]*gs.BeanDefinition + beansByType map[reflect.Type][]*gs.BeanDefinition + groupFuncs []gs.GroupFunc } // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion @@ -88,9 +90,8 @@ func New() *Container { cancel: cancel, p: dync.New(), tempContainer: &tempContainer{ - beansByName: make(map[string][]*gs.BeanDefinition), - beansByType: make(map[reflect.Type][]*gs.BeanDefinition), - mapOfOnProperty: make(map[string]interface{}), + beansByName: make(map[string][]*gs.BeanDefinition), + beansByType: make(map[reflect.Type][]*gs.BeanDefinition), }, } } @@ -104,24 +105,97 @@ func (c *Container) Properties() *dync.Properties { return c.p } -func validOnProperty(fn interface{}) error { - t := reflect.TypeOf(fn) - if t.Kind() != reflect.Func { - return errors.New("fn should be a func(value_type)") +type BeanInit interface { + OnInit(ctx gs.Context) error +} + +type BeanDestroy interface { + OnDestroy() +} + +// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { + + var v reflect.Value + var fromValue bool + var method bool + var name string + + switch i := objOrCtor.(type) { + case reflect.Value: + fromValue = true + v = i + default: + v = reflect.ValueOf(i) } - if t.NumIn() != 1 || !conf.IsValueType(t.In(0)) || t.NumOut() != 0 { - return errors.New("fn should be a func(value_type)") + + t := v.Type() + if !gs_util.IsBeanType(t) { + panic(errors.New("bean must be ref type")) } - return nil -} -// OnProperty 当 key 对应的属性值准备好后发送一个通知。 -func (c *Container) OnProperty(key string, fn interface{}) { - err := validOnProperty(fn) - if err != nil { - panic(err) + if !v.IsValid() || v.IsNil() { + panic(errors.New("bean can't be nil")) + } + + const skip = 2 + var f gs.Callable + _, file, line, _ := runtime.Caller(skip) + + // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 + if !fromValue && t.Kind() == reflect.Func { + + if !gs_util.IsConstructor(t) { + t1 := "func(...)bean" + t2 := "func(...)(bean, error)" + panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) + } + + var err error + f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) + if err != nil { + panic(err) + } + + out0 := t.Out(0) + v = reflect.New(out0) + if gs_util.IsBeanType(out0) { + v = v.Elem() + } + + t = v.Type() + if !gs_util.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 + fnPtr := reflect.ValueOf(objOrCtor).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + funcName := fnInfo.Name() + name = funcName[strings.LastIndex(funcName, "/")+1:] + name = name[strings.Index(name, ".")+1:] + if name[0] == '(' { + name = name[strings.Index(name, ".")+1:] + } + method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 + } + + if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { + panic(errors.New("bean should be *val but not *ref")) } - c.mapOfOnProperty[key] = fn + + // Type.String() 一般返回 *pkg.Type 形式的字符串, + // 我们只取最后的类型名,如有需要请自定义 bean 名称。 + if name == "" { + s := strings.Split(t.String(), ".") + name = strings.TrimPrefix(s[len(s)-1], "*") + } + + return gs.NewBean(t, v, f, name, method, file, line) +} + +func (c *Container) Group(fn gs.GroupFunc) { + c.groupFuncs = append(c.groupFuncs, fn) } func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { @@ -142,6 +216,26 @@ func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition return c.Accept(NewBean(ctor, args...)) } +func (c *Container) Keys() []string { + return c.p.Data().Keys() +} + +func (c *Container) Has(key string) bool { + return c.p.Data().Has(key) +} + +func (c *Container) Prop(key string, opts ...conf.GetOption) string { + return c.p.Data().Get(key, opts...) +} + +func (c *Container) Resolve(s string) (string, error) { + return c.p.Data().Resolve(s) +} + +func (c *Container) Bind(i interface{}, opts ...conf.BindArg) error { + return c.p.Data().Bind(i, opts...) +} + // destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 type destroyer struct { current *gs.BeanDefinition @@ -280,13 +374,13 @@ func (c *Container) Refresh(autoClear bool) (err error) { // start := time.Now() c.Object(c).Export((*gs.Context)(nil)) - for key, f := range c.mapOfOnProperty { - t := reflect.TypeOf(f) - in := reflect.New(t.In(0)).Elem() - if err = c.p.Bind(in, conf.Key(key)); err != nil { + for _, fn := range c.groupFuncs { + var beans []*gs.BeanDefinition + beans, err = fn(c.p.Data()) + if err != nil { return err } - reflect.ValueOf(f).Call([]reflect.Value{in}) + c.beans = append(c.beans, beans...) } c.state = Refreshing @@ -393,7 +487,7 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { if !ok || selector == "" { selector, _ = b.F.In(0) } - parents, err := c.findBean(selector) + parents, err := c.Find(selector) if err != nil { return err } @@ -492,9 +586,9 @@ func toWireString(tags []wireTag) string { return buf.String() } -// findBean 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, +// Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *Container) findBean(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { +func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { finder := func(fn func(*gs.BeanDefinition) bool) ([]*gs.BeanDefinition, error) { var result []*gs.BeanDefinition @@ -595,7 +689,7 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { // 对当前 bean 的间接依赖项进行注入。 for _, s := range b.GetDepends() { - beans, err := c.findBean(s) + beans, err := c.Find(s) if err != nil { return err } @@ -655,7 +749,7 @@ func (a *argContext) Matches(c gs.Condition) (bool, error) { } func (a *argContext) Bind(v reflect.Value, tag string) error { - return a.c.p.Bind(v, conf.Tag(tag)) + return a.c.p.Data().Bind(v, conf.Tag(tag)) } func (a *argContext) Wire(v reflect.Value, tag string) error { @@ -795,7 +889,7 @@ func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) e // tag 预处理,可能通过属性值进行指定。 if strings.HasPrefix(tag, "${") { - s, err := c.p.Resolve(tag) + s, err := c.p.Data().Resolve(tag) if err != nil { return err } @@ -1085,6 +1179,93 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return nil } +// Get 根据类型和选择器获取符合条件的 bean 对象。当 i 是一个基础类型的 bean 接收 +// 者时,表示符合条件的 bean 对象只能有一个,没有找到或者多于一个时会返回 error。 +// 当 i 是一个 map 类型的 bean 接收者时,表示获取任意数量的 bean 对象,map 的 +// key 是 bean 的名称,map 的 value 是 bean 的地址。当 i 是一个 array 或者 +// slice 时,也表示获取任意数量的 bean 对象,但是它会对获取到的 bean 对象进行排序, +// 如果没有传入选择器或者传入的选择器是 * ,则根据 bean 的 order 值进行排序,这种 +// 工作模式称为自动模式,否则根据传入的选择器列表进行排序,这种工作模式成为指派模式。 +// 该方法和 Find 方法的区别是该方法保证返回的所有 bean 对象都已经完成属性绑定和依 +// 赖注入,而 Find 方法只能保证返回的 bean 对象是有效的,即未被标记为删除的。 +func (c *Container) Get(i interface{}, selectors ...gs.BeanSelector) error { + + if i == nil { + return errors.New("i can't be nil") + } + + v := reflect.ValueOf(i) + if v.Kind() != reflect.Ptr { + return errors.New("i must be pointer") + } + + stack := newWiringStack() + + defer func() { + if len(stack.beans) > 0 { + // c.logger.Infof("wiring path %s", stack.path()) + } + }() + + var tags []wireTag + for _, s := range selectors { + tags = append(tags, toWireTag(s)) + } + return c.autowire(v.Elem(), tags, false, stack) +} + +// Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 +// 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 +// 种方式,该函数执行完后都会返回 bean 对象的真实值。 +func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { + + stack := newWiringStack() + + // defer func() { + // if len(stack.beans) > 0 { + // c.logger.Infof("wiring path %s", stack.path()) + // } + // }() + + b := NewBean(objOrCtor, ctorArgs...) + err := c.wireBean(b, stack) + if err != nil { + return nil, err + } + return b.Interface(), nil +} + +func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { + + if !gs_util.IsFuncType(reflect.TypeOf(fn)) { + return nil, errors.New("fn should be func type") + } + + stack := newWiringStack() + + // defer func() { + // if len(stack.beans) > 0 { + // c.logger.Infof("wiring path %s", stack.path()) + // } + // }() + + r, err := gs_arg.Bind(fn, args, 1) + if err != nil { + return nil, err + } + + ret, err := r.Call(&argContext{c: c, stack: stack}) + if err != nil { + return nil, err + } + + var a []interface{} + for _, v := range ret { + a = append(a, v.Interface()) + } + return a, nil +} + // Close 关闭容器,此方法必须在 Refresh 之后调用。该方法会触发 ctx 的 Done 信 // 号,然后等待所有 goroutine 结束,最后按照被依赖先销毁的原则执行所有的销毁函数。 func (c *Container) Close() { diff --git a/gs/internal/gs_ctx/gs_bean.go b/gs/internal/gs_ctx/gs_bean.go deleted file mode 100755 index b7462618..00000000 --- a/gs/internal/gs_ctx/gs_bean.go +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs_ctx - -import ( - "errors" - "fmt" - "reflect" - "runtime" - "strings" - - "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_util" -) - -// type beanStatus int8 -// -// const ( -// Deleted = beanStatus(-1) // 已删除 -// Default = beanStatus(iota) // 未处理 -// Resolving // 正在决议 -// Resolved // 已决议 -// Creating // 正在创建 -// Created // 已创建 -// Wired // 注入完成 -// ) -// -// func getStatusString(status beanStatus) string { -// switch status { -// case Deleted: -// return "Deleted" -// case Default: -// return "Default" -// case Resolving: -// return "Resolving" -// case Resolved: -// return "Resolved" -// case Creating: -// return "Creating" -// case Created: -// return "Created" -// case Wired: -// return "Wired" -// default: -// return "" -// } -// } -// -// func BeanID(typ interface{}, name string) string { -// return gsutil.TypeName(typ) + ":" + name -// } - -type BeanInit interface { - OnInit(ctx gs.Context) error -} - -type BeanDestroy interface { - OnDestroy() -} - -// // BeanDefinition bean 元数据。 -// type BeanDefinition struct { -// -// // 原始类型的全限定名 -// typeName string -// -// v reflect.Value // 值 -// t reflect.Type // 类型 -// f *gsarg.Callable // 构造函数 -// -// file string // 注册点所在文件 -// line int // 注册点所在行数 -// -// name string // 名称 -// status beanStatus // 状态 -// primary bool // 是否为主版本 -// method bool // 是否为成员方法 -// cond gscond.Condition // 判断条件 -// order float32 // 收集时的顺序 -// init interface{} // 初始化函数 -// destroy interface{} // 销毁函数 -// depends []gsutil.BeanSelector // 间接依赖项 -// exports []reflect.Type // 导出的接口 -// } -// -// // Type 返回 bean 的类型。 -// func (d *BeanDefinition) Type() reflect.Type { -// return d.t -// } -// -// // Value 返回 bean 的值。 -// func (d *BeanDefinition) Value() reflect.Value { -// return d.v -// } -// -// // Interface 返回 bean 的真实值。 -// func (d *BeanDefinition) Interface() interface{} { -// return d.v.Interface() -// } -// -// // ID 返回 bean 的 ID 。 -// func (d *BeanDefinition) ID() string { -// return d.typeName + ":" + d.name -// } -// -// // BeanName 返回 bean 的名称。 -// func (d *BeanDefinition) BeanName() string { -// return d.name -// } -// -// // TypeName 返回 bean 的原始类型的全限定名。 -// func (d *BeanDefinition) TypeName() string { -// return d.typeName -// } -// -// // Created 返回是否已创建。 -// func (d *BeanDefinition) Created() bool { -// return d.status >= Created -// } -// -// // Wired 返回 bean 是否已经注入。 -// func (d *BeanDefinition) Wired() bool { -// return d.status == Wired -// } -// -// // FileLine 返回 bean 的注册点。 -// func (d *BeanDefinition) FileLine() string { -// return fmt.Sprintf("%s:%d", d.file, d.line) -// } -// -// // getClass 返回 bean 的类型描述。 -// func (d *BeanDefinition) getClass() string { -// if d.f == nil { -// return "object bean" -// } -// return "constructor bean" -// } -// -// func (d *BeanDefinition) String() string { -// return fmt.Sprintf("%s name:%q %s", d.getClass(), d.name, d.FileLine()) -// } -// -// // Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 -// func (d *BeanDefinition) Match(typeName string, beanName string) bool { -// -// typeIsSame := false -// if typeName == "" || d.typeName == typeName { -// typeIsSame = true -// } -// -// nameIsSame := false -// if beanName == "" || d.name == beanName { -// nameIsSame = true -// } -// -// return typeIsSame && nameIsSame -// } -// -// // Name 设置 bean 的名称。 -// func (d *BeanDefinition) Name(name string) *BeanDefinition { -// d.name = name -// return d -// } -// -// // On 设置 bean 的 Condition。 -// func (d *BeanDefinition) On(cond gscond.Condition) *BeanDefinition { -// d.cond = cond -// return d -// } -// -// // Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 -// func (d *BeanDefinition) Order(order float32) *BeanDefinition { -// d.order = order -// return d -// } -// -// // DependsOn 设置 bean 的间接依赖项。 -// func (d *BeanDefinition) DependsOn(selectors ...gsutil.BeanSelector) *BeanDefinition { -// d.depends = append(d.depends, selectors...) -// return d -// } -// -// // Primary 设置 bean 为主版本。 -// func (d *BeanDefinition) Primary() *BeanDefinition { -// d.primary = true -// return d -// } -// -// // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 -// // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 -// func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { -// if !gsutil.IsFuncType(fnType) { -// return false -// } -// if fnType.NumIn() != 1 || !gsutil.HasReceiver(fnType, beanValue) { -// return false -// } -// return gsutil.ReturnNothing(fnType) || gsutil.ReturnOnlyError(fnType) -// } -// -// // Init 设置 bean 的初始化函数。 -// func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { -// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { -// d.init = fn -// return d -// } -// panic(errors.New("init should be func(bean) or func(bean)error")) -// } -// -// // Destroy 设置 bean 的销毁函数。 -// func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { -// if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { -// d.destroy = fn -// return d -// } -// panic(errors.New("destroy should be func(bean) or func(bean)error")) -// } -// -// // Export 设置 bean 的导出接口。 -// func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { -// err := d.export(exports...) -// if err != nil { -// panic(err) -// } -// return d -// } -// -// func (d *BeanDefinition) export(exports ...interface{}) error { -// for _, o := range exports { -// var typ reflect.Type -// if t, ok := o.(reflect.Type); ok { -// typ = t -// } else { // 处理 (*error)(nil) 这种导出形式 -// typ = gsutil.Indirect(reflect.TypeOf(o)) -// } -// if typ.Kind() != reflect.Interface { -// return errors.New("only interface type can be exported") -// } -// exported := false -// for _, export := range d.exports { -// if typ == export { -// exported = true -// break -// } -// } -// if exported { -// continue -// } -// d.exports = append(d.exports, typ) -// } -// return nil -// } - -// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { - - var v reflect.Value - var fromValue bool - var method bool - var name string - - switch i := objOrCtor.(type) { - case reflect.Value: - fromValue = true - v = i - default: - v = reflect.ValueOf(i) - } - - t := v.Type() - if !gs_util.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - if !v.IsValid() || v.IsNil() { - panic(errors.New("bean can't be nil")) - } - - const skip = 2 - var f gs.Callable - _, file, line, _ := runtime.Caller(skip) - - // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 - if !fromValue && t.Kind() == reflect.Func { - - if !gs_util.IsConstructor(t) { - t1 := "func(...)bean" - t2 := "func(...)(bean, error)" - panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) - } - - var err error - f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) - if err != nil { - panic(err) - } - - out0 := t.Out(0) - v = reflect.New(out0) - if gs_util.IsBeanType(out0) { - v = v.Elem() - } - - t = v.Type() - if !gs_util.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 - fnPtr := reflect.ValueOf(objOrCtor).Pointer() - fnInfo := runtime.FuncForPC(fnPtr) - funcName := fnInfo.Name() - name = funcName[strings.LastIndex(funcName, "/")+1:] - name = name[strings.Index(name, ".")+1:] - if name[0] == '(' { - name = name[strings.Index(name, ".")+1:] - } - method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 - } - - if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { - panic(errors.New("bean should be *val but not *ref")) - } - - // Type.String() 一般返回 *pkg.Type 形式的字符串, - // 我们只取最后的类型名,如有需要请自定义 bean 名称。 - if name == "" { - s := strings.Split(t.String(), ".") - name = strings.TrimPrefix(s[len(s)-1], "*") - } - - return gs.NewBean(t, v, f, name, method, file, line) - //return &gs.BeanDefinition{ - // t: t, - // v: v, - // f: f, - // name: name, - // typeName: gs_util.TypeName(t), - // status: gs.Default, - // method: method, - // file: file, - // line: line, - //} -} diff --git a/gs/internal/gs_ctx/gs_context.go b/gs/internal/gs_ctx/gs_context.go deleted file mode 100644 index 7d90180e..00000000 --- a/gs/internal/gs_ctx/gs_context.go +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs_ctx - -import ( - "errors" - "reflect" - - "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_util" -) - -func (c *Container) Keys() []string { - return c.p.Keys() -} - -func (c *Container) Has(key string) bool { - return c.p.Has(key) -} - -func (c *Container) Prop(key string, opts ...conf.GetOption) string { - return c.p.Get(key, opts...) -} - -func (c *Container) Resolve(s string) (string, error) { - return c.p.Resolve(s) -} - -func (c *Container) Bind(i interface{}, opts ...conf.BindArg) error { - return c.p.Bind(i, opts...) -} - -// Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的,即未被 -// 标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { - return c.findBean(selector) -} - -// Get 根据类型和选择器获取符合条件的 bean 对象。当 i 是一个基础类型的 bean 接收 -// 者时,表示符合条件的 bean 对象只能有一个,没有找到或者多于一个时会返回 error。 -// 当 i 是一个 map 类型的 bean 接收者时,表示获取任意数量的 bean 对象,map 的 -// key 是 bean 的名称,map 的 value 是 bean 的地址。当 i 是一个 array 或者 -// slice 时,也表示获取任意数量的 bean 对象,但是它会对获取到的 bean 对象进行排序, -// 如果没有传入选择器或者传入的选择器是 * ,则根据 bean 的 order 值进行排序,这种 -// 工作模式称为自动模式,否则根据传入的选择器列表进行排序,这种工作模式成为指派模式。 -// 该方法和 Find 方法的区别是该方法保证返回的所有 bean 对象都已经完成属性绑定和依 -// 赖注入,而 Find 方法只能保证返回的 bean 对象是有效的,即未被标记为删除的。 -func (c *Container) Get(i interface{}, selectors ...gs.BeanSelector) error { - - if i == nil { - return errors.New("i can't be nil") - } - - v := reflect.ValueOf(i) - if v.Kind() != reflect.Ptr { - return errors.New("i must be pointer") - } - - stack := newWiringStack() - - defer func() { - if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) - } - }() - - var tags []wireTag - for _, s := range selectors { - tags = append(tags, toWireTag(s)) - } - return c.autowire(v.Elem(), tags, false, stack) -} - -// Wire 如果传入的是 bean 对象,则对 bean 对象进行属性绑定和依赖注入,如果传入的 -// 是构造函数,则立即执行该构造函数,然后对返回的结果进行属性绑定和依赖注入。无论哪 -// 种方式,该函数执行完后都会返回 bean 对象的真实值。 -func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { - - stack := newWiringStack() - - // defer func() { - // if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) - // } - // }() - - b := NewBean(objOrCtor, ctorArgs...) - err := c.wireBean(b, stack) - if err != nil { - return nil, err - } - return b.Interface(), nil -} - -func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { - - if !gs_util.IsFuncType(reflect.TypeOf(fn)) { - return nil, errors.New("fn should be func type") - } - - stack := newWiringStack() - - // defer func() { - // if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) - // } - // }() - - r, err := gs_arg.Bind(fn, args, 1) - if err != nil { - return nil, err - } - - ret, err := r.Call(&argContext{c: c, stack: stack}) - if err != nil { - return nil, err - } - - var a []interface{} - for _, v := range ret { - a = append(a, v.Interface()) - } - return a, nil -} diff --git a/gs/internal/gs_ctx/gs_dynamic_test.go b/gs/internal/gs_ctx/gs_dynamic_test.go deleted file mode 100644 index c77335f7..00000000 --- a/gs/internal/gs_ctx/gs_dynamic_test.go +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs_ctx_test - -import ( - "encoding/json" - "errors" - "fmt" - "testing" - - "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" - "github.com/stretchr/testify/assert" -) - -type DynamicConfig struct { - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` - Map dync.Ref `value:"${map:=}"` - Slice dync.Ref `value:"${slice:=}"` - Event dync.Event `value:"${event}"` -} - -type DynamicConfigWrapper struct { - Wrapper DynamicConfig `value:"${wrapper}"` -} - -func TestDynamic(t *testing.T) { - - var cfg *DynamicConfig - wrapper := new(DynamicConfigWrapper) - - c := gs_ctx.New() - c.Provide(func() *DynamicConfig { - config := new(DynamicConfig) - config.Int.OnValidate(func(v int64) error { - if v < 3 { - return errors.New("should greeter than 3") - } - return nil - }) - config.Slice.Init(make([]string, 0)) - config.Map.Init(make(map[string]string)) - config.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) - return config - }).Init(func(config *DynamicConfig) { - cfg = config - }) - c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { - p.Wrapper.Slice.Init(make([]string, 0)) - p.Wrapper.Map.Init(make(map[string]string)) - p.Wrapper.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) - }) - err := c.Refresh(false) - assert.Nil(t, err) - - { - b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) - b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}}`) - } - - { - p := conf.NewProperties() - p.Set("int", 4) - p.Set("float", 2.3) - p.Set("map.a", 1) - p.Set("map.b", 2) - p.Set("slice[0]", 3) - p.Set("slice[1]", 4) - p.Set("wrapper.int", 3) - p.Set("wrapper.float", 1.5) - p.Set("wrapper.map.a", 9) - p.Set("wrapper.map.b", 8) - p.Set("wrapper.slice[0]", 4) - p.Set("wrapper.slice[1]", 6) - c.Properties().Refresh(p) - } - - { - b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) - b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) - } - - { - p := conf.NewProperties() - p.Set("int", 6) - p.Set("float", 5.1) - p.Set("map.a", 9) - p.Set("map.b", 8) - p.Set("slice[0]", 7) - p.Set("slice[1]", 6) - p.Set("wrapper.int", 9) - p.Set("wrapper.float", 8.4) - p.Set("wrapper.map.a", 3) - p.Set("wrapper.map.b", 4) - p.Set("wrapper.slice[0]", 2) - p.Set("wrapper.slice[1]", 1) - err = c.Properties().Refresh(p) - assert.Error(t, err, "validate failed on \"value:\\\"${int:=3}\\\" expr:\\\"$<6\\\"\" for value 9") - } - - { - b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) - b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) - } - - { - p := conf.NewProperties() - p.Set("int", 1) - p.Set("float", 5.1) - p.Set("map.a", 9) - p.Set("map.b", 8) - p.Set("slice[0]", 7) - p.Set("slice[1]", 6) - p.Set("wrapper.int", 9) - p.Set("wrapper.float", 8.4) - p.Set("wrapper.map.a", 3) - p.Set("wrapper.map.b", 4) - p.Set("wrapper.slice[0]", 2) - p.Set("wrapper.slice[1]", 1) - err = c.Properties().Refresh(p) - assert.Error(t, err, "should greeter than 3") - } - - { - b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) - b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) - } -} diff --git a/gs/internal/gs_ctx/gs_test.go b/gs/internal/gs_ctx/gs_test.go index 29a1dd60..226100ea 100755 --- a/gs/internal/gs_ctx/gs_test.go +++ b/gs/internal/gs_ctx/gs_test.go @@ -17,6 +17,7 @@ package gs_ctx_test import ( + "encoding/json" "errors" "fmt" "image" @@ -28,6 +29,7 @@ import ( "time" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" @@ -449,6 +451,29 @@ func TestApplicationContext_LoadProperties(t *testing.T) { assert.Nil(t, err) } +type BeanZero struct { + Int int +} + +type BeanOne struct { + Zero *BeanZero `autowire:""` +} + +type BeanTwo struct { + One *BeanOne `autowire:""` +} + +func (t *BeanTwo) Group() { +} + +type BeanThree struct { + One *BeanTwo `autowire:""` +} + +func (t *BeanThree) String() string { + return "" +} + func TestApplicationContext_Get(t *testing.T) { t.Run("panic", func(t *testing.T) { @@ -2876,3 +2901,132 @@ func TestContextAware(t *testing.T) { a := b.Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } + +type DynamicConfig struct { + Int dync.Int64 `value:"${int:=3}" expr:"$<6"` + Float dync.Float64 `value:"${float:=1.2}"` + Map dync.Ref `value:"${map:=}"` + Slice dync.Ref `value:"${slice:=}"` + Event dync.Event `value:"${event}"` +} + +type DynamicConfigWrapper struct { + Wrapper DynamicConfig `value:"${wrapper}"` +} + +func TestDynamic(t *testing.T) { + + var cfg *DynamicConfig + wrapper := new(DynamicConfigWrapper) + + c := gs_ctx.New() + c.Provide(func() *DynamicConfig { + config := new(DynamicConfig) + config.Int.OnValidate(func(v int64) error { + if v < 3 { + return errors.New("should greeter than 3") + } + return nil + }) + config.Slice.Init(make([]string, 0)) + config.Map.Init(make(map[string]string)) + config.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + fmt.Println("event fired.") + return nil + }) + return config + }).Init(func(config *DynamicConfig) { + cfg = config + }) + c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { + p.Wrapper.Slice.Init(make([]string, 0)) + p.Wrapper.Map.Init(make(map[string]string)) + p.Wrapper.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + fmt.Println("event fired.") + return nil + }) + }) + err := c.Refresh(false) + assert.Nil(t, err) + + { + b, _ := json.Marshal(cfg) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + b, _ = json.Marshal(wrapper) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}}`) + } + + { + p := conf.NewProperties() + p.Set("int", 4) + p.Set("float", 2.3) + p.Set("map.a", 1) + p.Set("map.b", 2) + p.Set("slice[0]", 3) + p.Set("slice[1]", 4) + p.Set("wrapper.int", 3) + p.Set("wrapper.float", 1.5) + p.Set("wrapper.map.a", 9) + p.Set("wrapper.map.b", 8) + p.Set("wrapper.slice[0]", 4) + p.Set("wrapper.slice[1]", 6) + c.Properties().Refresh(p) + } + + { + b, _ := json.Marshal(cfg) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + b, _ = json.Marshal(wrapper) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + } + + { + p := conf.NewProperties() + p.Set("int", 6) + p.Set("float", 5.1) + p.Set("map.a", 9) + p.Set("map.b", 8) + p.Set("slice[0]", 7) + p.Set("slice[1]", 6) + p.Set("wrapper.int", 9) + p.Set("wrapper.float", 8.4) + p.Set("wrapper.map.a", 3) + p.Set("wrapper.map.b", 4) + p.Set("wrapper.slice[0]", 2) + p.Set("wrapper.slice[1]", 1) + err = c.Properties().Refresh(p) + assert.Error(t, err, "validate failed on \"value:\\\"${int:=3}\\\" expr:\\\"$<6\\\"\" for value 9") + } + + { + b, _ := json.Marshal(cfg) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + b, _ = json.Marshal(wrapper) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + } + + { + p := conf.NewProperties() + p.Set("int", 1) + p.Set("float", 5.1) + p.Set("map.a", 9) + p.Set("map.b", 8) + p.Set("slice[0]", 7) + p.Set("slice[1]", 6) + p.Set("wrapper.int", 9) + p.Set("wrapper.float", 8.4) + p.Set("wrapper.map.a", 3) + p.Set("wrapper.map.b", 4) + p.Set("wrapper.slice[0]", 2) + p.Set("wrapper.slice[1]", 1) + err = c.Properties().Refresh(p) + assert.Error(t, err, "should greeter than 3") + } + + { + b, _ := json.Marshal(cfg) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + b, _ = json.Marshal(wrapper) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + } +} diff --git a/gs/internal/gs_util/type.go b/gs/internal/gs_util/type.go index dd3981e9..3a9e2f34 100644 --- a/gs/internal/gs_util/type.go +++ b/gs/internal/gs_util/type.go @@ -105,41 +105,6 @@ func HasReceiver(t reflect.Type, receiver reflect.Value) bool { return receiver.Type().Implements(t0) } -//// IsPrimitiveValueType returns whether `t` is the primitive value type which only is -//// int, unit, float, bool, string and complex. -//func IsPrimitiveValueType(t reflect.Type) bool { -// switch t.Kind() { -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// return true -// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: -// return true -// case reflect.Complex64, reflect.Complex128: -// return true -// case reflect.Float32, reflect.Float64: -// return true -// case reflect.String: -// return true -// case reflect.Bool: -// return true -// } -// return false -//} -// -//// IsValueType returns whether the input type is the primitive value type and their -//// composite type including array, slice, map and struct, such as []int, [3]string, -//// []string, map[int]int, map[string]string, etc. -//func IsValueType(t reflect.Type) bool { -// fn := func(t reflect.Type) bool { -// return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct -// } -// switch t.Kind() { -// case reflect.Map, reflect.Slice, reflect.Array: -// return fn(t.Elem()) -// default: -// return fn(t) -// } -//} - // IsBeanType returns whether `t` is a bean type. func IsBeanType(t reflect.Type) bool { switch t.Kind() { From e91bf60568693dfc6734529f3b4ef86cf7aa0bf9 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 31 Dec 2024 08:36:08 +0800 Subject: [PATCH 15/75] code refactor --- conf/bind.go | 28 ++++----- conf/bind_test.go | 8 +-- conf/conf.go | 16 ++++-- conf/conf_test.go | 32 +++++------ dync/bool.go | 6 +- dync/duration.go | 6 +- dync/dync.go | 65 +++------------------ dync/dync_test.go | 10 ++-- dync/event.go | 12 ++-- dync/float32.go | 6 +- dync/float64.go | 6 +- dync/int32.go | 6 +- dync/int64.go | 6 +- dync/ref.go | 10 ++-- dync/string.go | 6 +- dync/time.go | 6 +- dync/uint32.go | 6 +- dync/uint64.go | 6 +- gs/internal/gs_ctx/gs.go | 4 -- gs/internal/gs_ctx/gs_test.go | 104 +++++++++++++++++----------------- gs/sysconf/sysconf.go | 6 +- 21 files changed, 157 insertions(+), 198 deletions(-) diff --git a/conf/bind.go b/conf/bind.go index 97c5bfc1..26b97bd2 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -130,7 +130,7 @@ func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error { type Filter func(i interface{}, param BindParam) (bool, error) // BindValue binds properties to a value. -func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { +func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { if !IsValueType(t) { err := errors.New("target should be value type") @@ -224,7 +224,7 @@ func BindValue(p *Properties, v reflect.Value, t reflect.Type, param BindParam, } // bindSlice binds properties to a slice value. -func bindSlice(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { et := t.Elem() p, err := getSlice(p, et, param) @@ -257,7 +257,7 @@ func bindSlice(p *Properties, v reflect.Value, t reflect.Type, param BindParam, return nil } -func getSlice(p *Properties, et reflect.Type, param BindParam) (*Properties, error) { +func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyProperties, error) { // properties that defined as list. if p.Has(param.Key + "[0]") { @@ -304,16 +304,16 @@ func getSlice(p *Properties, et reflect.Type, param BindParam) (*Properties, err return nil, fmt.Errorf("%s: unknown splitter %q", util.FileLine(), s) } - p = NewProperties() + r := New() for i, s := range arrVal { k := fmt.Sprintf("%s[%d]", param.Key, i) - _ = p.store(k, s) + _ = r.store(k, s) } - return p, nil + return r, nil } // bindMap binds properties to a map value. -func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("map can't have a non-empty default value") @@ -324,7 +324,7 @@ func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fi ret := reflect.MakeMap(t) defer func() { v.Set(ret) }() - keys, err := p.storage.SubKeys(param.Key) + keys, err := p.SubKeys(param.Key) if err != nil { return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } @@ -349,7 +349,7 @@ func bindMap(p *Properties, v reflect.Value, t reflect.Type, param BindParam, fi } // bindStruct binds properties to a struct value. -func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("struct can't have a non-empty default value") @@ -416,11 +416,13 @@ func bindStruct(p *Properties, v reflect.Value, t reflect.Type, param BindParam, } // resolve returns property references processed property value. -func resolve(p *Properties, param BindParam) (string, error) { - if val, ok := p.storage.Get(param.Key); ok { +func resolve(p ReadOnlyProperties, param BindParam) (string, error) { + const defVal = "@@def@@" + val := p.Get(param.Key, Def(defVal)) + if val != defVal { return resolveString(p, val) } - if p.storage.Has(param.Key) { + if p.Has(param.Key) { err := fmt.Errorf("property %q isn't simple value", param.Key) return "", fmt.Errorf("%s: resolve property %q error, %w", util.FileLine(), param.Key, err) } @@ -432,7 +434,7 @@ func resolve(p *Properties, param BindParam) (string, error) { } // resolveString returns property references processed string. -func resolveString(p *Properties, s string) (string, error) { +func resolveString(p ReadOnlyProperties, s string) (string, error) { var ( length = len(s) diff --git a/conf/bind_test.go b/conf/bind_test.go index f0c045e1..7d820635 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -80,7 +80,7 @@ type NestedDbMapConfig struct { func TestProperties_Bind(t *testing.T) { t.Run("default", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() v := &struct { S struct { V int `value:"${:=3}"` @@ -139,7 +139,7 @@ func TestProperties_Bind(t *testing.T) { t.Run("simple map bind", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("a.b1", "b1") assert.Nil(t, err) err = p.Set("a.b2", "b2") @@ -189,13 +189,13 @@ func TestProperties_Bind(t *testing.T) { }) t.Run("ignore interface", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Bind(&struct{ fmt.Stringer }{}) assert.Nil(t, err) }) t.Run("", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Bytes([]byte(`m:`), ".yaml") if err != nil { t.Fatal(err) diff --git a/conf/conf.go b/conf/conf.go index 76ccdf99..a5d38241 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -109,8 +109,8 @@ type Properties struct { storage *store.Storage } -// NewProperties creates empty *Properties. -func NewProperties() *Properties { +// New creates empty *Properties. +func New() *Properties { return &Properties{ storage: store.NewStorage(), } @@ -118,7 +118,7 @@ func NewProperties() *Properties { // Map creates *Properties from map. func Map(m map[string]interface{}) (*Properties, error) { - p := NewProperties() + p := New() var keys []string for k := range m { keys = append(keys, k) @@ -134,7 +134,7 @@ func Map(m map[string]interface{}) (*Properties, error) { // Load creates *Properties from file. func Load(file string) (*Properties, error) { - p := NewProperties() + p := New() if err := p.Load(file); err != nil { return nil, err } @@ -196,6 +196,11 @@ func (p *Properties) Has(key string) bool { return p.storage.Has(key) } +// SubKeys returns the sorted sub keys of the key. +func (p *Properties) SubKeys(key string) ([]string, error) { + return p.storage.SubKeys(key) +} + type getArg struct { def string } @@ -340,6 +345,9 @@ type ReadOnlyProperties interface { // Has returns whether the key exists. Has(key string) bool + // SubKeys returns the sorted sub keys of the key. + SubKeys(key string) ([]string, error) + // Get returns key's value, using Def to return a default value. Get(key string, opts ...GetOption) string diff --git a/conf/conf_test.go b/conf/conf_test.go index bc2817a9..f37a8796 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -31,7 +31,7 @@ import ( func TestProperties_Load(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Load("testdata/config/application.yaml") assert.Nil(t, err) err = p.Load("testdata/config/application.properties") @@ -49,7 +49,7 @@ func TestProperties_Get(t *testing.T) { t.Run("base", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("a.b.c", "3") assert.Nil(t, err) @@ -198,13 +198,13 @@ func TestProperties_Ref(t *testing.T) { var httpLog struct{ FileLog } t.Run("not config", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Bind(&httpLog) assert.Error(t, err, "property \\\"app.dir\\\" not exist") }) t.Run("config", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() appDir := "/home/log" err := p.Set("app.dir", appDir) @@ -226,7 +226,7 @@ func TestProperties_Ref(t *testing.T) { }) t.Run("empty key", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() var s struct { KeyIsEmpty string `value:"${:=kie}"` } @@ -237,7 +237,7 @@ func TestProperties_Ref(t *testing.T) { } func TestBindSlice(t *testing.T) { - p := conf.NewProperties() + p := conf.New() p.Set("a", []string{"1", "2"}) var ss []string err := p.Bind(&ss, conf.Key("a")) @@ -251,19 +251,19 @@ func TestBindMap(t *testing.T) { t.Run("", func(t *testing.T) { var r [3]map[string]string - err := conf.NewProperties().Bind(&r) + err := conf.New().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind \\[3]map\\[string]string error, target should be value type") }) t.Run("", func(t *testing.T) { var r []map[string]string - err := conf.NewProperties().Bind(&r) + err := conf.New().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind \\[]map\\[string]string error, target should be value type") }) t.Run("", func(t *testing.T) { var r map[string]map[string]string - err := conf.NewProperties().Bind(&r) + err := conf.New().Bind(&r) assert.Error(t, err, ".*bind.go:.* bind map\\[string]map\\[string]string error, target should be value type") }) @@ -358,7 +358,7 @@ func TestBindMap(t *testing.T) { } func TestResolve(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("name", "Jim") assert.Nil(t, err) _, err = p.Resolve("my name is ${name") @@ -395,7 +395,7 @@ func TestProperties_Set(t *testing.T) { t.Run("map nil", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("m", nil) assert.Nil(t, err) assert.True(t, p.Has("m")) @@ -413,7 +413,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("map empty", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("m", map[string]string{}) assert.Nil(t, err) assert.True(t, p.Has("m")) @@ -424,7 +424,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list nil", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("a", nil) assert.Nil(t, err) assert.True(t, p.Has("a")) @@ -435,7 +435,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list empty", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("a", []string{}) assert.Nil(t, err) assert.True(t, p.Has("a")) @@ -446,7 +446,7 @@ func TestProperties_Set(t *testing.T) { }) t.Run("list", func(t *testing.T) { - p := conf.NewProperties() + p := conf.New() err := p.Set("a", []string{"a", "aa", "aaa"}) assert.Nil(t, err) err = p.Set("b", []int{1, 11, 111}) @@ -491,7 +491,7 @@ func TestSplitter(t *testing.T) { conf.RegisterConverter(PointConverter) conf.RegisterSplitter("PointSplitter", PointSplitter) var points []image.Point - err := conf.NewProperties().Bind(&points, conf.Tag("${:=(1,2)(3,4)}>>PointSplitter")) + err := conf.New().Bind(&points, conf.Tag("${:=(1,2)(3,4)}>>PointSplitter")) assert.Nil(t, err) assert.Equal(t, points, []image.Point{{X: 1, Y: 2}, {X: 3, Y: 4}}) } diff --git a/dync/bool.go b/dync/bool.go index 8f2ef3e6..ee604a51 100644 --- a/dync/bool.go +++ b/dync/bool.go @@ -32,7 +32,7 @@ func (x *Bool) Value() bool { return x.v.Load() } -func (x *Bool) getBool(prop *conf.Properties, param conf.BindParam) (bool, error) { +func (x *Bool) getBool(prop conf.ReadOnlyProperties, param conf.BindParam) (bool, error) { s, err := GetProperty(prop, param) if err != nil { return false, err @@ -44,7 +44,7 @@ func (x *Bool) getBool(prop *conf.Properties, param conf.BindParam) (bool, error return v, nil } -func (x *Bool) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Bool) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getBool(prop, param) if err != nil { return err @@ -53,7 +53,7 @@ func (x *Bool) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Bool) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Bool) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { _, err := x.getBool(prop, param) return err } diff --git a/dync/duration.go b/dync/duration.go index dd83b0e1..ee686339 100644 --- a/dync/duration.go +++ b/dync/duration.go @@ -40,7 +40,7 @@ func (x *Duration) OnValidate(f DurationValidateFunc) { x.f = f } -func (x *Duration) getDuration(prop *conf.Properties, param conf.BindParam) (time.Duration, error) { +func (x *Duration) getDuration(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Duration, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -52,7 +52,7 @@ func (x *Duration) getDuration(prop *conf.Properties, param conf.BindParam) (tim return v, nil } -func (x *Duration) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Duration) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getDuration(prop, param) if err != nil { return err @@ -61,7 +61,7 @@ func (x *Duration) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Duration) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Duration) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getDuration(prop, param) if err != nil { return err diff --git a/dync/dync.go b/dync/dync.go index 59406ee6..29d55bdf 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -29,8 +29,8 @@ import ( // Value 可动态刷新的对象 type Value interface { - Refresh(prop *conf.Properties, param conf.BindParam) error - Validate(prop *conf.Properties, param conf.BindParam) error + Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error + Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error } type Field struct { @@ -46,7 +46,7 @@ type Properties struct { func New() *Properties { p := &Properties{} - p.value.Store(conf.NewProperties()) + p.value.Store(conf.New()) return p } @@ -58,54 +58,7 @@ func (p *Properties) Data() conf.ReadOnlyProperties { return p.load() } -// func (p *Properties) Keys() []string { -// return p.load().Keys() -// } -// -// func (p *Properties) Has(key string) bool { -// return p.load().Has(key) -// } -// -// func (p *Properties) Get(key string, opts ...conf.GetOption) string { -// return p.load().Get(key, opts...) -// } -// -// func (p *Properties) Resolve(s string) (string, error) { -// return p.load().Resolve(s) -// } -// -// func (p *Properties) Bind(i interface{}, opts ...conf.BindArg) error { -// return p.load().Bind(i, opts...) -// } - -func (p *Properties) Update(m map[string]interface{}) error { - - flat := make(map[string]string) - for key, val := range m { - conf.FlattenValue(key, val, flat) - } - - keys := make([]string, 0, len(flat)) - for k := range flat { - keys = append(keys, k) - } - sort.Strings(keys) - - prop := conf.NewProperties() - err := p.load().CopyTo(prop) - if err != nil { - return err - } - for _, k := range keys { - err = prop.Set(k, flat[k]) - if err != nil { - return err - } - } - return p.refreshKeys(prop, keys) -} - -func (p *Properties) Refresh(prop *conf.Properties) (err error) { +func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { old := p.load() oldKeys := old.Keys() @@ -133,7 +86,7 @@ func (p *Properties) Refresh(prop *conf.Properties) (err error) { return p.refreshKeys(prop, keys) } -func (p *Properties) refreshKeys(prop *conf.Properties, keys []string) (err error) { +func (p *Properties) refreshKeys(prop conf.ReadOnlyProperties, keys []string) (err error) { updateIndexes := make(map[int]*Field) for _, key := range keys { @@ -165,7 +118,7 @@ func (p *Properties) refreshKeys(prop *conf.Properties, keys []string) (err erro return p.refreshFields(prop, updateFields) } -func (p *Properties) refreshFields(prop *conf.Properties, fields []*Field) (err error) { +func (p *Properties) refreshFields(prop conf.ReadOnlyProperties, fields []*Field) (err error) { err = validateFields(prop, fields) if err != nil { @@ -187,7 +140,7 @@ func (p *Properties) refreshFields(prop *conf.Properties, fields []*Field) (err return refreshFields(p.load(), fields) } -func validateFields(prop *conf.Properties, fields []*Field) error { +func validateFields(prop conf.ReadOnlyProperties, fields []*Field) error { for _, f := range fields { err := f.value.Validate(prop, f.param) if err != nil { @@ -197,7 +150,7 @@ func validateFields(prop *conf.Properties, fields []*Field) error { return nil } -func refreshFields(prop *conf.Properties, fields []*Field) error { +func refreshFields(prop conf.ReadOnlyProperties, fields []*Field) error { for _, f := range fields { err := f.value.Refresh(prop, f.param) if err != nil { @@ -244,7 +197,7 @@ func (p *Properties) bindValue(i interface{}, param conf.BindParam) (bool, error return true, nil } -func GetProperty(prop *conf.Properties, param conf.BindParam) (string, error) { +func GetProperty(prop conf.ReadOnlyProperties, param conf.BindParam) (string, error) { key := param.Key if !prop.Has(key) && !param.Tag.HasDef { return "", fmt.Errorf("property %q not exist", key) diff --git a/dync/dync_test.go b/dync/dync_test.go index 82dffc6a..9708582d 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -33,7 +33,7 @@ type Integer struct { v int } -func (x *Integer) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Integer) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { s, err := dync.GetProperty(prop, param) if err != nil { return err @@ -46,7 +46,7 @@ func (x *Integer) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Integer) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Integer) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { return nil } @@ -91,7 +91,7 @@ func TestDynamic(t *testing.T) { } cfg.Slice.Init(make([]string, 0)) cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil }) @@ -124,7 +124,7 @@ func TestDynamic(t *testing.T) { }) cfg.Slice.Init(make([]string, 0)) cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil }) @@ -162,7 +162,7 @@ func TestDynamic(t *testing.T) { }) cfg.Slice.Init(make([]string, 0)) cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil }) diff --git a/dync/event.go b/dync/event.go index 5b0d8033..09bb758c 100644 --- a/dync/event.go +++ b/dync/event.go @@ -22,13 +22,13 @@ import ( "github.com/go-spring/spring-core/conf" ) -type EventFunc func(prop *conf.Properties, param conf.BindParam) error -type EventValidateFunc func(prop *conf.Properties, param conf.BindParam) error +type EventFunc func(prop conf.ReadOnlyProperties, param conf.BindParam) error +type EventValidateFunc func(prop conf.ReadOnlyProperties, param conf.BindParam) error type Event struct { f EventFunc h EventValidateFunc - init func() (*conf.Properties, conf.BindParam) + init func() (conf.ReadOnlyProperties, conf.BindParam) } func (e *Event) OnValidate(h EventValidateFunc) { @@ -44,9 +44,9 @@ func (e *Event) OnEvent(f EventFunc) error { return e.Refresh(prop, param) } -func (e *Event) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (e *Event) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { if e.f == nil { - e.init = func() (*conf.Properties, conf.BindParam) { + e.init = func() (conf.ReadOnlyProperties, conf.BindParam) { return prop, param } return nil @@ -54,7 +54,7 @@ func (e *Event) Refresh(prop *conf.Properties, param conf.BindParam) error { return e.f(prop, param) } -func (e *Event) Validate(prop *conf.Properties, param conf.BindParam) error { +func (e *Event) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { if e.h != nil { return e.h(prop, param) } diff --git a/dync/float32.go b/dync/float32.go index e02df928..3a8bc7c5 100644 --- a/dync/float32.go +++ b/dync/float32.go @@ -39,7 +39,7 @@ func (x *Float32) OnValidate(f Float32ValidateFunc) { x.f = f } -func (x *Float32) getFloat32(prop *conf.Properties, param conf.BindParam) (float32, error) { +func (x *Float32) getFloat32(prop conf.ReadOnlyProperties, param conf.BindParam) (float32, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Float32) getFloat32(prop *conf.Properties, param conf.BindParam) (float return float32(v), nil } -func (x *Float32) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Float32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat32(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Float32) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Float32) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Float32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat32(prop, param) if err != nil { return err diff --git a/dync/float64.go b/dync/float64.go index 4f06c1df..cb882c45 100644 --- a/dync/float64.go +++ b/dync/float64.go @@ -39,7 +39,7 @@ func (x *Float64) OnValidate(f Float64ValidateFunc) { x.f = f } -func (x *Float64) getFloat64(prop *conf.Properties, param conf.BindParam) (float64, error) { +func (x *Float64) getFloat64(prop conf.ReadOnlyProperties, param conf.BindParam) (float64, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Float64) getFloat64(prop *conf.Properties, param conf.BindParam) (float return v, nil } -func (x *Float64) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Float64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat64(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Float64) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Float64) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Float64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat64(prop, param) if err != nil { return err diff --git a/dync/int32.go b/dync/int32.go index 294a9873..62b5801e 100644 --- a/dync/int32.go +++ b/dync/int32.go @@ -39,7 +39,7 @@ func (x *Int32) OnValidate(f Int32ValidateFunc) { x.f = f } -func (x *Int32) getInt32(prop *conf.Properties, param conf.BindParam) (int32, error) { +func (x *Int32) getInt32(prop conf.ReadOnlyProperties, param conf.BindParam) (int32, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Int32) getInt32(prop *conf.Properties, param conf.BindParam) (int32, er return int32(v), nil } -func (x *Int32) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Int32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt32(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Int32) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Int32) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Int32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt32(prop, param) if err != nil { return err diff --git a/dync/int64.go b/dync/int64.go index 422c6427..43286be4 100644 --- a/dync/int64.go +++ b/dync/int64.go @@ -39,7 +39,7 @@ func (x *Int64) OnValidate(f Int64ValidateFunc) { x.f = f } -func (x *Int64) getInt64(prop *conf.Properties, param conf.BindParam) (int64, error) { +func (x *Int64) getInt64(prop conf.ReadOnlyProperties, param conf.BindParam) (int64, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Int64) getInt64(prop *conf.Properties, param conf.BindParam) (int64, er return v, nil } -func (x *Int64) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Int64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt64(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Int64) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Int64) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Int64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt64(prop, param) if err != nil { return err diff --git a/dync/ref.go b/dync/ref.go index 6b28da6c..c222f239 100644 --- a/dync/ref.go +++ b/dync/ref.go @@ -29,7 +29,7 @@ type RefValidateFunc func(v interface{}) error type Ref struct { v atomic.Value f RefValidateFunc - init func() (*conf.Properties, conf.BindParam) + init func() (conf.ReadOnlyProperties, conf.BindParam) } func (r *Ref) Init(i interface{}) error { @@ -50,10 +50,10 @@ func (r *Ref) OnValidate(f RefValidateFunc) { r.f = f } -func (r *Ref) getRef(prop *conf.Properties, param conf.BindParam) (interface{}, error) { +func (r *Ref) getRef(prop conf.ReadOnlyProperties, param conf.BindParam) (interface{}, error) { o := r.Value() if o == nil { - r.init = func() (*conf.Properties, conf.BindParam) { + r.init = func() (conf.ReadOnlyProperties, conf.BindParam) { return prop, param } return nil, nil @@ -67,7 +67,7 @@ func (r *Ref) getRef(prop *conf.Properties, param conf.BindParam) (interface{}, return v.Elem().Interface(), nil } -func (r *Ref) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (r *Ref) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := r.getRef(prop, param) if err != nil { return err @@ -79,7 +79,7 @@ func (r *Ref) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (r *Ref) Validate(prop *conf.Properties, param conf.BindParam) error { +func (r *Ref) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := r.getRef(prop, param) if r.f != nil { return r.f(v) diff --git a/dync/string.go b/dync/string.go index c368f198..e6b6bbaa 100644 --- a/dync/string.go +++ b/dync/string.go @@ -38,11 +38,11 @@ func (x *String) OnValidate(f StringValidateFunc) { x.f = f } -func (x *String) getString(prop *conf.Properties, param conf.BindParam) (string, error) { +func (x *String) getString(prop conf.ReadOnlyProperties, param conf.BindParam) (string, error) { return GetProperty(prop, param) } -func (x *String) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *String) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getString(prop, param) if err != nil { return err @@ -51,7 +51,7 @@ func (x *String) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *String) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *String) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getString(prop, param) if err != nil { return err diff --git a/dync/time.go b/dync/time.go index 98c3b6d8..1a0cce7c 100644 --- a/dync/time.go +++ b/dync/time.go @@ -40,7 +40,7 @@ func (x *Time) OnValidate(f TimeValidateFunc) { x.f = f } -func (x *Time) getTime(prop *conf.Properties, param conf.BindParam) (time.Time, error) { +func (x *Time) getTime(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Time, error) { s, err := GetProperty(prop, param) if err != nil { return time.Time{}, err @@ -52,7 +52,7 @@ func (x *Time) getTime(prop *conf.Properties, param conf.BindParam) (time.Time, return v, nil } -func (x *Time) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Time) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getTime(prop, param) if err != nil { return err @@ -61,7 +61,7 @@ func (x *Time) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Time) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Time) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getTime(prop, param) if err != nil { return err diff --git a/dync/uint32.go b/dync/uint32.go index d6451075..a48ee5c2 100644 --- a/dync/uint32.go +++ b/dync/uint32.go @@ -39,7 +39,7 @@ func (x *Uint32) OnValidate(f Uint32ValidateFunc) { x.f = f } -func (x *Uint32) getUint32(prop *conf.Properties, param conf.BindParam) (uint32, error) { +func (x *Uint32) getUint32(prop conf.ReadOnlyProperties, param conf.BindParam) (uint32, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Uint32) getUint32(prop *conf.Properties, param conf.BindParam) (uint32, return uint32(v), nil } -func (x *Uint32) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Uint32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint32(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Uint32) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Uint32) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Uint32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint32(prop, param) if err != nil { return err diff --git a/dync/uint64.go b/dync/uint64.go index 0aad5db2..bae97a64 100644 --- a/dync/uint64.go +++ b/dync/uint64.go @@ -39,7 +39,7 @@ func (x *Uint64) OnValidate(f Uint64ValidateFunc) { x.f = f } -func (x *Uint64) getUint64(prop *conf.Properties, param conf.BindParam) (uint64, error) { +func (x *Uint64) getUint64(prop conf.ReadOnlyProperties, param conf.BindParam) (uint64, error) { s, err := GetProperty(prop, param) if err != nil { return 0, err @@ -51,7 +51,7 @@ func (x *Uint64) getUint64(prop *conf.Properties, param conf.BindParam) (uint64, return v, nil } -func (x *Uint64) Refresh(prop *conf.Properties, param conf.BindParam) error { +func (x *Uint64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint64(prop, param) if err != nil { return err @@ -60,7 +60,7 @@ func (x *Uint64) Refresh(prop *conf.Properties, param conf.BindParam) error { return nil } -func (x *Uint64) Validate(prop *conf.Properties, param conf.BindParam) error { +func (x *Uint64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint64(prop, param) if err != nil { return err diff --git a/gs/internal/gs_ctx/gs.go b/gs/internal/gs_ctx/gs.go index 05b5a431..ee3ef5dc 100755 --- a/gs/internal/gs_ctx/gs.go +++ b/gs/internal/gs_ctx/gs.go @@ -101,10 +101,6 @@ func (c *Container) Context() context.Context { return c.ctx } -func (c *Container) Properties() *dync.Properties { - return c.p -} - type BeanInit interface { OnInit(ctx gs.Context) error } diff --git a/gs/internal/gs_ctx/gs_test.go b/gs/internal/gs_ctx/gs_test.go index 226100ea..14e2ff21 100755 --- a/gs/internal/gs_ctx/gs_test.go +++ b/gs/internal/gs_ctx/gs_test.go @@ -204,7 +204,7 @@ type Setting struct { func TestApplicationContext_ValueTag(t *testing.T) { c := gs_ctx.New() - p := conf.NewProperties() + p := conf.New() p.Set("int", int(3)) p.Set("uint", uint(3)) p.Set("float", float32(3)) @@ -643,7 +643,7 @@ func NewPtrStudent(teacher Teacher, room string) *Student { } func TestApplicationContext_RegisterBeanFn(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("room", "Class 3 Grade 1") c := gs_ctx.New() @@ -711,7 +711,7 @@ func TestApplicationContext_Profile(t *testing.T) { }) t.Run("bean:_c:test", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("spring.profiles.active", "test") c := gs_ctx.New() @@ -859,7 +859,7 @@ func (m localManager) Cluster() string { func TestApplicationContext_RegisterBeanFn2(t *testing.T) { t.Run("ptr manager", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("manager.version", "1.0.0") c := gs_ctx.New() @@ -882,7 +882,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("manager.version", "1.0.0") c := gs_ctx.New() @@ -905,7 +905,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("manager.version", "1.0.0") c := gs_ctx.New() c.Provide(NewManagerRetError) @@ -915,7 +915,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return error nil", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("manager.version", "1.0.0") c := gs_ctx.New() @@ -927,7 +927,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { }) t.Run("manager return nil", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("manager.version", "1.0.0") c := gs_ctx.New() @@ -1008,7 +1008,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { assert.Error(t, err, "error") } - prop := conf.NewProperties() + prop := conf.New() prop.Set("int", 0) c := gs_ctx.New() @@ -1045,7 +1045,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { assert.Error(t, err, "error") } - prop := conf.NewProperties() + prop := conf.New() prop.Set("int", 0) c := gs_ctx.New() @@ -1093,7 +1093,7 @@ type RecoresCluster struct { } func TestApplicationContext_ValueBincoreng(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") c := gs_ctx.New() @@ -1150,7 +1150,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") c := gs_ctx.New() @@ -1168,7 +1168,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") c := gs_ctx.New() @@ -1296,7 +1296,7 @@ func TestOptionPattern(t *testing.T) { func TestOptionConstructorArg(t *testing.T) { t.Run("option default", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("president", "CaiYuanPei") c := gs_ctx.New() @@ -1315,7 +1315,7 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withClassName", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("president", "CaiYuanPei") c := gs_ctx.New() @@ -1335,7 +1335,7 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("class_name", "二年级03班") prop.Set("president", "CaiYuanPei") @@ -1358,7 +1358,7 @@ func TestOptionConstructorArg(t *testing.T) { }) t.Run("option withStudents withClassName", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("class_name", "二年级06班") prop.Set("president", "CaiYuanPei") @@ -1431,7 +1431,7 @@ type Service struct { func TestApplicationContext_RegisterMethodBean(t *testing.T) { t.Run("method bean", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1458,7 +1458,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean condition", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1481,7 +1481,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean arg", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1507,7 +1507,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean wire to other bean", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1561,7 +1561,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { } }() - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1579,7 +1579,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean autowire", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1596,7 +1596,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1622,7 +1622,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector type error", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1635,7 +1635,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1661,7 +1661,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { }) t.Run("method bean selector beanId error", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -1694,7 +1694,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { return 0, errors.New("error level") }) - prop := conf.NewProperties() + prop := conf.New() prop.Set("time", "2018-12-20") prop.Set("duration", "1h") prop.Set("level", "debug") @@ -1837,7 +1837,7 @@ func NewNilVarObj(i interface{}, options ...VarOptionFunc) *VarObj { func TestApplicationContext_RegisterOptionBean(t *testing.T) { t.Run("nil param 0", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("var.obj", "description") c := gs_ctx.New() @@ -1857,7 +1857,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 1", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("var.obj", "description") c := gs_ctx.New() @@ -1878,7 +1878,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option param 2", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("var.obj", "description") c := gs_ctx.New() @@ -2045,7 +2045,7 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - prop := conf.NewProperties() + prop := conf.New() prop.Set("int", 0) c := gs_ctx.New() @@ -2119,7 +2119,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { t.Run("private", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("sdk.wx.auto-create", true) prop.Set("sdk.wx.enable", true) @@ -2138,7 +2138,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { }) t.Run("public", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("sdk.wx.auto-create", true) prop.Set("sdk.wx.enable", true) @@ -2267,7 +2267,7 @@ func TestApplicationContext_Properties(t *testing.T) { MapA map[string]string `value:"${map_a:=}"` }{} - prop := conf.NewProperties() + prop := conf.New() prop.Set("map_a.nba", "nba") prop.Set("map_a.cba", "cba") prop.Set("int_a", "3") @@ -2379,7 +2379,7 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:test", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("spring.profiles.active", "test") c := gs_ctx.New() @@ -2395,7 +2395,7 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("bean:test_ctx:stable", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("spring.profiles.active", "stable") c := gs_ctx.New() @@ -2412,7 +2412,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("option withClassName Condition", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("president", "CaiYuanPei") prop.Set("class_floor", 2) @@ -2436,7 +2436,7 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("option withClassName Apply", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("president", "CaiYuanPei") onProperty := gs_cond.OnProperty("class_name_enable") @@ -2462,7 +2462,7 @@ func TestDefaultSpringContext(t *testing.T) { }) t.Run("method bean cond", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("server.version", "1.0.0") c := gs_ctx.New() @@ -2729,7 +2729,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { func TestApplicationContext_Invoke(t *testing.T) { t.Run("not run", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("version", "v0.0.1") c := gs_ctx.New() @@ -2745,7 +2745,7 @@ func TestApplicationContext_Invoke(t *testing.T) { }) t.Run("run", func(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("version", "v0.0.1") prop.Set("spring.profiles.active", "dev") @@ -2827,7 +2827,7 @@ func newCircularB() *circularB { func TestLazy(t *testing.T) { for i := 0; i < 1; i++ { - prop := conf.NewProperties() + prop := conf.New() prop.Set("spring.main.allow-circular-references", "true") c := gs_ctx.New() @@ -2889,7 +2889,7 @@ func (c *ContextAware) Echo(str string) string { } func TestContextAware(t *testing.T) { - prop := conf.NewProperties() + prop := conf.New() prop.Set("prefix", "hello") c := gs_ctx.New() @@ -2930,7 +2930,7 @@ func TestDynamic(t *testing.T) { }) config.Slice.Init(make([]string, 0)) config.Map.Init(make(map[string]string)) - config.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil }) @@ -2941,7 +2941,7 @@ func TestDynamic(t *testing.T) { c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { p.Wrapper.Slice.Init(make([]string, 0)) p.Wrapper.Map.Init(make(map[string]string)) - p.Wrapper.Event.OnEvent(func(prop *conf.Properties, param conf.BindParam) error { + p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil }) @@ -2957,7 +2957,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.NewProperties() + p := conf.New() p.Set("int", 4) p.Set("float", 2.3) p.Set("map.a", 1) @@ -2970,7 +2970,7 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.map.b", 8) p.Set("wrapper.slice[0]", 4) p.Set("wrapper.slice[1]", 6) - c.Properties().Refresh(p) + c.RefreshProperties(p) } { @@ -2981,7 +2981,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.NewProperties() + p := conf.New() p.Set("int", 6) p.Set("float", 5.1) p.Set("map.a", 9) @@ -2994,7 +2994,7 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.map.b", 4) p.Set("wrapper.slice[0]", 2) p.Set("wrapper.slice[1]", 1) - err = c.Properties().Refresh(p) + err = c.RefreshProperties(p) assert.Error(t, err, "validate failed on \"value:\\\"${int:=3}\\\" expr:\\\"$<6\\\"\" for value 9") } @@ -3006,7 +3006,7 @@ func TestDynamic(t *testing.T) { } { - p := conf.NewProperties() + p := conf.New() p.Set("int", 1) p.Set("float", 5.1) p.Set("map.a", 9) @@ -3019,7 +3019,7 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.map.b", 4) p.Set("wrapper.slice[0]", 2) p.Set("wrapper.slice[1]", 1) - err = c.Properties().Refresh(p) + err = c.RefreshProperties(p) assert.Error(t, err, "should greeter than 3") } diff --git a/gs/sysconf/sysconf.go b/gs/sysconf/sysconf.go index 1835fd64..218ac9d2 100644 --- a/gs/sysconf/sysconf.go +++ b/gs/sysconf/sysconf.go @@ -23,7 +23,7 @@ import ( ) var ( - prop = conf.NewProperties() + prop = conf.New() lock sync.Mutex ) @@ -51,7 +51,7 @@ func Unset(key string) { func Clear() { lock.Lock() defer lock.Unlock() - prop = conf.NewProperties() + prop = conf.New() } // Clone copies all properties into another properties. @@ -59,7 +59,7 @@ func Clone() *conf.Properties { lock.Lock() m := prop.Data() lock.Unlock() - p := conf.NewProperties() + p := conf.New() for k, v := range m { _ = p.Set(k, v) } From aa60f8c4cf8999aad9bd4ecb26ed718e082555fe Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 31 Dec 2024 10:33:17 +0800 Subject: [PATCH 16/75] code refactor --- dync/dync_test.go | 26 +++++++++---------- dync/ref.go | 66 +++++++++++++++-------------------------------- 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/dync/dync_test.go b/dync/dync_test.go index 9708582d..b7625e27 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -55,12 +55,12 @@ func (x *Integer) MarshalJSON() ([]byte, error) { } type Config struct { - Integer Integer `value:"${int:=3}" expr:"$<6"` - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` - Map dync.Ref `value:"${map:=}"` - Slice dync.Ref `value:"${slice:=}"` - Event dync.Event `value:"${event}"` + Integer Integer `value:"${int:=3}" expr:"$<6"` + Int dync.Int64 `value:"${int:=3}" expr:"$<6"` + Float dync.Float64 `value:"${float:=1.2}"` + Map dync.Ref[map[string]string] `value:"${map:=}"` + Slice dync.Ref[[]string] `value:"${slice:=}"` + Event dync.Event `value:"${event}"` } func newTest() (*dync.Properties, *Config, error) { @@ -81,7 +81,7 @@ func TestDynamic(t *testing.T) { return } b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":null,"Slice":null,"Event":{}}`) + assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) }) t.Run("init", func(t *testing.T) { @@ -89,8 +89,8 @@ func TestDynamic(t *testing.T) { if err != nil { return } - cfg.Slice.Init(make([]string, 0)) - cfg.Map.Init(make(map[string]string)) + //cfg.Slice.Init(make([]string, 0)) + //cfg.Map.Init(make(map[string]string)) cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil @@ -122,8 +122,8 @@ func TestDynamic(t *testing.T) { } return nil }) - cfg.Slice.Init(make([]string, 0)) - cfg.Map.Init(make(map[string]string)) + //cfg.Slice.Init(make([]string, 0)) + //cfg.Map.Init(make(map[string]string)) cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil @@ -160,8 +160,8 @@ func TestDynamic(t *testing.T) { } return nil }) - cfg.Slice.Init(make([]string, 0)) - cfg.Map.Init(make(map[string]string)) + //cfg.Slice.Init(make([]string, 0)) + //cfg.Map.Init(make(map[string]string)) cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil diff --git a/dync/ref.go b/dync/ref.go index c222f239..17f00c62 100644 --- a/dync/ref.go +++ b/dync/ref.go @@ -26,67 +26,43 @@ import ( type RefValidateFunc func(v interface{}) error -type Ref struct { - v atomic.Value - f RefValidateFunc - init func() (conf.ReadOnlyProperties, conf.BindParam) +type Ref[T interface{}] struct { + v atomic.Value + f RefValidateFunc } -func (r *Ref) Init(i interface{}) error { - r.v.Store(i) - if r.init == nil { - return nil - } - prop, param := r.init() - r.init = nil - return r.Refresh(prop, param) -} - -func (r *Ref) Value() interface{} { - return r.v.Load() -} - -func (r *Ref) OnValidate(f RefValidateFunc) { +func (r *Ref[T]) OnValidate(f RefValidateFunc) { r.f = f } -func (r *Ref) getRef(prop conf.ReadOnlyProperties, param conf.BindParam) (interface{}, error) { - o := r.Value() - if o == nil { - r.init = func() (conf.ReadOnlyProperties, conf.BindParam) { - return prop, param - } - return nil, nil - } - t := reflect.TypeOf(o) - v := reflect.New(t) - err := conf.BindValue(prop, v.Elem(), t, param, nil) - if err != nil { - return nil, err - } - return v.Elem().Interface(), nil +func bindRef[T any](o T, prop conf.ReadOnlyProperties, param conf.BindParam) error { + t := reflect.TypeOf(o).Elem() + v := reflect.ValueOf(o).Elem() + return conf.BindValue(prop, v, t, param, nil) } -func (r *Ref) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := r.getRef(prop, param) +func (r *Ref[T]) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { + var o T + err := bindRef(&o, prop, param) if err != nil { return err } - if v == nil { - return nil - } - r.v.Store(v) + r.v.Store(o) return nil } -func (r *Ref) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := r.getRef(prop, param) +func (r *Ref[T]) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { + var o T + err := bindRef(&o, prop, param) + if err != nil { + return err + } if r.f != nil { - return r.f(v) + return r.f(o) } return err } -func (r *Ref) MarshalJSON() ([]byte, error) { - return json.Marshal(r.Value()) +func (r *Ref[T]) MarshalJSON() ([]byte, error) { + return json.Marshal(r.v.Load()) } From e8e1d3d82e5f1c4f7830a49490d3568ac3532c07 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 31 Dec 2024 13:55:07 +0800 Subject: [PATCH 17/75] code refactor --- gs/gs.go | 6 +- gs/internal/gs/{gs.go => core.go} | 0 gs/internal/gs_app/app.go | 67 +++++++++++-------- .../gs_app/{app_bootstrap.go => boot.go} | 31 ++++++--- gs/internal/gs_conf/conf.go | 12 ++-- gs/internal/gs_ctx/{gs.go => ctx.go} | 2 +- .../gs_ctx/{gs_test.go => ctx_test.go} | 20 +++--- 7 files changed, 79 insertions(+), 59 deletions(-) rename gs/internal/gs/{gs.go => core.go} (100%) rename gs/internal/gs_app/{app_bootstrap.go => boot.go} (71%) rename gs/internal/gs_ctx/{gs.go => ctx.go} (99%) rename gs/internal/gs_ctx/{gs_test.go => ctx_test.go} (99%) diff --git a/gs/gs.go b/gs/gs.go index 02977415..e6cc50ec 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -53,9 +53,9 @@ func ShutDown(msg ...string) { app.ShutDown(msg...) } -// Bootstrap 参考 App.Bootstrap 的解释。 -func Bootstrap() *gs_app.Bootstrapper { - return app.Bootstrap() +// Boot 参考 App.Boot 的解释。 +func Boot() *gs_app.Boot { + return app.Boot() } // Object 参考 Container.Object 的解释。 diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/core.go similarity index 100% rename from gs/internal/gs/gs.go rename to gs/internal/gs/core.go diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 2b9b5418..de0e6cb6 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -26,6 +26,7 @@ import ( "syscall" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) @@ -56,19 +57,20 @@ type tempApp struct { type App struct { *tempApp + b *Boot c *gs_ctx.Container - b *Bootstrapper + p *gs_conf.Configuration exitChan chan struct{} - Events []AppEvent `autowire:"${application-event.collection:=*?}"` - Runners []AppRunner `autowire:"${command-line-runner.collection:=*?}"` + Events []AppEvent `autowire:"${application-event.collection:=*?}"` } // NewApp application 的构造函数 func NewApp() *App { return &App{ c: gs_ctx.New(), + p: gs_conf.NewConfiguration(), tempApp: &tempApp{}, exitChan: make(chan struct{}), } @@ -81,37 +83,46 @@ func (app *App) Banner(banner string) { func (app *App) Start() (gs.Context, error) { - app.Object(app) + //showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) + //if showBanner { + // app.printBanner(app.getBanner(e)) + //} - // showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) - // if showBanner { - // app.printBanner(app.getBanner(e)) - // } + if app.b != nil { + err := app.b.run() + if err != nil { + return nil, err + } + } - // if app.b != nil { - // if err := app.b.start(e); err != nil { - // return err - // } - // } - // - // if err := app.loadProperties(e); err != nil { - // return err - // } + p, err := app.p.Refresh() + if err != nil { + return nil, err + } - // // 保存从环境变量和命令行解析的属性 - // for _, k := range e.p.Keys() { - // app.c.initProperties.Set(k, e.p.Get(k)) - // } + app.Object(app) - if err := app.c.Refresh(false); err != nil { + err = app.c.RefreshProperties(p) + if err != nil { return nil, err } - // 执行命令行启动器 - for _, r := range app.Runners { - r.Run(app.c) + err = app.c.Refresh(false) + if err != nil { + return nil, err } + //var runners []AppRunner + //err = app.c.Get(&runners, "${command-line-runner.collection:=*?}") + //if err != nil { + // return nil, err + //} + // + //// 执行命令行启动器 + //for _, r := range runners { + // r.Run(app.c) + //} + // 通知应用启动事件 for _, event := range app.Events { event.OnAppStart(app.c) @@ -219,10 +230,10 @@ func (app *App) ShutDown(msg ...string) { } } -// Bootstrap 返回 *bootstrap 对象。 -func (app *App) Bootstrap() *Bootstrapper { +// Boot 返回 *bootstrap 对象。 +func (app *App) Boot() *Boot { if app.b == nil { - app.b = newBootstrap() + app.b = newBoot() } return app.b } diff --git a/gs/internal/gs_app/app_bootstrap.go b/gs/internal/gs_app/boot.go similarity index 71% rename from gs/internal/gs_app/app_bootstrap.go rename to gs/internal/gs_app/boot.go index 3f50661d..7b755827 100644 --- a/gs/internal/gs_app/app_bootstrap.go +++ b/gs/internal/gs_app/boot.go @@ -20,41 +20,50 @@ import ( "reflect" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) -type Bootstrapper struct { +type Boot struct { c *gs_ctx.Container + p *gs_conf.Bootstrap } -func newBootstrap() *Bootstrapper { - return &Bootstrapper{ +func newBoot() *Boot { + return &Boot{ c: gs_ctx.New(), + p: gs_conf.NewBootstrap(), } } -func (b *Bootstrapper) Group(fn gs.GroupFunc) { +func (b *Boot) Group(fn gs.GroupFunc) { b.c.Group(fn) } // Object 参考 Container.Object 的解释。 -func (b *Bootstrapper) Object(i interface{}) *gs.BeanDefinition { +func (b *Boot) Object(i interface{}) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *Bootstrapper) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { +func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(ctor, args...)) } -func (b *Bootstrapper) start() error { +func (b *Boot) run() error { + + p, err := b.p.Refresh() + if err != nil { + return err + } b.c.Object(b) - // if err := b.loadBootstrap(e); err != nil { - // return err - // } - // + err = b.c.RefreshProperties(p) + if err != nil { + return err + } + // // 保存从环境变量和命令行解析的属性 // for _, k := range e.p.Keys() { // b.c.initProperties.Set(k, e.p.Get(k)) diff --git a/gs/internal/gs_conf/conf.go b/gs/internal/gs_conf/conf.go index 1a6035a9..e786e9cc 100644 --- a/gs/internal/gs_conf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -97,17 +97,17 @@ func (c *Configuration) Refresh() (conf.ReadOnlyProperties, error) { return p, nil } -/******************************* Bootstrapper ********************************/ +/******************************** Bootstrap **********************************/ -// Bootstrapper is a layered bootstrap configuration. -type Bootstrapper struct { +// Bootstrap is a layered bootstrap configuration. +type Bootstrap struct { LocalFile *PropertySources Environment *Environment CommandArgs *CommandArgs } -func NewBootstrapper() *Bootstrapper { - return &Bootstrapper{ +func NewBootstrap() *Bootstrap { + return &Bootstrap{ LocalFile: NewPropertySources(ConfigTypeLocal, "bootstrap"), Environment: NewEnvironment(), CommandArgs: NewCommandArgs(), @@ -115,7 +115,7 @@ func NewBootstrapper() *Bootstrapper { } // Refresh merges all layers into a properties as read-only. -func (c *Bootstrapper) Refresh() (conf.ReadOnlyProperties, error) { +func (c *Bootstrap) Refresh() (conf.ReadOnlyProperties, error) { p := sysconf.Clone() err := merge(p, c.Environment, c.CommandArgs) diff --git a/gs/internal/gs_ctx/gs.go b/gs/internal/gs_ctx/ctx.go similarity index 99% rename from gs/internal/gs_ctx/gs.go rename to gs/internal/gs_ctx/ctx.go index ee3ef5dc..73ca319f 100755 --- a/gs/internal/gs_ctx/gs.go +++ b/gs/internal/gs_ctx/ctx.go @@ -356,7 +356,7 @@ func (c *Container) clear() { c.tempContainer = nil } -func (c *Container) RefreshProperties(p *conf.Properties) error { +func (c *Container) RefreshProperties(p conf.ReadOnlyProperties) error { return c.p.Refresh(p) } diff --git a/gs/internal/gs_ctx/gs_test.go b/gs/internal/gs_ctx/ctx_test.go similarity index 99% rename from gs/internal/gs_ctx/gs_test.go rename to gs/internal/gs_ctx/ctx_test.go index 14e2ff21..0c263050 100755 --- a/gs/internal/gs_ctx/gs_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -911,7 +911,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.Provide(NewManagerRetError) c.RefreshProperties(prop) err := c.Refresh(false) - assert.Error(t, err, "gs_test.go:\\d* error") + assert.Error(t, err, "ctx_test.go:\\d* error") }) t.Run("manager return error nil", func(t *testing.T) { @@ -2903,11 +2903,11 @@ func TestContextAware(t *testing.T) { } type DynamicConfig struct { - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` - Map dync.Ref `value:"${map:=}"` - Slice dync.Ref `value:"${slice:=}"` - Event dync.Event `value:"${event}"` + Int dync.Int64 `value:"${int:=3}" expr:"$<6"` + Float dync.Float64 `value:"${float:=1.2}"` + Map dync.Ref[map[string]string] `value:"${map:=}"` + Slice dync.Ref[[]string] `value:"${slice:=}"` + Event dync.Event `value:"${event}"` } type DynamicConfigWrapper struct { @@ -2928,8 +2928,8 @@ func TestDynamic(t *testing.T) { } return nil }) - config.Slice.Init(make([]string, 0)) - config.Map.Init(make(map[string]string)) + //config.Slice.Init(make([]string, 0)) + //config.Map.Init(make(map[string]string)) config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil @@ -2939,8 +2939,8 @@ func TestDynamic(t *testing.T) { cfg = config }) c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { - p.Wrapper.Slice.Init(make([]string, 0)) - p.Wrapper.Map.Init(make(map[string]string)) + //p.Wrapper.Slice.Init(make([]string, 0)) + //p.Wrapper.Map.Init(make(map[string]string)) p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil From 38d3ec7350e00093ab7122e81503cf57b0d2a2f4 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 31 Dec 2024 21:20:54 +0800 Subject: [PATCH 18/75] code refactor --- gs/internal/gs_app/app.go | 138 ++++++++------------------------- gs/internal/gs_app/banner.go | 57 ++++++++++++++ gs/internal/gs_app/boot.go | 32 ++++++-- gs/internal/gs_ctx/ctx.go | 58 ++++++++++---- gs/internal/gs_ctx/ctx_test.go | 96 +++++++++++------------ 5 files changed, 204 insertions(+), 177 deletions(-) create mode 100644 gs/internal/gs_app/banner.go diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index de0e6cb6..4e78b6d9 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -22,7 +22,6 @@ import ( "os" "os/signal" "reflect" - "strings" "syscall" "github.com/go-spring/spring-core/gs/internal/gs" @@ -49,13 +48,9 @@ type AppEvent interface { OnAppStop(ctx context.Context) // 应用停止的事件 } -type tempApp struct { - banner string -} - // App 应用 type App struct { - *tempApp + banner string b *Boot c *gs_ctx.Container @@ -68,25 +63,29 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { - return &App{ + app := &App{ + banner: DefaultBanner, c: gs_ctx.New(), p: gs_conf.NewConfiguration(), - tempApp: &tempApp{}, exitChan: make(chan struct{}), } + app.Object(app) + return app } -// Banner 自定义 banner 字符串。 -func (app *App) Banner(banner string) { - app.banner = banner +func (app *App) Run() error { + _, err := app.Start() + if err != nil { + return err + } + app.wait() + app.Stop() + return nil } func (app *App) Start() (gs.Context, error) { - //showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) - //if showBanner { - // app.printBanner(app.getBanner(e)) - //} + app.showBanner() if app.b != nil { err := app.b.run() @@ -100,28 +99,26 @@ func (app *App) Start() (gs.Context, error) { return nil, err } - app.Object(app) - err = app.c.RefreshProperties(p) if err != nil { return nil, err } - err = app.c.Refresh(false) + err = app.c.Refresh() if err != nil { return nil, err } - //var runners []AppRunner - //err = app.c.Get(&runners, "${command-line-runner.collection:=*?}") - //if err != nil { - // return nil, err - //} - // - //// 执行命令行启动器 - //for _, r := range runners { - // r.Run(app.c) - //} + var runners []AppRunner + err = app.c.Get(&runners, "${spring.app.runners:=*?}") + if err != nil { + return nil, err + } + + // 执行命令行启动器 + for _, r := range runners { + r.Run(app.c) + } // 通知应用启动事件 for _, event := range app.Events { @@ -150,76 +147,9 @@ func (app *App) wait() { } func (app *App) Stop() { - - // if app.b != nil { - // app.b.c.Close() - // } - app.c.Close() } -func (app *App) Run() error { - _, err := app.Start() - if err != nil { - return err - } - app.wait() - app.Stop() - return nil -} - -const DefaultBanner = ` - (_) - __ _ ___ ___ _ __ _ __ _ _ __ __ _ - / _' | / _ \ ______ / __| | '_ \ | '__| | | | '_ \ / _' | -| (_| | | (_) | |______| \__ \ | |_) | | | | | | | | | | (_| | - \__, | \___/ |___/ | .__/ |_| |_| |_| |_| \__, | - __/ | | | __/ | - |___/ |_| |___/ -` - -func (app *App) getBanner() string { - if app.banner != "" { - return app.banner - } - banner := DefaultBanner - // for _, resource := range resources { - // if b, _ := ioutil.ReadAll(resource); b != nil { - // banner = string(b) - // } - // } - return banner -} - -// printBanner 打印 banner 到控制台 -func (app *App) printBanner(banner string) { - - if banner[0] != '\n' { - fmt.Println() - } - - maxLength := 0 - for _, s := range strings.Split(banner, "\n") { - fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN - if len(s) > maxLength { - maxLength = len(s) - } - } - - if banner[len(banner)-1] != '\n' { - fmt.Println() - } - - var padding []byte - if n := (maxLength - len(Version)) / 2; n > 0 { - padding = make([]byte, n) - for i := range padding { - padding[i] = ' ' - } - } - fmt.Println(string(padding) + Version + "\n") -} - // ShutDown 关闭执行器 func (app *App) ShutDown(msg ...string) { select { @@ -238,15 +168,6 @@ func (app *App) Boot() *Boot { return app.b } -func (app *App) Group(fn gs.GroupFunc) { - app.c.Group(fn) -} - -// Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { - return app.c.Accept(b) -} - // Object 参考 Container.Object 的解释。 func (app *App) Object(i interface{}) *gs.BeanDefinition { return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) @@ -256,3 +177,12 @@ func (app *App) Object(i interface{}) *gs.BeanDefinition { func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return app.c.Accept(gs_ctx.NewBean(ctor, args...)) } + +// Accept 参考 Container.Accept 的解释。 +func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { + return app.c.Accept(b) +} + +func (app *App) Group(fn gs.GroupFunc) { + app.c.Group(fn) +} diff --git a/gs/internal/gs_app/banner.go b/gs/internal/gs_app/banner.go new file mode 100644 index 00000000..b1592397 --- /dev/null +++ b/gs/internal/gs_app/banner.go @@ -0,0 +1,57 @@ +package gs_app + +import ( + "fmt" + "strings" +) + +const DefaultBanner = ` + (_) + __ _ ___ ___ _ __ _ __ _ _ __ __ _ + / _' | / _ \ ______ / __| | '_ \ | '__| | | | '_ \ / _' | +| (_| | | (_) | |______| \__ \ | |_) | | | | | | | | | | (_| | + \__, | \___/ |___/ | .__/ |_| |_| |_| |_| \__, | + __/ | | | __/ | + |___/ |_| |___/ +` + +// Banner 自定义 banner 字符串。 +func (app *App) Banner(banner string) { + app.banner = banner +} + +func (app *App) showBanner() { + // showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) + // if showBanner { + // app.printBanner(app.banner) + // } +} + +// printBanner 打印 banner 到控制台 +func (app *App) printBanner(banner string) { + + if banner[0] != '\n' { + fmt.Println() + } + + maxLength := 0 + for _, s := range strings.Split(banner, "\n") { + fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN + if len(s) > maxLength { + maxLength = len(s) + } + } + + if banner[len(banner)-1] != '\n' { + fmt.Println() + } + + var padding []byte + if n := (maxLength - len(Version)) / 2; n > 0 { + padding = make([]byte, n) + for i := range padding { + padding[i] = ' ' + } + } + fmt.Println(string(padding) + Version + "\n") +} diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 7b755827..726f8498 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -24,16 +24,22 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) +type BootRunner interface { + Run(ctx gs.Context) +} + type Boot struct { c *gs_ctx.Container p *gs_conf.Bootstrap } func newBoot() *Boot { - return &Boot{ + b := &Boot{ c: gs_ctx.New(), p: gs_conf.NewBootstrap(), } + b.c.Object(b) + return b } func (b *Boot) Group(fn gs.GroupFunc) { @@ -57,17 +63,27 @@ func (b *Boot) run() error { return err } - b.c.Object(b) - err = b.c.RefreshProperties(p) if err != nil { return err } - // // 保存从环境变量和命令行解析的属性 - // for _, k := range e.p.Keys() { - // b.c.initProperties.Set(k, e.p.Get(k)) - // } + err = b.c.Refresh() + if err != nil { + return err + } + + var runners []AppRunner + err = b.c.Get(&runners, "${spring.boot.runners:=*?}") + if err != nil { + return err + } + + // 执行命令行启动器 + for _, r := range runners { + r.Run(b.c) + } - return b.c.Refresh(false) + b.c.Close() + return nil } diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index 73ca319f..d4150d1d 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -360,7 +360,7 @@ func (c *Container) RefreshProperties(p conf.ReadOnlyProperties) error { return c.p.Refresh(p) } -func (c *Container) Refresh(autoClear bool) (err error) { +func (c *Container) Refresh() (err error) { if c.state != Unrefreshed { return errors.New("Container already refreshed") @@ -450,9 +450,9 @@ func (c *Container) Refresh(autoClear bool) (err error) { // cost := time.Now().Sub(start) // c.logger.Infof("refresh %d beans cost %v", len(beansById), cost) - if autoClear && !c.ContextAware { - c.clear() - } + // if autoClear && !c.ContextAware { + // c.clear() + // } // c.logger.Info("Container refreshed successfully") return nil @@ -558,16 +558,20 @@ func (tag wireTag) String() string { return b.String() } -func toWireTag(selector gs.BeanSelector) wireTag { +func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { switch s := selector.(type) { case string: - return parseWireTag(s) + s, err := c.resolveTag(s) + if err != nil { + return wireTag{}, err + } + return parseWireTag(s), nil case gs.BeanDefinition: - return parseWireTag(s.ID()) + return parseWireTag(s.ID()), nil case *gs.BeanDefinition: - return parseWireTag(s.ID()) + return parseWireTag(s.ID()), nil default: - return parseWireTag(gs_util.TypeName(s) + ":") + return parseWireTag(gs_util.TypeName(s) + ":"), nil } } @@ -606,7 +610,10 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) var t reflect.Type switch st := selector.(type) { case string, gs.BeanDefinition, *gs.BeanDefinition: - tag := toWireTag(selector) + tag, err := c.toWireTag(selector) + if err != nil { + return nil, err + } return finder(func(b *gs.BeanDefinition) bool { return b.Match(tag.typeName, tag.beanName) }) @@ -881,15 +888,23 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar return nil } -func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { - - // tag 预处理,可能通过属性值进行指定。 +// resolveTag tag 预处理,可能通过属性值进行指定。 +func (c *Container) resolveTag(tag string) (string, error) { if strings.HasPrefix(tag, "${") { s, err := c.p.Data().Resolve(tag) if err != nil { - return err + return "", err } - tag = s + return s, nil + } + return tag, nil +} + +func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { + + tag, err := c.resolveTag(tag) + if err != nil { + return err } if tag == "" { @@ -899,7 +914,12 @@ func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) e var tags []wireTag if tag != "?" { for _, s := range strings.Split(tag, ",") { - tags = append(tags, toWireTag(s)) + var g wireTag + g, err = c.toWireTag(s) + if err != nil { + return err + } + tags = append(tags, g) } } return c.autowire(v, tags, tag == "?", stack) @@ -1205,7 +1225,11 @@ func (c *Container) Get(i interface{}, selectors ...gs.BeanSelector) error { var tags []wireTag for _, s := range selectors { - tags = append(tags, toWireTag(s)) + g, err := c.toWireTag(s) + if err != nil { + return err + } + tags = append(tags, g) } return c.autowire(v.Elem(), tags, false, stack) } diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index 0c263050..c6c3b2c6 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -48,7 +48,7 @@ func runTest(c *gs_ctx.Container, fn func(gs.Context)) error { fn(p) return PandoraAware{} }) - return c.Refresh(false) + return c.Refresh() } func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { @@ -57,7 +57,7 @@ func TestApplicationContext_RegisterBeanFrozen(t *testing.T) { // c.Object(func() {}).Init(func(f func()) { // c.Object(func() {}) // 不能在这里注册新的 Object // }) - // _ = c.Refresh(false) + // _ = c.Refresh() // }, "should call before Refresh") } @@ -78,7 +78,7 @@ func TestApplicationContext(t *testing.T) { c.Object(&e).Name("i3") c.Object(&e).Name("i4") - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -95,7 +95,7 @@ func TestApplicationContext(t *testing.T) { c.Object(&e).Name("i4") c.Object(&e).Name("i5") - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) } @@ -226,7 +226,7 @@ func TestApplicationContext_ValueTag(t *testing.T) { err := c.RefreshProperties(p) assert.Nil(t, err) - err = c.Refresh(false) + err = c.Refresh() assert.Nil(t, err) fmt.Printf("%+v\n", setting) @@ -281,7 +281,7 @@ func TestApplicationContext_PrototypeBean(t *testing.T) { f := &PrototypeBeanFactory{} c.Object(f) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) s.Service("Li Lei") @@ -351,7 +351,7 @@ func TestApplicationContext_TypeConverter(t *testing.T) { err := c.RefreshProperties(prop) assert.Nil(t, err) - err = c.Refresh(false) + err = c.Refresh() assert.Nil(t, err) assert.Equal(t, b.EnvType, ENV_TEST) @@ -381,7 +381,7 @@ func TestApplicationContext_NestedBean(t *testing.T) { c := gs_ctx.New() c.Object(new(MyGrouper)).Export((*Grouper)(nil)) c.Object(new(ProxyGrouper)) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) } @@ -399,7 +399,7 @@ func TestApplicationContext_SameNameBean(t *testing.T) { c.Object(new(SamePkgHolder)) c.Object(&pkg1.SamePkg{}).Export((*Pkg)(nil)) c.Object(&pkg2.SamePkg{}).Export((*Pkg)(nil)) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) } @@ -427,7 +427,7 @@ func TestApplicationContext_DiffNameBean(t *testing.T) { c.Object(&DiffPkgOne{}).Name("same").Export((*Pkg)(nil)) c.Object(&DiffPkgTwo{}).Name("same").Export((*Pkg)(nil)) c.Object(new(DiffPkgHolder)) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) } @@ -558,7 +558,7 @@ func TestApplicationContext_Get(t *testing.T) { // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) // c.Object(new(BeanTwo)) -// c.Refresh(false) +// c.Refresh() // // p := <-ch // @@ -736,7 +736,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -751,7 +751,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)).DependsOn(dependsOn...) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) } @@ -764,7 +764,7 @@ func TestApplicationContext_Primary(t *testing.T) { c.Object(&BeanZero{6}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "duplicate beans ") }) @@ -775,7 +775,7 @@ func TestApplicationContext_Primary(t *testing.T) { c.Object(&BeanZero{6}).Primary() c.Object(new(BeanOne)) c.Object(new(BeanTwo)) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "duplicate beans ") }) @@ -818,7 +818,7 @@ func TestDefaultProperties_WireFunc(t *testing.T) { c.Object(func(int) int { return 6 }) obj := new(FuncObj) c.Object(obj) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) i := obj.Fn(3) assert.Equal(t, i, 6) @@ -910,7 +910,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c := gs_ctx.New() c.Provide(NewManagerRetError) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "ctx_test.go:\\d* error") }) @@ -922,7 +922,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.Provide(NewManagerRetErrorNil) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -934,7 +934,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.Provide(NewNullPtrManager) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "return nil") }) } @@ -1004,7 +1004,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { { c := gs_ctx.New() c.Object(&callDestroy{i: 1}).Init((*callDestroy).InitWithError) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "error") } @@ -1041,7 +1041,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { { c := gs_ctx.New() c.Provide(func() destroyable { return &callDestroy{i: 1} }).Init(destroyable.InitWithError) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "error") } @@ -1189,7 +1189,7 @@ func TestApplicationContext_Collect(t *testing.T) { assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) } @@ -1569,7 +1569,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() if err != nil { panic(err) } @@ -1630,7 +1630,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*int)(nil)) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "can't find bean, bean:\"int:\" type:\"\\*gs_test.Server\"") }) @@ -1669,7 +1669,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "NULL") c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "can't find bean, bean:\"NULL\" type:\"\\*gs_test.Server\"") }) } @@ -1703,7 +1703,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { c.Object(&config) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) fmt.Printf("%+v\n", config) @@ -1729,7 +1729,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Object(new(CircleA)) c.Object(new(CircleB)) c.Object(new(CircleC)) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -1740,7 +1740,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Provide(func() *CircleC { return new(CircleC) }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -1753,7 +1753,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Provide(func() *CircleC { return new(CircleC) }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -1768,7 +1768,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { c.Provide(func(a *CircleA) *CircleC { return new(CircleC) }) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "found circle autowire") }) } @@ -1958,7 +1958,7 @@ func TestApplicationContext_Close(t *testing.T) { c := gs_ctx.New() c.Object(func() {}).Destroy(func(f func()) { called = true }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) c.Close() @@ -2172,7 +2172,7 @@ func TestApplicationContext_FnArgCollectBean(t *testing.T) { assert.Equal(t, names, []string{"t1", "t2"}) return func() {} }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) } @@ -2193,7 +2193,7 @@ func TestApplicationContext_BeanCache(t *testing.T) { t.Run("not implement interface", func(t *testing.T) { c := gs_ctx.New() c.Object(func() {}).Export((*filter)(nil)) - err := c.Refresh(false) + err := c.Refresh() assert.Error(t, err, "doesn't implement interface gs_test.filter") }) @@ -2209,7 +2209,7 @@ func TestApplicationContext_BeanCache(t *testing.T) { c.Object(new(filterImpl)).Export((*filter)(nil)).Name("f2") c.Object(&server) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) } @@ -2227,7 +2227,7 @@ func (i Integer) Value() int { func TestApplicationContext_IntInterface(t *testing.T) { c := gs_ctx.New() c.Provide(func() IntInterface { return Integer(5) }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) } @@ -2254,7 +2254,7 @@ func TestApplicationContext_Properties(t *testing.T) { b := new(ArrayProperties) c := gs_ctx.New() c.Object(b) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) }) @@ -2277,7 +2277,7 @@ func TestApplicationContext_Properties(t *testing.T) { c.Object(&obj) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) assert.Equal(t, obj.Int, 5) @@ -2331,7 +2331,7 @@ func TestApplicationContext_Destroy(t *testing.T) { destroyArray[destroyIndex] = 4 destroyIndex++ }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) c.Close() @@ -2492,7 +2492,7 @@ func TestDefaultSpringContext(t *testing.T) { // parent := c.Provide(NewServerInterface).On(cond.OnProperty("server.is.nil")) // c.Provide(ServerInterface.Consumer, parent.ID()) // -// c.Refresh(false) +// c.Refresh() // // var s *Server // ok := p.Get(&s) @@ -2623,7 +2623,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) -// c.Refresh(false) +// c.Refresh() // // c1 := cond.OnBean("BeanOne") // assert.True(t, c1.Matches(c)) @@ -2637,7 +2637,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Object(&BeanZero{5}) // c.Object(new(BeanOne)) -// c.Refresh(false) +// c.Refresh() // // c1 := cond.OnMissingBean("BeanOne") // assert.False(t, c1.Matches(c)) @@ -2655,7 +2655,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // c := gs.New() // c.Property("bool", false) // c.Property("int", 3) -// c.Refresh(false) +// c.Refresh() // // c1 := cond.OnProperty("int") // assert.True(t, c1.Matches(c)) @@ -2707,7 +2707,7 @@ func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { // // c := gs.New() // c.Property(environ.SpringProfilesActive, "test") -// c.Refresh(false) +// c.Refresh() // // profileCond := cond.OnProfile("test") // assert.True(t, profileCond.Matches(c)) @@ -2774,7 +2774,7 @@ func TestEmptyStruct(t *testing.T) { c.Object(objA) objB := &emptyStructB{} c.Object(objB) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) // objA 和 objB 的地址相同但是类型确实不一样。 @@ -2839,7 +2839,7 @@ func TestLazy(t *testing.T) { c.Object(&d) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) assert.NotNil(t, d.b) assert.NotNil(t, d.b.A) @@ -2876,7 +2876,7 @@ func TestDestroyDependence(t *testing.T) { c.Object(new(memory)) c.Object(new(table)).Name("aaa") c.Object(new(table)).Name("bbb") - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) } @@ -2896,7 +2896,7 @@ func TestContextAware(t *testing.T) { b := c.Object(new(ContextAware)) c.RefreshProperties(prop) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) a := b.Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") @@ -2946,7 +2946,7 @@ func TestDynamic(t *testing.T) { return nil }) }) - err := c.Refresh(false) + err := c.Refresh() assert.Nil(t, err) { From 40c568bc3d94f433064f909b1f7c56961ca2e502 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 31 Dec 2024 22:42:41 +0800 Subject: [PATCH 19/75] support configuration bean --- gs/internal/gs/bean.go | 112 +++++++-------------------------- gs/internal/gs_ctx/ctx.go | 97 ++++++++++++++++++++++------ gs/internal/gs_ctx/ctx_test.go | 44 +++++++++++-- 3 files changed, 141 insertions(+), 112 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 0dcc84b3..1480203a 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -61,6 +61,10 @@ type BeanDefinition struct { destroy interface{} // 销毁函数 depends []BeanSelector // 间接依赖项 exports []reflect.Type // 导出的接口 + + configuration bool // 是否扫描成员方法 + includeMethod []string // 包含哪些成员方法 + excludeMethod []string // 排除那些成员方法 } func (d *BeanDefinition) GetName() string { return d.name } @@ -75,6 +79,9 @@ func (d *BeanDefinition) GetInit() interface{} { return d.init } func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } func (d *BeanDefinition) GetDepends() []BeanSelector { return d.depends } func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } +func (d *BeanDefinition) IsConfiguration() bool { return d.configuration } +func (d *BeanDefinition) GetIncludeMethod() []string { return d.includeMethod } +func (d *BeanDefinition) GetExcludeMethod() []string { return d.excludeMethod } // Type 返回 bean 的类型。 func (d *BeanDefinition) Type() reflect.Type { @@ -116,6 +123,14 @@ func (d *BeanDefinition) Wired() bool { return d.status == Wired } +func (d *BeanDefinition) File() string { + return d.file +} + +func (d *BeanDefinition) Line() int { + return d.line +} + // FileLine 返回 bean 的注册点。 func (d *BeanDefinition) FileLine() string { return fmt.Sprintf("%s:%d", d.file, d.line) @@ -244,96 +259,13 @@ func (d *BeanDefinition) export(exports ...interface{}) error { return nil } -//// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -//func NewBean(objOrCtor interface{}, ctorArgs ...Arg) *BeanDefinition { -// -// var v reflect.Value -// var fromValue bool -// var method bool -// var name string -// -// switch i := objOrCtor.(type) { -// case reflect.Value: -// fromValue = true -// v = i -// default: -// v = reflect.ValueOf(i) -// } -// -// t := v.Type() -// if !gs_util.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// if !v.IsValid() || v.IsNil() { -// panic(errors.New("bean can't be nil")) -// } -// -// const skip = 2 -// var f Callable -// _, file, line, _ := runtime.Caller(skip) -// -// // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 -// if !fromValue && t.Kind() == reflect.Func { -// -// if !gs_util.IsConstructor(t) { -// t1 := "func(...)bean" -// t2 := "func(...)(bean, error)" -// panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) -// } -// -// var err error -// f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) -// if err != nil { -// panic(err) -// } -// -// out0 := t.Out(0) -// v = reflect.New(out0) -// if gs_util.IsBeanType(out0) { -// v = v.Elem() -// } -// -// t = v.Type() -// if !gs_util.IsBeanType(t) { -// panic(errors.New("bean must be ref type")) -// } -// -// // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 -// fnPtr := reflect.ValueOf(objOrCtor).Pointer() -// fnInfo := runtime.FuncForPC(fnPtr) -// funcName := fnInfo.Name() -// name = funcName[strings.LastIndex(funcName, "/")+1:] -// name = name[strings.Index(name, ".")+1:] -// if name[0] == '(' { -// name = name[strings.Index(name, ".")+1:] -// } -// method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 -// } -// -// if t.Kind() == reflect.Ptr && !gs_util.IsValueType(t.Elem()) { -// panic(errors.New("bean should be *val but not *ref")) -// } -// -// // Type.String() 一般返回 *pkg.Type 形式的字符串, -// // 我们只取最后的类型名,如有需要请自定义 bean 名称。 -// if name == "" { -// s := strings.Split(t.String(), ".") -// name = strings.TrimPrefix(s[len(s)-1], "*") -// } -// -// return &BeanDefinition{ -// T: t, -// V: v, -// F: f, -// name: name, -// typeName: gs_util.TypeName(t), -// status: Default, -// method: method, -// file: file, -// line: line, -// } -//} +// Configuration 设置 bean 为配置类。 +func (d *BeanDefinition) Configuration(includes []string, excludes []string) *BeanDefinition { + d.configuration = true + d.includeMethod = includes + d.excludeMethod = excludes + return d +} // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index d4150d1d..4293e98c 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "reflect" + "regexp" "runtime" "sort" "strings" @@ -56,13 +57,6 @@ type ContextAware struct { GSContext gs.Context `autowire:""` } -type tempContainer struct { - beans []*gs.BeanDefinition - beansByName map[string][]*gs.BeanDefinition - beansByType map[reflect.Type][]*gs.BeanDefinition - groupFuncs []gs.GroupFunc -} - // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -71,7 +65,10 @@ type tempContainer struct { // go-spring 严格区分了这两种概念,在描述对 bean 的处理时要么单独使用依赖注入或属 // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { - *tempContainer + beans []*gs.BeanDefinition + beansByName map[string][]*gs.BeanDefinition + beansByType map[reflect.Type][]*gs.BeanDefinition + groupFuncs []gs.GroupFunc ctx context.Context cancel context.CancelFunc destroyers []func() @@ -85,15 +82,15 @@ type Container struct { // New 创建 IoC 容器。 func New() *Container { ctx, cancel := context.WithCancel(context.Background()) - return &Container{ - ctx: ctx, - cancel: cancel, - p: dync.New(), - tempContainer: &tempContainer{ - beansByName: make(map[string][]*gs.BeanDefinition), - beansByType: make(map[reflect.Type][]*gs.BeanDefinition), - }, + c := &Container{ + ctx: ctx, + cancel: cancel, + p: dync.New(), + beansByName: make(map[string][]*gs.BeanDefinition), + beansByType: make(map[reflect.Type][]*gs.BeanDefinition), } + c.Object(c).Export((*gs.Context)(nil)) + return c } // Context 返回 IoC 容器的 ctx 对象。 @@ -353,7 +350,6 @@ func (s *wiringStack) sortDestroyers() []func() { } func (c *Container) clear() { - c.tempContainer = nil } func (c *Container) RefreshProperties(p conf.ReadOnlyProperties) error { @@ -368,7 +364,6 @@ func (c *Container) Refresh() (err error) { c.state = RefreshInit // start := time.Now() - c.Object(c).Export((*gs.Context)(nil)) for _, fn := range c.groupFuncs { var beans []*gs.BeanDefinition @@ -379,6 +374,72 @@ func (c *Container) Refresh() (err error) { c.beans = append(c.beans, beans...) } + var newBeans []*gs.BeanDefinition + for _, bd := range c.beans { + if !bd.IsConfiguration() { + continue + } + var ( + includes []*regexp.Regexp + excludes []*regexp.Regexp + ) + ss := bd.GetIncludeMethod() + if len(ss) == 0 { + ss = []string{"New*"} + } + for _, s := range ss { + var x *regexp.Regexp + x, err = regexp.Compile(s) + if err != nil { + return err + } + includes = append(includes, x) + } + ss = bd.GetExcludeMethod() + for _, s := range ss { + var x *regexp.Regexp + x, err = regexp.Compile(s) + if err != nil { + return err + } + excludes = append(excludes, x) + } + n := bd.T.NumMethod() + for i := 0; i < n; i++ { + m := bd.T.Method(i) + skip := false + for _, x := range excludes { + if x.MatchString(m.Name) { + skip = true + break + } + } + if skip { + continue + } + for _, x := range includes { + if x.MatchString(m.Name) { + var f gs.Callable + f, err = gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd}, 0) + if err != nil { + return err + } + name := bd.GetName() + "_" + m.Name + out0 := m.Type.Out(0) + v := reflect.New(out0) + if gs_util.IsBeanType(out0) { + v = v.Elem() + } + t := v.Type() + b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()) + newBeans = append(newBeans, b) + break + } + } + } + } + c.beans = append(c.beans, newBeans...) + c.state = Refreshing for _, b := range c.beans { diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index c6c3b2c6..533a25b7 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -2928,8 +2928,8 @@ func TestDynamic(t *testing.T) { } return nil }) - //config.Slice.Init(make([]string, 0)) - //config.Map.Init(make(map[string]string)) + // config.Slice.Init(make([]string, 0)) + // config.Map.Init(make(map[string]string)) config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil @@ -2939,8 +2939,8 @@ func TestDynamic(t *testing.T) { cfg = config }) c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { - //p.Wrapper.Slice.Init(make([]string, 0)) - //p.Wrapper.Map.Init(make(map[string]string)) + // p.Wrapper.Slice.Init(make([]string, 0)) + // p.Wrapper.Map.Init(make(map[string]string)) p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { fmt.Println("event fired.") return nil @@ -3030,3 +3030,39 @@ func TestDynamic(t *testing.T) { assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) } } + +type ChildBean struct { + s string +} + +type ConfigurationBean struct { + s string +} + +func NewConfigurationBean(s string) *ConfigurationBean { + return &ConfigurationBean{s} +} + +func (c *ConfigurationBean) NewChild() *ChildBean { + return &ChildBean{c.s} +} + +func TestConfiguration(t *testing.T) { + c := gs_ctx.New() + c.Object(&ConfigurationBean{"123"}).Configuration(nil, nil).Name("123") + c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") + if err := c.Refresh(); err != nil { + t.Fatal(err) + } + var b *ChildBean + err := c.Get(&b, "123_NewChild") + if err != nil { + t.Fatal(err) + } + assert.Equal(t, b.s, "123") + err = c.Get(&b, "456_NewChild") + if err != nil { + t.Fatal(err) + } + assert.Equal(t, b.s, "456") +} From 5932b169102eeda41f744977a8997bb46b112551 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 1 Jan 2025 15:42:38 +0800 Subject: [PATCH 20/75] code refactor --- gs/internal/gs_app/app.go | 34 +++++++----------- gs/internal/gs_app/banner.go | 3 ++ gs/internal/gs_app/boot.go | 13 ++++--- gs/internal/gs_arg/arg.go | 18 +++++----- gs/internal/gs_cond/cond.go | 68 ++++++++++++++++++------------------ 5 files changed, 67 insertions(+), 69 deletions(-) diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 4e78b6d9..52f9d344 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -34,16 +34,11 @@ const ( Website = "https://go-spring.com/" ) -// SpringBannerVisible 是否显示 banner。 -const SpringBannerVisible = "spring.banner.visible" - -// AppRunner 命令行启动器接口 type AppRunner interface { Run(ctx gs.Context) } -// AppEvent 应用运行过程中的事件 -type AppEvent interface { +type AppServer interface { OnAppStart(ctx gs.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -58,7 +53,7 @@ type App struct { exitChan chan struct{} - Events []AppEvent `autowire:"${application-event.collection:=*?}"` + Servers []AppServer `autowire:"${spring.app.servers:=*?}"` } // NewApp application 的构造函数 @@ -74,11 +69,16 @@ func NewApp() *App { } func (app *App) Run() error { - _, err := app.Start() - if err != nil { + if _, err := app.Start(); err != nil { return err } - app.wait() + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt, syscall.SIGTERM) + sig := <-ch + app.ShutDown(fmt.Sprintf("signal %v", sig)) + }() + <-app.exitChan app.Stop() return nil } @@ -121,14 +121,14 @@ func (app *App) Start() (gs.Context, error) { } // 通知应用启动事件 - for _, event := range app.Events { + for _, event := range app.Servers { event.OnAppStart(app.c) } // 通知应用停止事件 app.c.Go(func(ctx context.Context) { <-ctx.Done() - for _, event := range app.Events { + for _, event := range app.Servers { event.OnAppStop(context.Background()) } }) @@ -136,16 +136,6 @@ func (app *App) Start() (gs.Context, error) { return app.c, nil } -func (app *App) wait() { - go func() { - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt, syscall.SIGTERM) - sig := <-ch - app.ShutDown(fmt.Sprintf("signal %v", sig)) - }() - <-app.exitChan -} - func (app *App) Stop() { app.c.Close() } diff --git a/gs/internal/gs_app/banner.go b/gs/internal/gs_app/banner.go index b1592397..430edb95 100644 --- a/gs/internal/gs_app/banner.go +++ b/gs/internal/gs_app/banner.go @@ -5,6 +5,9 @@ import ( "strings" ) +// SpringBannerVisible 是否显示 banner。 +const SpringBannerVisible = "spring.banner.visible" + const DefaultBanner = ` (_) __ _ ___ ___ _ __ _ __ _ _ __ __ _ diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 726f8498..5c0b928e 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -42,10 +42,6 @@ func newBoot() *Boot { return b } -func (b *Boot) Group(fn gs.GroupFunc) { - b.c.Group(fn) -} - // Object 参考 Container.Object 的解释。 func (b *Boot) Object(i interface{}) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) @@ -56,6 +52,15 @@ func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { return b.c.Accept(gs_ctx.NewBean(ctor, args...)) } +// Accept 参考 Container.Accept 的解释。 +func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanDefinition { + return b.c.Accept(bd) +} + +func (b *Boot) Group(fn gs.GroupFunc) { + b.c.Group(fn) +} + func (b *Boot) run() error { p, err := b.p.Refresh() diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index e9b49500..bb9e8b86 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -104,7 +104,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { if len(args) > 0 { switch arg := args[0].(type) { - case *optionArg: + case *OptionArg: fnArgs = append(fnArgs, arg) case IndexArg: if arg.n < 0 || arg.n >= fixedArgCount { @@ -125,7 +125,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { for i := 1; i < len(args); i++ { switch arg := args[i].(type) { - case *optionArg: + case *OptionArg: fnArgs = append(fnArgs, arg) case IndexArg: if !shouldIndex { @@ -222,7 +222,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return reflect.Zero(t), nil } return reflect.ValueOf(g.v), nil - case *optionArg: + case *OptionArg: return g.call(ctx) case *gs.BeanDefinition: tag = g.ID() @@ -256,8 +256,8 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return reflect.Value{}, util.Errorf(util.FileLine(), "error type %s", t.String()) } -// optionArg Option 函数的参数绑定。 -type optionArg struct { +// OptionArg Option 函数的参数绑定。 +type OptionArg struct { r *Callable c gs.Condition } @@ -272,7 +272,7 @@ func Provide(fn interface{}, args ...gs.Arg) *Callable { } // Option 返回 Option 函数的参数绑定。 -func Option(fn interface{}, args ...gs.Arg) *optionArg { +func Option(fn interface{}, args ...gs.Arg) *OptionArg { t := reflect.TypeOf(fn) if t.Kind() != reflect.Func || t.NumOut() != 1 { @@ -283,16 +283,16 @@ func Option(fn interface{}, args ...gs.Arg) *optionArg { if err != nil { panic(err) } - return &optionArg{r: r} + return &OptionArg{r: r} } // On 设置一个 gs_cond.Condition 对象。 -func (arg *optionArg) On(c gs.Condition) *optionArg { +func (arg *OptionArg) On(c gs.Condition) *OptionArg { arg.c = c return arg } -func (arg *optionArg) call(ctx gs.ArgContext) (reflect.Value, error) { +func (arg *OptionArg) call(ctx gs.ArgContext) (reflect.Value, error) { var ( ok bool diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index f07f1918..fd7a8011 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -247,24 +247,24 @@ func (n *node) Matches(ctx gs.CondContext) (bool, error) { return false, fmt.Errorf("error condition operator %d", n.op) } -// conditional is a Condition implemented by link of Condition(s). -type conditional struct { +// Conditional is a Condition implemented by link of Condition(s). +type Conditional struct { head *node curr *node } // New returns a Condition implemented by link of Condition(s). -func New() *conditional { +func New() *Conditional { n := &node{} - return &conditional{head: n, curr: n} + return &Conditional{head: n, curr: n} } -func (c *conditional) Matches(ctx gs.CondContext) (bool, error) { +func (c *Conditional) Matches(ctx gs.CondContext) (bool, error) { return c.head.Matches(ctx) } // Or sets a Or operator. -func (c *conditional) Or() *conditional { +func (c *Conditional) Or() *Conditional { n := &node{} c.curr.op = Or c.curr.next = n @@ -273,7 +273,7 @@ func (c *conditional) Or() *conditional { } // And sets a And operator. -func (c *conditional) And() *conditional { +func (c *Conditional) And() *Conditional { n := &node{} c.curr.op = And c.curr.next = n @@ -281,13 +281,13 @@ func (c *conditional) And() *conditional { return c } -// On returns a conditional that starts with one Condition. -func On(cond gs.Condition) *conditional { +// On returns a Conditional that starts with one Condition. +func On(cond gs.Condition) *Conditional { return New().On(cond) } // On adds one Condition. -func (c *conditional) On(cond gs.Condition) *conditional { +func (c *Conditional) On(cond gs.Condition) *Conditional { if c.curr.cond != nil { c.And() } @@ -311,14 +311,14 @@ func HavingValue(havingValue string) PropertyOption { } } -// OnProperty returns a conditional that starts with a Condition that checks a property +// OnProperty returns a Conditional that starts with a Condition that checks a property // and its value. -func OnProperty(name string, options ...PropertyOption) *conditional { +func OnProperty(name string, options ...PropertyOption) *Conditional { return New().OnProperty(name, options...) } // OnProperty adds a Condition that checks a property and its value. -func (c *conditional) OnProperty(name string, options ...PropertyOption) *conditional { +func (c *Conditional) OnProperty(name string, options ...PropertyOption) *Conditional { cond := &onProperty{name: name} for _, option := range options { option(cond) @@ -326,79 +326,79 @@ func (c *conditional) OnProperty(name string, options ...PropertyOption) *condit return c.On(cond) } -// OnMissingProperty returns a conditional that starts with a Condition that returns +// OnMissingProperty returns a Conditional that starts with a Condition that returns // true when property doesn't exist. -func OnMissingProperty(name string) *conditional { +func OnMissingProperty(name string) *Conditional { return New().OnMissingProperty(name) } // OnMissingProperty adds a Condition that returns true when property doesn't exist. -func (c *conditional) OnMissingProperty(name string) *conditional { +func (c *Conditional) OnMissingProperty(name string) *Conditional { return c.On(&onMissingProperty{name: name}) } -// OnBean returns a conditional that starts with a Condition that returns true when +// OnBean returns a Conditional that starts with a Condition that returns true when // finding more than one beans. -func OnBean(selector gs.BeanSelector) *conditional { +func OnBean(selector gs.BeanSelector) *Conditional { return New().OnBean(selector) } // OnBean adds a Condition that returns true when finding more than one beans. -func (c *conditional) OnBean(selector gs.BeanSelector) *conditional { +func (c *Conditional) OnBean(selector gs.BeanSelector) *Conditional { return c.On(&onBean{selector: selector}) } -// OnMissingBean returns a conditional that starts with a Condition that returns +// OnMissingBean returns a Conditional that starts with a Condition that returns // true when finding no beans. -func OnMissingBean(selector gs.BeanSelector) *conditional { +func OnMissingBean(selector gs.BeanSelector) *Conditional { return New().OnMissingBean(selector) } // OnMissingBean adds a Condition that returns true when finding no beans. -func (c *conditional) OnMissingBean(selector gs.BeanSelector) *conditional { +func (c *Conditional) OnMissingBean(selector gs.BeanSelector) *Conditional { return c.On(&onMissingBean{selector: selector}) } -// OnSingleBean returns a conditional that starts with a Condition that returns +// OnSingleBean returns a Conditional that starts with a Condition that returns // true when finding only one bean. -func OnSingleBean(selector gs.BeanSelector) *conditional { +func OnSingleBean(selector gs.BeanSelector) *Conditional { return New().OnSingleBean(selector) } // OnSingleBean adds a Condition that returns true when finding only one bean. -func (c *conditional) OnSingleBean(selector gs.BeanSelector) *conditional { +func (c *Conditional) OnSingleBean(selector gs.BeanSelector) *Conditional { return c.On(&onSingleBean{selector: selector}) } -// OnExpression returns a conditional that starts with a Condition that returns +// OnExpression returns a Conditional that starts with a Condition that returns // true when an expression returns true. -func OnExpression(expression string) *conditional { +func OnExpression(expression string) *Conditional { return New().OnExpression(expression) } // OnExpression adds a Condition that returns true when an expression returns true. -func (c *conditional) OnExpression(expression string) *conditional { +func (c *Conditional) OnExpression(expression string) *Conditional { return c.On(&onExpression{expression: expression}) } -// OnMatches returns a conditional that starts with a Condition that returns true +// OnMatches returns a Conditional that starts with a Condition that returns true // when function returns true. -func OnMatches(fn func(ctx gs.CondContext) (bool, error)) *conditional { +func OnMatches(fn func(ctx gs.CondContext) (bool, error)) *Conditional { return New().OnMatches(fn) } // OnMatches adds a Condition that returns true when function returns true. -func (c *conditional) OnMatches(fn func(ctx gs.CondContext) (bool, error)) *conditional { +func (c *Conditional) OnMatches(fn func(ctx gs.CondContext) (bool, error)) *Conditional { return c.On(FuncCond(fn)) } -// OnProfile returns a conditional that starts with a Condition that returns true +// OnProfile returns a Conditional that starts with a Condition that returns true // when property value equals to profile. -func OnProfile(profile string) *conditional { +func OnProfile(profile string) *Conditional { return New().OnProfile(profile) } // OnProfile adds a Condition that returns true when property value equals to profile. -func (c *conditional) OnProfile(profile string) *conditional { +func (c *Conditional) OnProfile(profile string) *Conditional { return c.OnProperty("spring.profiles.active", HavingValue(profile)) } From 426470d531ea37d4985a808b7214428438106e5e Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 1 Jan 2025 18:49:40 +0800 Subject: [PATCH 21/75] code refactor --- dync/bool.go | 7 +- dync/duration.go | 20 +----- dync/dync.go | 34 ++------- dync/dync_test.go | 144 ++++++++++++++++---------------------- dync/event.go | 66 ----------------- dync/float32.go | 22 +----- dync/float64.go | 22 +----- dync/int32.go | 22 +----- dync/int64.go | 22 +----- dync/string.go | 22 +----- dync/time.go | 20 +----- dync/uint32.go | 22 +----- dync/uint64.go | 20 +----- dync/{ref.go => value.go} | 29 ++------ gs/syslog/syslog.go | 31 ++++++++ 15 files changed, 120 insertions(+), 383 deletions(-) delete mode 100644 dync/event.go rename dync/{ref.go => value.go} (61%) diff --git a/dync/bool.go b/dync/bool.go index ee604a51..d26f1b8f 100644 --- a/dync/bool.go +++ b/dync/bool.go @@ -44,7 +44,7 @@ func (x *Bool) getBool(prop conf.ReadOnlyProperties, param conf.BindParam) (bool return v, nil } -func (x *Bool) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Bool) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getBool(prop, param) if err != nil { return err @@ -53,11 +53,6 @@ func (x *Bool) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error return nil } -func (x *Bool) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - _, err := x.getBool(prop, param) - return err -} - func (x *Bool) MarshalJSON() ([]byte, error) { return json.Marshal(x.Value()) } diff --git a/dync/duration.go b/dync/duration.go index ee686339..764385a8 100644 --- a/dync/duration.go +++ b/dync/duration.go @@ -25,21 +25,14 @@ import ( "go.uber.org/atomic" ) -type DurationValidateFunc func(v time.Duration) error - type Duration struct { v atomic.Duration - f DurationValidateFunc } func (x *Duration) Value() time.Duration { return x.v.Load() } -func (x *Duration) OnValidate(f DurationValidateFunc) { - x.f = f -} - func (x *Duration) getDuration(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Duration, error) { s, err := GetProperty(prop, param) if err != nil { @@ -52,7 +45,7 @@ func (x *Duration) getDuration(prop conf.ReadOnlyProperties, param conf.BindPara return v, nil } -func (x *Duration) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Duration) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getDuration(prop, param) if err != nil { return err @@ -61,17 +54,6 @@ func (x *Duration) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) e return nil } -func (x *Duration) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getDuration(prop, param) - if err != nil { - return err - } - if x.f != nil { - return x.f(v) - } - return nil -} - func (x *Duration) MarshalJSON() ([]byte, error) { return json.Marshal(x.Value()) } diff --git a/dync/dync.go b/dync/dync.go index 29d55bdf..01a6589a 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -27,14 +27,13 @@ import ( "go.uber.org/atomic" ) -// Value 可动态刷新的对象 -type Value interface { - Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error - Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error +// ValueInterface 可动态刷新的对象 +type ValueInterface interface { + OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error } type Field struct { - value Value + value ValueInterface param conf.BindParam } @@ -120,11 +119,6 @@ func (p *Properties) refreshKeys(prop conf.ReadOnlyProperties, keys []string) (e func (p *Properties) refreshFields(prop conf.ReadOnlyProperties, fields []*Field) (err error) { - err = validateFields(prop, fields) - if err != nil { - return - } - old := p.load() defer func() { if r := recover(); err != nil || r != nil { @@ -140,19 +134,9 @@ func (p *Properties) refreshFields(prop conf.ReadOnlyProperties, fields []*Field return refreshFields(p.load(), fields) } -func validateFields(prop conf.ReadOnlyProperties, fields []*Field) error { - for _, f := range fields { - err := f.value.Validate(prop, f.param) - if err != nil { - return err - } - } - return nil -} - func refreshFields(prop conf.ReadOnlyProperties, fields []*Field) error { for _, f := range fields { - err := f.value.Refresh(prop, f.param) + err := f.value.OnRefresh(prop, f.param) if err != nil { return err } @@ -175,17 +159,13 @@ func (p *Properties) BindValue(v reflect.Value, param conf.BindParam) error { func (p *Properties) bindValue(i interface{}, param conf.BindParam) (bool, error) { - v, ok := i.(Value) + v, ok := i.(ValueInterface) if !ok { return false, nil } prop := p.load() - err := v.Validate(prop, param) - if err != nil { - return false, err - } - err = v.Refresh(prop, param) + err := v.OnRefresh(prop, param) if err != nil { return false, err } diff --git a/dync/dync_test.go b/dync/dync_test.go index b7625e27..494fe9b2 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -18,49 +18,27 @@ package dync_test import ( "encoding/json" - "errors" - "fmt" "reflect" "testing" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/spf13/cast" "github.com/stretchr/testify/assert" ) -type Integer struct { - v int -} - -func (x *Integer) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - s, err := dync.GetProperty(prop, param) - if err != nil { - return err - } - v, err := cast.ToInt64E(s) - if err != nil { - return err - } - x.v = int(v) - return nil -} +// todo 自定义类型通过类型转换器实现刷新机制 -func (x *Integer) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - return nil -} - -func (x *Integer) MarshalJSON() ([]byte, error) { - return json.Marshal(x.v) +type Object struct { + Str string `value:"${string:=abc}" expr:"len($)<6"` + Int int `value:"${int:=3}" expr:"$<6"` } type Config struct { - Integer Integer `value:"${int:=3}" expr:"$<6"` - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` - Map dync.Ref[map[string]string] `value:"${map:=}"` - Slice dync.Ref[[]string] `value:"${slice:=}"` - Event dync.Event `value:"${event}"` + Int dync.Int64 `value:"${int:=3}" expr:"$<6"` + Float dync.Float64 `value:"${float:=1.2}"` + Map dync.Value[map[string]string] `value:"${map:=}"` + Slice dync.Value[[]string] `value:"${slice:=}"` + Object dync.Value[Object] `value:"${object:=}"` } func newTest() (*dync.Properties, *Config, error) { @@ -81,7 +59,7 @@ func TestDynamic(t *testing.T) { return } b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Object":{"Str":"abc","Int":3}}`) }) t.Run("init", func(t *testing.T) { @@ -89,52 +67,52 @@ func TestDynamic(t *testing.T) { if err != nil { return } - //cfg.Slice.Init(make([]string, 0)) - //cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) + // cfg.Slice.Init(make([]string, 0)) + // cfg.Map.Init(make(map[string]string)) + // cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { + // fmt.Println("event fired.") + // return nil + // }) b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Object":{"Str":"abc","Int":3}}`) }) - t.Run("default validate error", func(t *testing.T) { - mgr := dync.New() - cfg := new(Config) - cfg.Int.OnValidate(func(v int64) error { - if v < 6 { - return errors.New("should greeter than 6") - } - return nil - }) - err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) - assert.Error(t, err, "should greeter than 6") - }) + // t.Run("default validate error", func(t *testing.T) { + // mgr := dync.New() + // cfg := new(Config) + // // cfg.Int.OnValidate(func(v int64) error { + // // if v < 6 { + // // return errors.New("should greeter than 6") + // // } + // // return nil + // // }) + // err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) + // assert.Error(t, err, "should greeter than 6") + // }) t.Run("init validate error", func(t *testing.T) { mgr := dync.New() cfg := new(Config) - cfg.Int.OnValidate(func(v int64) error { - if v < 3 { - return errors.New("should greeter than 3") - } - return nil - }) - //cfg.Slice.Init(make([]string, 0)) - //cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) + // cfg.Int.OnValidate(func(v int64) error { + // if v < 3 { + // return errors.New("should greeter than 3") + // } + // return nil + // }) + // cfg.Slice.Init(make([]string, 0)) + // cfg.Map.Init(make(map[string]string)) + // cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { + // fmt.Println("event fired.") + // return nil + // }) err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) if err != nil { t.Fatal(err) } b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Object":{"Str":"abc","Int":3}}`) p := conf.New() p.Set("int", 1) @@ -144,35 +122,35 @@ func TestDynamic(t *testing.T) { p.Set("slice[0]", 2) p.Set("slice[1]", 9) err = mgr.Refresh(p) - assert.Error(t, err, "should greeter than 3") + // assert.Error(t, err, "should greeter than 3") b, _ = json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":1,"Float":5.4,"Map":{"a":"3","b":"7"},"Slice":["2","9"],"Object":{"Str":"abc","Int":3}}`) }) t.Run("success", func(t *testing.T) { mgr := dync.New() cfg := new(Config) - cfg.Int.OnValidate(func(v int64) error { - if v < 3 { - return errors.New("should greeter than 3") - } - return nil - }) - //cfg.Slice.Init(make([]string, 0)) - //cfg.Map.Init(make(map[string]string)) - cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) + // cfg.Int.OnValidate(func(v int64) error { + // if v < 3 { + // return errors.New("should greeter than 3") + // } + // return nil + // }) + // cfg.Slice.Init(make([]string, 0)) + // cfg.Map.Init(make(map[string]string)) + // cfg.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { + // fmt.Println("event fired.") + // return nil + // }) err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) if err != nil { t.Fatal(err) } b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Object":{"Str":"abc","Int":3}}`) p := conf.New() p.Set("int", 1) @@ -182,10 +160,10 @@ func TestDynamic(t *testing.T) { p.Set("slice[0]", 2) p.Set("slice[1]", 9) err = mgr.Refresh(p) - assert.Error(t, err, "should greeter than 3") + // assert.Error(t, err, "should greeter than 3") b, _ = json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":1,"Float":5.4,"Map":{"a":"3","b":"7"},"Slice":["2","9"],"Object":{"Str":"abc","Int":3}}`) p = conf.New() p.Set("int", 6) @@ -198,7 +176,7 @@ func TestDynamic(t *testing.T) { assert.Error(t, err, "validate failed on \"\\$<6\" for value 6") b, _ = json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":3,"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":1,"Float":5.4,"Map":{"a":"3","b":"7"},"Slice":["2","9"],"Object":{"Str":"abc","Int":3}}`) p = conf.New() p.Set("int", 4) @@ -210,6 +188,6 @@ func TestDynamic(t *testing.T) { mgr.Refresh(p) b, _ = json.Marshal(cfg) - assert.Equal(t, string(b), `{"Integer":4,"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Object":{"Str":"abc","Int":3}}`) }) } diff --git a/dync/event.go b/dync/event.go deleted file mode 100644 index 09bb758c..00000000 --- a/dync/event.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" -) - -type EventFunc func(prop conf.ReadOnlyProperties, param conf.BindParam) error -type EventValidateFunc func(prop conf.ReadOnlyProperties, param conf.BindParam) error - -type Event struct { - f EventFunc - h EventValidateFunc - init func() (conf.ReadOnlyProperties, conf.BindParam) -} - -func (e *Event) OnValidate(h EventValidateFunc) { - e.h = h -} - -func (e *Event) OnEvent(f EventFunc) error { - if e.init == nil { - return nil - } - prop, param := e.init() - e.init = nil - return e.Refresh(prop, param) -} - -func (e *Event) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - if e.f == nil { - e.init = func() (conf.ReadOnlyProperties, conf.BindParam) { - return prop, param - } - return nil - } - return e.f(prop, param) -} - -func (e *Event) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - if e.h != nil { - return e.h(prop, param) - } - return nil -} - -func (e *Event) MarshalJSON() ([]byte, error) { - return json.Marshal(make(map[string]string)) -} diff --git a/dync/float32.go b/dync/float32.go index 3a8bc7c5..502123ea 100644 --- a/dync/float32.go +++ b/dync/float32.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Float32ValidateFunc func(v float32) error - type Float32 struct { v atomic.Float32 - f Float32ValidateFunc } func (x *Float32) Value() float32 { return x.v.Load() } -func (x *Float32) OnValidate(f Float32ValidateFunc) { - x.f = f -} - func (x *Float32) getFloat32(prop conf.ReadOnlyProperties, param conf.BindParam) (float32, error) { s, err := GetProperty(prop, param) if err != nil { @@ -51,16 +44,7 @@ func (x *Float32) getFloat32(prop conf.ReadOnlyProperties, param conf.BindParam) return float32(v), nil } -func (x *Float32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getFloat32(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Float32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Float32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat32(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Float32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) e if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/float64.go b/dync/float64.go index cb882c45..01d07fd7 100644 --- a/dync/float64.go +++ b/dync/float64.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Float64ValidateFunc func(v float64) error - type Float64 struct { v atomic.Float64 - f Float64ValidateFunc } func (x *Float64) Value() float64 { return x.v.Load() } -func (x *Float64) OnValidate(f Float64ValidateFunc) { - x.f = f -} - func (x *Float64) getFloat64(prop conf.ReadOnlyProperties, param conf.BindParam) (float64, error) { s, err := GetProperty(prop, param) if err != nil { @@ -51,16 +44,7 @@ func (x *Float64) getFloat64(prop conf.ReadOnlyProperties, param conf.BindParam) return v, nil } -func (x *Float64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getFloat64(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Float64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Float64) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getFloat64(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Float64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) e if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/int32.go b/dync/int32.go index 62b5801e..78ef6f48 100644 --- a/dync/int32.go +++ b/dync/int32.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Int32ValidateFunc func(v int32) error - type Int32 struct { v atomic.Int32 - f Int32ValidateFunc } func (x *Int32) Value() int32 { return x.v.Load() } -func (x *Int32) OnValidate(f Int32ValidateFunc) { - x.f = f -} - func (x *Int32) getInt32(prop conf.ReadOnlyProperties, param conf.BindParam) (int32, error) { s, err := GetProperty(prop, param) if err != nil { @@ -51,16 +44,7 @@ func (x *Int32) getInt32(prop conf.ReadOnlyProperties, param conf.BindParam) (in return int32(v), nil } -func (x *Int32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getInt32(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Int32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Int32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt32(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Int32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) err if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/int64.go b/dync/int64.go index 43286be4..1017121b 100644 --- a/dync/int64.go +++ b/dync/int64.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Int64ValidateFunc func(v int64) error - type Int64 struct { v atomic.Int64 - f Int64ValidateFunc } func (x *Int64) Value() int64 { return x.v.Load() } -func (x *Int64) OnValidate(f Int64ValidateFunc) { - x.f = f -} - func (x *Int64) getInt64(prop conf.ReadOnlyProperties, param conf.BindParam) (int64, error) { s, err := GetProperty(prop, param) if err != nil { @@ -51,16 +44,7 @@ func (x *Int64) getInt64(prop conf.ReadOnlyProperties, param conf.BindParam) (in return v, nil } -func (x *Int64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getInt64(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Int64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Int64) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getInt64(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Int64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) err if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/string.go b/dync/string.go index e6b6bbaa..4098966e 100644 --- a/dync/string.go +++ b/dync/string.go @@ -23,35 +23,19 @@ import ( "go.uber.org/atomic" ) -type StringValidateFunc func(v string) error - type String struct { v atomic.String - f StringValidateFunc } func (x *String) Value() string { return x.v.Load() } -func (x *String) OnValidate(f StringValidateFunc) { - x.f = f -} - func (x *String) getString(prop conf.ReadOnlyProperties, param conf.BindParam) (string, error) { return GetProperty(prop, param) } -func (x *String) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getString(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *String) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *String) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getString(prop, param) if err != nil { return err @@ -60,9 +44,7 @@ func (x *String) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) er if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/time.go b/dync/time.go index 1a0cce7c..c05f89a5 100644 --- a/dync/time.go +++ b/dync/time.go @@ -25,21 +25,14 @@ import ( "go.uber.org/atomic" ) -type TimeValidateFunc func(v time.Time) error - type Time struct { v atomic.Time - f TimeValidateFunc } func (x *Time) Value() time.Time { return x.v.Load() } -func (x *Time) OnValidate(f TimeValidateFunc) { - x.f = f -} - func (x *Time) getTime(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Time, error) { s, err := GetProperty(prop, param) if err != nil { @@ -52,7 +45,7 @@ func (x *Time) getTime(prop conf.ReadOnlyProperties, param conf.BindParam) (time return v, nil } -func (x *Time) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Time) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getTime(prop, param) if err != nil { return err @@ -61,17 +54,6 @@ func (x *Time) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error return nil } -func (x *Time) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getTime(prop, param) - if err != nil { - return err - } - if x.f != nil { - return x.f(v) - } - return nil -} - func (x *Time) MarshalJSON() ([]byte, error) { return json.Marshal(x.Value()) } diff --git a/dync/uint32.go b/dync/uint32.go index a48ee5c2..f64bb434 100644 --- a/dync/uint32.go +++ b/dync/uint32.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Uint32ValidateFunc func(v uint32) error - type Uint32 struct { v atomic.Uint32 - f Uint32ValidateFunc } func (x *Uint32) Value() uint32 { return x.v.Load() } -func (x *Uint32) OnValidate(f Uint32ValidateFunc) { - x.f = f -} - func (x *Uint32) getUint32(prop conf.ReadOnlyProperties, param conf.BindParam) (uint32, error) { s, err := GetProperty(prop, param) if err != nil { @@ -51,16 +44,7 @@ func (x *Uint32) getUint32(prop conf.ReadOnlyProperties, param conf.BindParam) ( return uint32(v), nil } -func (x *Uint32) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getUint32(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Uint32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (x *Uint32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint32(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Uint32) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) er if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/uint64.go b/dync/uint64.go index bae97a64..bc416704 100644 --- a/dync/uint64.go +++ b/dync/uint64.go @@ -24,21 +24,14 @@ import ( "go.uber.org/atomic" ) -type Uint64ValidateFunc func(v uint64) error - type Uint64 struct { v atomic.Uint64 - f Uint64ValidateFunc } func (x *Uint64) Value() uint64 { return x.v.Load() } -func (x *Uint64) OnValidate(f Uint64ValidateFunc) { - x.f = f -} - func (x *Uint64) getUint64(prop conf.ReadOnlyProperties, param conf.BindParam) (uint64, error) { s, err := GetProperty(prop, param) if err != nil { @@ -52,15 +45,6 @@ func (x *Uint64) getUint64(prop conf.ReadOnlyProperties, param conf.BindParam) ( } func (x *Uint64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getUint64(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Uint64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { v, err := x.getUint64(prop, param) if err != nil { return err @@ -69,9 +53,7 @@ func (x *Uint64) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) er if err != nil { return err } - if x.f != nil { - return x.f(v) - } + x.v.Store(v) return nil } diff --git a/dync/ref.go b/dync/value.go similarity index 61% rename from dync/ref.go rename to dync/value.go index 17f00c62..8828ccc2 100644 --- a/dync/ref.go +++ b/dync/value.go @@ -24,26 +24,19 @@ import ( "go.uber.org/atomic" ) -type RefValidateFunc func(v interface{}) error - -type Ref[T interface{}] struct { +type Value[T interface{}] struct { v atomic.Value - f RefValidateFunc -} - -func (r *Ref[T]) OnValidate(f RefValidateFunc) { - r.f = f } -func bindRef[T any](o T, prop conf.ReadOnlyProperties, param conf.BindParam) error { +func bindValue[T any](o T, prop conf.ReadOnlyProperties, param conf.BindParam) error { t := reflect.TypeOf(o).Elem() v := reflect.ValueOf(o).Elem() return conf.BindValue(prop, v, t, param, nil) } -func (r *Ref[T]) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { var o T - err := bindRef(&o, prop, param) + err := bindValue(&o, prop, param) if err != nil { return err } @@ -51,18 +44,6 @@ func (r *Ref[T]) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) err return nil } -func (r *Ref[T]) Validate(prop conf.ReadOnlyProperties, param conf.BindParam) error { - var o T - err := bindRef(&o, prop, param) - if err != nil { - return err - } - if r.f != nil { - return r.f(o) - } - return err -} - -func (r *Ref[T]) MarshalJSON() ([]byte, error) { +func (r *Value[T]) MarshalJSON() ([]byte, error) { return json.Marshal(r.v.Load()) } diff --git a/gs/syslog/syslog.go b/gs/syslog/syslog.go index 9247bbff..252bb182 100644 --- a/gs/syslog/syslog.go +++ b/gs/syslog/syslog.go @@ -15,3 +15,34 @@ */ package syslog + +import ( + "context" + "log/slog" + "os" +) + +func init() { + slog.Default().Enabled(context.Background(), slog.LevelInfo) + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) +} + +// Debug calls [Logger.Debug] on the default logger. +func Debug(msg string, args ...any) { + slog.Default().Log(context.Background(), slog.LevelDebug, msg, args...) +} + +// Info calls [Logger.Info] on the default logger. +func Info(msg string, args ...any) { + slog.Default().Log(context.Background(), slog.LevelInfo, msg, args...) +} + +// Warn calls [Logger.Warn] on the default logger. +func Warn(msg string, args ...any) { + slog.Default().Log(context.Background(), slog.LevelWarn, msg, args...) +} + +// Error calls [Logger.Error] on the default logger. +func Error(msg string, args ...any) { + slog.Default().Log(context.Background(), slog.LevelError, msg, args...) +} From 8d6c6d10a36279e1b067cad10475722dd49733b7 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 1 Jan 2025 20:05:40 +0800 Subject: [PATCH 22/75] code refactor --- dync/bool.go | 58 ------------------------------- dync/duration.go | 59 -------------------------------- dync/dync.go | 47 ++++++++++++-------------- dync/dync_test.go | 8 ++--- dync/float32.go | 62 ---------------------------------- dync/float64.go | 62 ---------------------------------- dync/int32.go | 62 ---------------------------------- dync/int64.go | 62 ---------------------------------- dync/string.go | 53 ----------------------------- dync/time.go | 59 -------------------------------- dync/uint32.go | 62 ---------------------------------- dync/uint64.go | 62 ---------------------------------- dync/value.go | 49 --------------------------- gs/internal/gs_ctx/ctx_test.go | 53 ++++++++++++++--------------- 14 files changed, 51 insertions(+), 707 deletions(-) delete mode 100644 dync/bool.go delete mode 100644 dync/duration.go delete mode 100644 dync/float32.go delete mode 100644 dync/float64.go delete mode 100644 dync/int32.go delete mode 100644 dync/int64.go delete mode 100644 dync/string.go delete mode 100644 dync/time.go delete mode 100644 dync/uint32.go delete mode 100644 dync/uint64.go delete mode 100644 dync/value.go diff --git a/dync/bool.go b/dync/bool.go deleted file mode 100644 index d26f1b8f..00000000 --- a/dync/bool.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Bool struct { - v atomic.Bool -} - -func (x *Bool) Value() bool { - return x.v.Load() -} - -func (x *Bool) getBool(prop conf.ReadOnlyProperties, param conf.BindParam) (bool, error) { - s, err := GetProperty(prop, param) - if err != nil { - return false, err - } - v, err := cast.ToBoolE(s) - if err != nil { - return false, err - } - return v, nil -} - -func (x *Bool) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getBool(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Bool) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/duration.go b/dync/duration.go deleted file mode 100644 index 764385a8..00000000 --- a/dync/duration.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - "time" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Duration struct { - v atomic.Duration -} - -func (x *Duration) Value() time.Duration { - return x.v.Load() -} - -func (x *Duration) getDuration(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Duration, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToDurationE(s) - if err != nil { - return 0, err - } - return v, nil -} - -func (x *Duration) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getDuration(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Duration) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/dync.go b/dync/dync.go index 01a6589a..9c77c0d7 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -17,13 +17,13 @@ package dync import ( + "encoding/json" "fmt" "reflect" "sort" "strings" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/expr" "go.uber.org/atomic" ) @@ -32,6 +32,26 @@ type ValueInterface interface { OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error } +// Value 可动态刷新的对象 +type Value[T interface{}] struct { + v atomic.Value +} + +func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { + var o T + v := reflect.ValueOf(&o).Elem() + err := conf.BindValue(prop, v, v.Type(), param, nil) + if err != nil { + return err + } + r.v.Store(o) + return nil +} + +func (r *Value[T]) MarshalJSON() ([]byte, error) { + return json.Marshal(r.v.Load()) +} + type Field struct { value ValueInterface param conf.BindParam @@ -176,28 +196,3 @@ func (p *Properties) bindValue(i interface{}, param conf.BindParam) (bool, error }) return true, nil } - -func GetProperty(prop conf.ReadOnlyProperties, param conf.BindParam) (string, error) { - key := param.Key - if !prop.Has(key) && !param.Tag.HasDef { - return "", fmt.Errorf("property %q not exist", key) - } - s := prop.Get(key, conf.Def(param.Tag.Def)) - return s, nil -} - -func Validate(val interface{}, param conf.BindParam) error { - if param.Validate == "" { - return nil - } - tag, ok := param.Validate.Lookup("expr") - if !ok { - return nil - } - if b, err := expr.Eval(tag, val); err != nil { - return err - } else if !b { - return fmt.Errorf("validate failed on %q for value %v", param.Validate, val) - } - return nil -} diff --git a/dync/dync_test.go b/dync/dync_test.go index 494fe9b2..e279ff17 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -28,17 +28,17 @@ import ( // todo 自定义类型通过类型转换器实现刷新机制 -type Object struct { +type StructValue struct { Str string `value:"${string:=abc}" expr:"len($)<6"` Int int `value:"${int:=3}" expr:"$<6"` } type Config struct { - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` + Int dync.Value[int64] `value:"${int:=3}" expr:"$<6"` + Float dync.Value[float64] `value:"${float:=1.2}"` Map dync.Value[map[string]string] `value:"${map:=}"` Slice dync.Value[[]string] `value:"${slice:=}"` - Object dync.Value[Object] `value:"${object:=}"` + Object dync.Value[StructValue] `value:"${object:=}"` } func newTest() (*dync.Properties, *Config, error) { diff --git a/dync/float32.go b/dync/float32.go deleted file mode 100644 index 502123ea..00000000 --- a/dync/float32.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Float32 struct { - v atomic.Float32 -} - -func (x *Float32) Value() float32 { - return x.v.Load() -} - -func (x *Float32) getFloat32(prop conf.ReadOnlyProperties, param conf.BindParam) (float32, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToFloat64E(s) - if err != nil { - return 0, err - } - return float32(v), nil -} - -func (x *Float32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getFloat32(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Float32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/float64.go b/dync/float64.go deleted file mode 100644 index 01d07fd7..00000000 --- a/dync/float64.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Float64 struct { - v atomic.Float64 -} - -func (x *Float64) Value() float64 { - return x.v.Load() -} - -func (x *Float64) getFloat64(prop conf.ReadOnlyProperties, param conf.BindParam) (float64, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToFloat64E(s) - if err != nil { - return 0, err - } - return v, nil -} - -func (x *Float64) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getFloat64(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Float64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/int32.go b/dync/int32.go deleted file mode 100644 index 78ef6f48..00000000 --- a/dync/int32.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Int32 struct { - v atomic.Int32 -} - -func (x *Int32) Value() int32 { - return x.v.Load() -} - -func (x *Int32) getInt32(prop conf.ReadOnlyProperties, param conf.BindParam) (int32, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToInt64E(s) - if err != nil { - return 0, err - } - return int32(v), nil -} - -func (x *Int32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getInt32(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Int32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/int64.go b/dync/int64.go deleted file mode 100644 index 1017121b..00000000 --- a/dync/int64.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Int64 struct { - v atomic.Int64 -} - -func (x *Int64) Value() int64 { - return x.v.Load() -} - -func (x *Int64) getInt64(prop conf.ReadOnlyProperties, param conf.BindParam) (int64, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToInt64E(s) - if err != nil { - return 0, err - } - return v, nil -} - -func (x *Int64) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getInt64(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Int64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/string.go b/dync/string.go deleted file mode 100644 index 4098966e..00000000 --- a/dync/string.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "go.uber.org/atomic" -) - -type String struct { - v atomic.String -} - -func (x *String) Value() string { - return x.v.Load() -} - -func (x *String) getString(prop conf.ReadOnlyProperties, param conf.BindParam) (string, error) { - return GetProperty(prop, param) -} - -func (x *String) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getString(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *String) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/time.go b/dync/time.go deleted file mode 100644 index c05f89a5..00000000 --- a/dync/time.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - "time" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Time struct { - v atomic.Time -} - -func (x *Time) Value() time.Time { - return x.v.Load() -} - -func (x *Time) getTime(prop conf.ReadOnlyProperties, param conf.BindParam) (time.Time, error) { - s, err := GetProperty(prop, param) - if err != nil { - return time.Time{}, err - } - v, err := cast.ToTimeE(s) - if err != nil { - return time.Time{}, err - } - return v, nil -} - -func (x *Time) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getTime(prop, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Time) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/uint32.go b/dync/uint32.go deleted file mode 100644 index f64bb434..00000000 --- a/dync/uint32.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Uint32 struct { - v atomic.Uint32 -} - -func (x *Uint32) Value() uint32 { - return x.v.Load() -} - -func (x *Uint32) getUint32(prop conf.ReadOnlyProperties, param conf.BindParam) (uint32, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToUint64E(s) - if err != nil { - return 0, err - } - return uint32(v), nil -} - -func (x *Uint32) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getUint32(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Uint32) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/uint64.go b/dync/uint64.go deleted file mode 100644 index bc416704..00000000 --- a/dync/uint64.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - - "github.com/go-spring/spring-core/conf" - "github.com/spf13/cast" - "go.uber.org/atomic" -) - -type Uint64 struct { - v atomic.Uint64 -} - -func (x *Uint64) Value() uint64 { - return x.v.Load() -} - -func (x *Uint64) getUint64(prop conf.ReadOnlyProperties, param conf.BindParam) (uint64, error) { - s, err := GetProperty(prop, param) - if err != nil { - return 0, err - } - v, err := cast.ToUint64E(s) - if err != nil { - return 0, err - } - return v, nil -} - -func (x *Uint64) Refresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - v, err := x.getUint64(prop, param) - if err != nil { - return err - } - err = Validate(v, param) - if err != nil { - return err - } - x.v.Store(v) - return nil -} - -func (x *Uint64) MarshalJSON() ([]byte, error) { - return json.Marshal(x.Value()) -} diff --git a/dync/value.go b/dync/value.go deleted file mode 100644 index 8828ccc2..00000000 --- a/dync/value.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dync - -import ( - "encoding/json" - "reflect" - - "github.com/go-spring/spring-core/conf" - "go.uber.org/atomic" -) - -type Value[T interface{}] struct { - v atomic.Value -} - -func bindValue[T any](o T, prop conf.ReadOnlyProperties, param conf.BindParam) error { - t := reflect.TypeOf(o).Elem() - v := reflect.ValueOf(o).Elem() - return conf.BindValue(prop, v, t, param, nil) -} - -func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { - var o T - err := bindValue(&o, prop, param) - if err != nil { - return err - } - r.v.Store(o) - return nil -} - -func (r *Value[T]) MarshalJSON() ([]byte, error) { - return json.Marshal(r.v.Load()) -} diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index 533a25b7..372e45c9 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -2903,11 +2903,10 @@ func TestContextAware(t *testing.T) { } type DynamicConfig struct { - Int dync.Int64 `value:"${int:=3}" expr:"$<6"` - Float dync.Float64 `value:"${float:=1.2}"` - Map dync.Ref[map[string]string] `value:"${map:=}"` - Slice dync.Ref[[]string] `value:"${slice:=}"` - Event dync.Event `value:"${event}"` + Int dync.Value[int64] `value:"${int:=3}" expr:"$<6"` + Float dync.Value[float64] `value:"${float:=1.2}"` + Map dync.Value[map[string]string] `value:"${map:=}"` + Slice dync.Value[[]string] `value:"${slice:=}"` } type DynamicConfigWrapper struct { @@ -2922,18 +2921,18 @@ func TestDynamic(t *testing.T) { c := gs_ctx.New() c.Provide(func() *DynamicConfig { config := new(DynamicConfig) - config.Int.OnValidate(func(v int64) error { - if v < 3 { - return errors.New("should greeter than 3") - } - return nil - }) + // config.Int.OnValidate(func(v int64) error { + // if v < 3 { + // return errors.New("should greeter than 3") + // } + // return nil + // }) // config.Slice.Init(make([]string, 0)) // config.Map.Init(make(map[string]string)) - config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) + // config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { + // fmt.Println("event fired.") + // return nil + // }) return config }).Init(func(config *DynamicConfig) { cfg = config @@ -2941,19 +2940,19 @@ func TestDynamic(t *testing.T) { c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { // p.Wrapper.Slice.Init(make([]string, 0)) // p.Wrapper.Map.Init(make(map[string]string)) - p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - fmt.Println("event fired.") - return nil - }) + // p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { + // fmt.Println("event fired.") + // return nil + // }) }) err := c.Refresh() assert.Nil(t, err) { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[],"Event":{}}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[]}}`) } { @@ -2975,9 +2974,9 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) } { @@ -3000,9 +2999,9 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) } { @@ -3025,9 +3024,9 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Event":{}}`) + assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"],"Event":{}}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) } } From d187a89212c594f40c5be3274785a0c9435849a3 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 2 Jan 2025 08:47:16 +0800 Subject: [PATCH 23/75] code refactor --- gs/gs.go | 98 +++++++++++++++++++++++++++++++++--- gs/internal/gs_app/app.go | 24 --------- gs/internal/gs_app/banner.go | 60 ---------------------- gs/internal/gs_app/boot.go | 4 +- 4 files changed, 94 insertions(+), 92 deletions(-) delete mode 100644 gs/internal/gs_app/banner.go diff --git a/gs/gs.go b/gs/gs.go index e6cc50ec..5fbd8935 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -17,24 +17,62 @@ package gs import ( + "fmt" "reflect" + "strings" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) +const ( + Version = "go-spring@v1.1.3" + Website = "https://go-spring.com/" +) + type ( + BeanSelector = gs.BeanSelector + Condition = gs.Condition + CondContext = gs.CondContext Arg = gs.Arg Context = gs.Context - BeanSelector = gs.BeanSelector GroupFunc = gs.GroupFunc ) +/************************************ boot ***********************************/ + +var boot *gs_app.Boot + +func runBoot() error { + if boot != nil { + if err := boot.Run(); err != nil { + return err + } + boot = nil // Boot 阶段结束,释放资源 + } + return nil +} + +// Boot 参考 App.Boot 的解释。 +func Boot() *gs_app.Boot { + if boot == nil { + boot = gs_app.NewBoot() + } + return boot +} + +/*********************************** app *************************************/ + var app = gs_app.NewApp() // Start 启动程序。 func Start() (gs.Context, error) { + printBanner() + err := runBoot() + if err != nil { + return nil, err + } return app.Start() } @@ -45,6 +83,11 @@ func Stop() { // Run 启动程序。 func Run() error { + printBanner() + err := runBoot() + if err != nil { + return err + } return app.Run() } @@ -53,11 +96,6 @@ func ShutDown(msg ...string) { app.ShutDown(msg...) } -// Boot 参考 App.Boot 的解释。 -func Boot() *gs_app.Boot { - return app.Boot() -} - // Object 参考 Container.Object 的解释。 func Object(i interface{}) *gs.BeanDefinition { return app.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) @@ -76,3 +114,51 @@ func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { func Group(fn GroupFunc) { app.Group(fn) } + +/********************************** banner ***********************************/ + +var appBanner = ` + (_) + __ _ ___ ___ _ __ _ __ _ _ __ __ _ + / _' | / _ \ ______ / __| | '_ \ | '__| | | | '_ \ / _' | +| (_| | | (_) | |______| \__ \ | |_) | | | | | | | | | | (_| | + \__, | \___/ |___/ | .__/ |_| |_| |_| |_| \__, | + __/ | | | __/ | + |___/ |_| |___/ +` + +// Banner 自定义 banner 字符串。 +func Banner(banner string) { + appBanner = banner +} + +func printBanner() { + if len(appBanner) == 0 { + return + } + + if appBanner[0] != '\n' { + fmt.Println() + } + + maxLength := 0 + for _, s := range strings.Split(appBanner, "\n") { + fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN + if len(s) > maxLength { + maxLength = len(s) + } + } + + if appBanner[len(appBanner)-1] != '\n' { + fmt.Println() + } + + var padding []byte + if n := (maxLength - len(Version)) / 2; n > 0 { + padding = make([]byte, n) + for i := range padding { + padding[i] = ' ' + } + } + fmt.Println(string(padding) + Version + "\n") +} diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 52f9d344..89fb17c9 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -29,11 +29,6 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_ctx" ) -const ( - Version = "go-spring@v1.1.3" - Website = "https://go-spring.com/" -) - type AppRunner interface { Run(ctx gs.Context) } @@ -47,7 +42,6 @@ type AppServer interface { type App struct { banner string - b *Boot c *gs_ctx.Container p *gs_conf.Configuration @@ -59,7 +53,6 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { app := &App{ - banner: DefaultBanner, c: gs_ctx.New(), p: gs_conf.NewConfiguration(), exitChan: make(chan struct{}), @@ -85,15 +78,6 @@ func (app *App) Run() error { func (app *App) Start() (gs.Context, error) { - app.showBanner() - - if app.b != nil { - err := app.b.run() - if err != nil { - return nil, err - } - } - p, err := app.p.Refresh() if err != nil { return nil, err @@ -150,14 +134,6 @@ func (app *App) ShutDown(msg ...string) { } } -// Boot 返回 *bootstrap 对象。 -func (app *App) Boot() *Boot { - if app.b == nil { - app.b = newBoot() - } - return app.b -} - // Object 参考 Container.Object 的解释。 func (app *App) Object(i interface{}) *gs.BeanDefinition { return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) diff --git a/gs/internal/gs_app/banner.go b/gs/internal/gs_app/banner.go deleted file mode 100644 index 430edb95..00000000 --- a/gs/internal/gs_app/banner.go +++ /dev/null @@ -1,60 +0,0 @@ -package gs_app - -import ( - "fmt" - "strings" -) - -// SpringBannerVisible 是否显示 banner。 -const SpringBannerVisible = "spring.banner.visible" - -const DefaultBanner = ` - (_) - __ _ ___ ___ _ __ _ __ _ _ __ __ _ - / _' | / _ \ ______ / __| | '_ \ | '__| | | | '_ \ / _' | -| (_| | | (_) | |______| \__ \ | |_) | | | | | | | | | | (_| | - \__, | \___/ |___/ | .__/ |_| |_| |_| |_| \__, | - __/ | | | __/ | - |___/ |_| |___/ -` - -// Banner 自定义 banner 字符串。 -func (app *App) Banner(banner string) { - app.banner = banner -} - -func (app *App) showBanner() { - // showBanner, _ := strconv.ParseBool(e.p.Get(SpringBannerVisible)) - // if showBanner { - // app.printBanner(app.banner) - // } -} - -// printBanner 打印 banner 到控制台 -func (app *App) printBanner(banner string) { - - if banner[0] != '\n' { - fmt.Println() - } - - maxLength := 0 - for _, s := range strings.Split(banner, "\n") { - fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN - if len(s) > maxLength { - maxLength = len(s) - } - } - - if banner[len(banner)-1] != '\n' { - fmt.Println() - } - - var padding []byte - if n := (maxLength - len(Version)) / 2; n > 0 { - padding = make([]byte, n) - for i := range padding { - padding[i] = ' ' - } - } - fmt.Println(string(padding) + Version + "\n") -} diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 5c0b928e..6bfffc26 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -33,7 +33,7 @@ type Boot struct { p *gs_conf.Bootstrap } -func newBoot() *Boot { +func NewBoot() *Boot { b := &Boot{ c: gs_ctx.New(), p: gs_conf.NewBootstrap(), @@ -61,7 +61,7 @@ func (b *Boot) Group(fn gs.GroupFunc) { b.c.Group(fn) } -func (b *Boot) run() error { +func (b *Boot) Run() error { p, err := b.p.Refresh() if err != nil { From 362f6ead1f395f89c990e82414da0badeb805eec Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 2 Jan 2025 13:46:06 +0800 Subject: [PATCH 24/75] code refactor --- gs/internal/gs/bean.go | 162 +++++++++--------- gs/internal/gs/bean_test.go | 3 +- gs/internal/gs/{core.go => gs.go} | 30 +++- gs/internal/{gs_util/type.go => gs/util.go} | 10 +- gs/internal/gs_app/app.go | 40 ++--- gs/internal/gs_arg/arg.go | 7 +- gs/internal/gs_ctx/ctx.go | 81 +++++---- .../{gs_util/value.go => gs_ctx/util.go} | 31 +--- 8 files changed, 174 insertions(+), 190 deletions(-) rename gs/internal/gs/{core.go => gs.go} (78%) rename gs/internal/{gs_util/type.go => gs/util.go} (93%) rename gs/internal/{gs_util/value.go => gs_ctx/util.go} (59%) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 1480203a..b1852d60 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" "reflect" - - "github.com/go-spring/spring-core/gs/internal/gs_util" ) type BeanStatus int8 @@ -20,82 +18,83 @@ const ( Wired // 注入完成 ) -func GetStatusString(status BeanStatus) string { - switch status { - case Deleted: - return "Deleted" - case Default: - return "Default" - case Resolving: - return "Resolving" - case Resolved: - return "Resolved" - case Creating: - return "Creating" - case Created: - return "Created" - case Wired: - return "Wired" - default: - return "" - } +func (d *BeanDefinition) GetName() string { + return d.name +} + +func (d *BeanDefinition) GetTypeName() string { + return d.typeName +} + +func (d *BeanDefinition) GetStatus() BeanStatus { + return d.status +} + +func (d *BeanDefinition) SetStatus(status BeanStatus) { + d.status = status +} + +func (d *BeanDefinition) IsPrimary() bool { + return d.primary +} + +func (d *BeanDefinition) IsMethod() bool { + return d.method +} + +func (d *BeanDefinition) GetCond() Condition { + return d.cond +} + +func (d *BeanDefinition) GetOrder() float32 { + return d.order } -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - V reflect.Value // 值 - T reflect.Type // 类型 - F Callable // 构造函数 - - file string // 注册点所在文件 - line int // 注册点所在行数 - - name string // 名称 - typeName string // 原始类型的全限定名 - status BeanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 - - configuration bool // 是否扫描成员方法 - includeMethod []string // 包含哪些成员方法 - excludeMethod []string // 排除那些成员方法 -} - -func (d *BeanDefinition) GetName() string { return d.name } -func (d *BeanDefinition) GetTypeName() string { return d.typeName } -func (d *BeanDefinition) GetStatus() BeanStatus { return d.status } -func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } -func (d *BeanDefinition) IsPrimary() bool { return d.primary } -func (d *BeanDefinition) IsMethod() bool { return d.method } -func (d *BeanDefinition) GetCond() Condition { return d.cond } -func (d *BeanDefinition) GetOrder() float32 { return d.order } -func (d *BeanDefinition) GetInit() interface{} { return d.init } -func (d *BeanDefinition) GetDestroy() interface{} { return d.destroy } -func (d *BeanDefinition) GetDepends() []BeanSelector { return d.depends } -func (d *BeanDefinition) GetExports() []reflect.Type { return d.exports } -func (d *BeanDefinition) IsConfiguration() bool { return d.configuration } -func (d *BeanDefinition) GetIncludeMethod() []string { return d.includeMethod } -func (d *BeanDefinition) GetExcludeMethod() []string { return d.excludeMethod } +func (d *BeanDefinition) GetInit() interface{} { + return d.init +} + +func (d *BeanDefinition) GetDestroy() interface{} { + return d.destroy +} + +func (d *BeanDefinition) GetDepends() []BeanSelector { + return d.depends +} + +func (d *BeanDefinition) GetExports() []reflect.Type { + return d.exports +} + +func (d *BeanDefinition) IsConfiguration() bool { + return d.configuration +} + +func (d *BeanDefinition) GetIncludeMethod() []string { + return d.includeMethod +} + +func (d *BeanDefinition) GetExcludeMethod() []string { + return d.excludeMethod +} + +func (d *BeanDefinition) Callable() Callable { + return d.f +} // Type 返回 bean 的类型。 func (d *BeanDefinition) Type() reflect.Type { - return d.T + return d.t } // Value 返回 bean 的值。 func (d *BeanDefinition) Value() reflect.Value { - return d.V + return d.v } // Interface 返回 bean 的真实值。 func (d *BeanDefinition) Interface() interface{} { - return d.V.Interface() + return d.v.Interface() } // ID 返回 bean 的 ID 。 @@ -138,7 +137,7 @@ func (d *BeanDefinition) FileLine() string { // GetClass 返回 bean 的类型描述。 func (d *BeanDefinition) GetClass() string { - if d.F == nil { + if d.f == nil { return "object bean" } return "constructor bean" @@ -197,13 +196,13 @@ func (d *BeanDefinition) Primary() *BeanDefinition { // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !gs_util.IsFuncType(fnType) { + if !IsFuncType(fnType) { return false } - if fnType.NumIn() != 1 || !gs_util.HasReceiver(fnType, beanValue) { + if fnType.NumIn() != 1 || !HasReceiver(fnType, beanValue) { return false } - return gs_util.ReturnNothing(fnType) || gs_util.ReturnOnlyError(fnType) + return ReturnNothing(fnType) || ReturnOnlyError(fnType) } // Init 设置 bean 的初始化函数。 @@ -235,18 +234,19 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { func (d *BeanDefinition) export(exports ...interface{}) error { for _, o := range exports { - var typ reflect.Type - if t, ok := o.(reflect.Type); ok { - typ = t - } else { // 处理 (*error)(nil) 这种导出形式 - typ = gs_util.Indirect(reflect.TypeOf(o)) + t, ok := o.(reflect.Type) + if !ok { + t = reflect.TypeOf(o) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } } - if typ.Kind() != reflect.Interface { + if t.Kind() != reflect.Interface { return errors.New("only interface type can be exported") } exported := false for _, export := range d.exports { - if typ == export { + if t == export { exported = true break } @@ -254,7 +254,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { if exported { continue } - d.exports = append(d.exports, typ) + d.exports = append(d.exports, t) } return nil } @@ -270,11 +270,11 @@ func (d *BeanDefinition) Configuration(includes []string, excludes []string) *Be // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ - T: t, - V: v, - F: f, + t: t, + v: v, + f: f, name: name, - typeName: gs_util.TypeName(t), + typeName: TypeName(t), status: Default, method: method, file: file, diff --git a/gs/internal/gs/bean_test.go b/gs/internal/gs/bean_test.go index 9ac29a54..e921c0d0 100644 --- a/gs/internal/gs/bean_test.go +++ b/gs/internal/gs/bean_test.go @@ -25,7 +25,6 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_ctx" - "github.com/go-spring/spring-core/gs/internal/gs_util" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/stretchr/testify/assert" @@ -100,7 +99,7 @@ func TestIsFuncBeanType(t *testing.T) { } for k, v := range data { - ok := gs_util.IsConstructor(k) + ok := gs.IsConstructor(k) assert.Equal(t, ok, v) } } diff --git a/gs/internal/gs/core.go b/gs/internal/gs/gs.go similarity index 78% rename from gs/internal/gs/core.go rename to gs/internal/gs/gs.go index cb427d46..a6f450be 100644 --- a/gs/internal/gs/core.go +++ b/gs/internal/gs/gs.go @@ -11,6 +11,32 @@ import ( // `(*error)(nil)`, or a BeanDefinition value. type BeanSelector interface{} +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + v reflect.Value // 值 + t reflect.Type // 类型 + f Callable // 构造函数 + + file string // 注册点所在文件 + line int // 注册点所在行数 + + name string // 名称 + typeName string // 原始类型的全限定名 + status BeanStatus // 状态 + primary bool // 是否为主版本 + method bool // 是否为成员方法 + cond Condition // 判断条件 + order float32 // 收集时的顺序 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 + + configuration bool // 是否扫描成员方法 + includeMethod []string // 包含哪些成员方法 + excludeMethod []string // 排除那些成员方法 +} + // CondContext defines some methods of IoC container that conditions use. type CondContext interface { // Has returns whether the IoC container has a property. @@ -50,8 +76,6 @@ type Callable interface { Call(ctx ArgContext) ([]reflect.Value, error) } -type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) - // Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 // 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 // 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, @@ -72,3 +96,5 @@ type Context interface { Invoke(fn interface{}, args ...Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) } + +type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) diff --git a/gs/internal/gs_util/type.go b/gs/internal/gs/util.go similarity index 93% rename from gs/internal/gs_util/type.go rename to gs/internal/gs/util.go index 3a9e2f34..e774e6fd 100644 --- a/gs/internal/gs_util/type.go +++ b/gs/internal/gs/util.go @@ -14,10 +14,9 @@ * limitations under the License. */ -package gs_util +package gs import ( - "context" "reflect" "strings" ) @@ -25,8 +24,6 @@ import ( // errorType the reflection type of error. var errorType = reflect.TypeOf((*error)(nil)).Elem() -var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() - // TypeName returns a fully qualified name consisting of package path and type name. func TypeName(i interface{}) string { @@ -65,11 +62,6 @@ func IsErrorType(t reflect.Type) bool { return t == errorType || t.Implements(errorType) } -// IsContextType returns whether `t` is context.Context type. -func IsContextType(t reflect.Type) bool { - return t == contextType || t.Implements(contextType) -} - // ReturnNothing returns whether the function has no return value. func ReturnNothing(t reflect.Type) bool { return t.NumOut() == 0 diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 89fb17c9..43554592 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -40,8 +40,6 @@ type AppServer interface { // App 应用 type App struct { - banner string - c *gs_ctx.Container p *gs_conf.Configuration @@ -61,6 +59,25 @@ func NewApp() *App { return app } +// Object 参考 Container.Object 的解释。 +func (app *App) Object(i interface{}) *gs.BeanDefinition { + return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) +} + +// Provide 参考 Container.Provide 的解释。 +func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { + return app.c.Accept(gs_ctx.NewBean(ctor, args...)) +} + +// Accept 参考 Container.Accept 的解释。 +func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { + return app.c.Accept(b) +} + +func (app *App) Group(fn gs.GroupFunc) { + app.c.Group(fn) +} + func (app *App) Run() error { if _, err := app.Start(); err != nil { return err @@ -133,22 +150,3 @@ func (app *App) ShutDown(msg ...string) { close(app.exitChan) } } - -// Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *gs.BeanDefinition { - return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) -} - -// Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return app.c.Accept(gs_ctx.NewBean(ctor, args...)) -} - -// Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { - return app.c.Accept(b) -} - -func (app *App) Group(fn gs.GroupFunc) { - app.c.Group(fn) -} diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index bb9e8b86..4b6d58e4 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -27,7 +27,6 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) @@ -229,7 +228,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine case string: tag = g default: - tag = gs_util.TypeName(g) + ":" + tag = gs.TypeName(g) + ":" } // binds properties value by the "value" tag. @@ -245,7 +244,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine } // wires dependent beans by the "autowire" tag. - if gs_util.IsBeanReceiver(t) { + if gs.IsBeanReceiver(t) { v := reflect.New(t).Elem() if err = ctx.Wire(v, tag); err != nil { return reflect.Value{}, err @@ -384,7 +383,7 @@ func (r *Callable) Call(ctx gs.ArgContext) ([]reflect.Value, error) { } o := out[n-1] - if gs_util.IsErrorType(o.Type()) { + if gs.IsErrorType(o.Type()) { if i := o.Interface(); i != nil { return out[:n-1], i.(error) } diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index 4293e98c..76615518 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -35,10 +35,17 @@ import ( "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/gs/internal/gs_util" "github.com/go-spring/spring-core/util" ) +type BeanInit interface { + OnInit(ctx gs.Context) error +} + +type BeanDestroy interface { + OnDestroy() +} + type refreshState int const ( @@ -93,19 +100,6 @@ func New() *Container { return c } -// Context 返回 IoC 容器的 ctx 对象。 -func (c *Container) Context() context.Context { - return c.ctx -} - -type BeanInit interface { - OnInit(ctx gs.Context) error -} - -type BeanDestroy interface { - OnDestroy() -} - // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { @@ -123,7 +117,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { } t := v.Type() - if !gs_util.IsBeanType(t) { + if !gs.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -138,7 +132,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 if !fromValue && t.Kind() == reflect.Func { - if !gs_util.IsConstructor(t) { + if !gs.IsConstructor(t) { t1 := "func(...)bean" t2 := "func(...)(bean, error)" panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) @@ -152,12 +146,12 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { out0 := t.Out(0) v = reflect.New(out0) - if gs_util.IsBeanType(out0) { + if gs.IsBeanType(out0) { v = v.Elem() } t = v.Type() - if !gs_util.IsBeanType(t) { + if !gs.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -187,8 +181,14 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { return gs.NewBean(t, v, f, name, method, file, line) } -func (c *Container) Group(fn gs.GroupFunc) { - c.groupFuncs = append(c.groupFuncs, fn) +// Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 +func (c *Container) Object(i interface{}) *gs.BeanDefinition { + return c.Accept(NewBean(reflect.ValueOf(i))) +} + +// Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 +func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { + return c.Accept(NewBean(ctor, args...)) } func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { @@ -199,14 +199,13 @@ func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return b } -// Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Object(i interface{}) *gs.BeanDefinition { - return c.Accept(NewBean(reflect.ValueOf(i))) +func (c *Container) Group(fn gs.GroupFunc) { + c.groupFuncs = append(c.groupFuncs, fn) } -// Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return c.Accept(NewBean(ctor, args...)) +// Context 返回 IoC 容器的 ctx 对象。 +func (c *Container) Context() context.Context { + return c.ctx } func (c *Container) Keys() []string { @@ -404,9 +403,9 @@ func (c *Container) Refresh() (err error) { } excludes = append(excludes, x) } - n := bd.T.NumMethod() + n := bd.Type().NumMethod() for i := 0; i < n; i++ { - m := bd.T.Method(i) + m := bd.Type().Method(i) skip := false for _, x := range excludes { if x.MatchString(m.Name) { @@ -427,7 +426,7 @@ func (c *Container) Refresh() (err error) { name := bd.GetName() + "_" + m.Name out0 := m.Type.Out(0) v := reflect.New(out0) - if gs_util.IsBeanType(out0) { + if gs.IsBeanType(out0) { v = v.Elem() } t := v.Type() @@ -540,9 +539,9 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { // method bean 先确定 parent bean 是否存在 if b.IsMethod() { - selector, ok := b.F.Arg(0) + selector, ok := b.Callable().Arg(0) if !ok || selector == "" { - selector, _ = b.F.In(0) + selector, _ = b.Callable().In(0) } parents, err := c.Find(selector) if err != nil { @@ -550,7 +549,7 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { } n := len(parents) if n > 1 { - msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.T.In(0)) + msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.Type().In(0)) for _, b := range parents { msg += "( " + b.String() + " ), " } @@ -632,7 +631,7 @@ func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { case *gs.BeanDefinition: return parseWireTag(s.ID()), nil default: - return parseWireTag(gs_util.TypeName(s) + ":"), nil + return parseWireTag(gs.TypeName(s) + ":"), nil } } @@ -737,7 +736,7 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { stack.pushBack(b) - if b.GetStatus() == gs.Creating && b.F != nil { + if b.GetStatus() == gs.Creating && b.Callable() != nil { prev := stack.beans[len(stack.beans)-2] if prev.GetStatus() == gs.Creating { return errors.New("found circle autowire") @@ -823,17 +822,17 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { - if b.F == nil { + if b.Callable() == nil { return b.Value(), nil } - out, err := b.F.Call(&argContext{c: c, stack: stack}) + out, err := b.Callable().Call(&argContext{c: c, stack: stack}) if err != nil { return reflect.Value{}, err /* fmt.Errorf("%s:%s return error: %v", b.getClass(), b.ID(), err) */ } // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 - if val := out[0]; gs_util.IsBeanType(val.Type()) { + if val := out[0]; gs.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 if !val.IsNil() && val.Kind() == reflect.Interface && conf.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) @@ -888,7 +887,7 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar fv := v.Field(i) if !fv.CanInterface() { - fv = gs_util.PatchValue(fv) + fv = PatchValue(fv) if !fv.CanInterface() { continue } @@ -1009,7 +1008,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } t := v.Type() - if !gs_util.IsBeanReceiver(t) { + if !gs.IsBeanReceiver(t) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -1147,7 +1146,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } et := t.Elem() - if !gs_util.IsBeanReceiver(et) { + if !gs.IsBeanReceiver(et) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -1318,7 +1317,7 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { - if !gs_util.IsFuncType(reflect.TypeOf(fn)) { + if !gs.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") } diff --git a/gs/internal/gs_util/value.go b/gs/internal/gs_ctx/util.go similarity index 59% rename from gs/internal/gs_util/value.go rename to gs/internal/gs_ctx/util.go index 68320817..b4cefca3 100644 --- a/gs/internal/gs_util/value.go +++ b/gs/internal/gs_ctx/util.go @@ -14,12 +14,10 @@ * limitations under the License. */ -package gs_util +package gs_ctx import ( "reflect" - "runtime" - "strings" "unsafe" ) @@ -37,30 +35,3 @@ func PatchValue(v reflect.Value) reflect.Value { *ptrFlag = *ptrFlag &^ flagRO return v } - -// Indirect returns its element type when t is a pointer type. -func Indirect(t reflect.Type) reflect.Type { - if t.Kind() != reflect.Ptr { - return t - } - return t.Elem() -} - -// FileLine returns a function's name, file name and line number. -func FileLine(fn interface{}) (file string, line int, fnName string) { - - fnPtr := reflect.ValueOf(fn).Pointer() - fnInfo := runtime.FuncForPC(fnPtr) - file, line = fnInfo.FileLine(fnPtr) - - s := fnInfo.Name() - if ss := strings.Split(s, "/"); len(ss) > 0 { - s = ss[len(ss)-1] - i := strings.Index(s, ".") - s = s[i+1:] - } - - // method values are printed as "T.m-fm" - s = strings.TrimRight(s, "-fm") - return file, line, s -} From 3d567045cc5f52e15e5fd1a51857913d5847e7e7 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 2 Jan 2025 18:00:12 +0800 Subject: [PATCH 25/75] code refactor --- conf/bind.go | 19 +- conf/conf.go | 2 - conf/expr.go | 51 ++ expr/expr_test.go | 17 - gs/internal/gs_cond/cond.go | 7 +- gs/internal/gs_cond/cond_mock.go | 4 +- gs/internal/gs_cond/cond_test.go | 891 +++++++++++++------------- {expr => gs/internal/gs_cond}/expr.go | 27 +- gs/internal/gs_ctx/ctx.go | 5 +- gs/internal/gs_ctx/util.go | 72 ++- util/error.go | 19 + util/fileline.go | 45 -- util/triple_sort.go | 90 --- 13 files changed, 609 insertions(+), 640 deletions(-) create mode 100644 conf/expr.go delete mode 100644 expr/expr_test.go rename {expr => gs/internal/gs_cond}/expr.go (60%) delete mode 100644 util/fileline.go delete mode 100644 util/triple_sort.go diff --git a/conf/bind.go b/conf/bind.go index 26b97bd2..ec78497d 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -31,21 +31,6 @@ var ( ErrInvalidSyntax = errors.New("invalid syntax") ) -var ( - bindValidator ValidatorInterface -) - -// ValidatorInterface is the interface for validating a field. -type ValidatorInterface interface { - Name() string - Field(tag string, i interface{}) error -} - -// SetValidator sets the validator. -func SetValidator(i ValidatorInterface) { - bindValidator = i -} - // ParsedTag a value tag includes at most three parts: required key, optional // default value, and optional splitter, the syntax is ${key:=value}>>splitter. type ParsedTag struct { @@ -139,9 +124,9 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind defer func() { if RetErr == nil { - tag, ok := param.Validate.Lookup(bindValidator.Name()) + tag, ok := param.Validate.Lookup("expr") if ok && len(tag) > 0 { - err := bindValidator.Field(tag, v.Interface()) + err := validateField(tag, v.Interface()) if err != nil { RetErr = err } diff --git a/conf/conf.go b/conf/conf.go index a5d38241..0253f2e6 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -32,7 +32,6 @@ import ( "github.com/go-spring/spring-core/conf/reader/toml" "github.com/go-spring/spring-core/conf/reader/yaml" "github.com/go-spring/spring-core/conf/store" - "github.com/go-spring/spring-core/expr" "github.com/spf13/cast" ) @@ -43,7 +42,6 @@ var ( ) func init() { - SetValidator(&expr.Validator{}) RegisterReader(json.Read, ".json") RegisterReader(prop.Read, ".properties") diff --git a/conf/expr.go b/conf/expr.go new file mode 100644 index 00000000..8e5feba9 --- /dev/null +++ b/conf/expr.go @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package conf + +import ( + "fmt" + + "github.com/expr-lang/expr" +) + +type ValidateFunc interface{} + +var validateFuncs = map[string]interface{}{} + +func RegisterValidateFunc(name string, fn ValidateFunc) { + validateFuncs[name] = fn +} + +// validateField validates the field with the given tag and value. +func validateField(tag string, i interface{}) error { + env := map[string]interface{}{"$": i} + for k, v := range validateFuncs { + env[k] = v + } + r, err := expr.Eval(tag, env) + if err != nil { + return fmt.Errorf("eval %q returns error, %w", tag, err) + } + ret, ok := r.(bool) + if !ok { + return fmt.Errorf("eval %q doesn't return bool value", tag) + } + if !ret { + return fmt.Errorf("validate failed on %q for value %v", tag, i) + } + return nil +} diff --git a/expr/expr_test.go b/expr/expr_test.go deleted file mode 100644 index 55bd4a71..00000000 --- a/expr/expr_test.go +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package expr_test diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index fd7a8011..68fc0327 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -16,7 +16,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=cond -source=cond.go -destination=cond_mock.go -// Package cond provides many conditions used when registering bean. +// Package gs_cond provides many conditions used when registering bean. package gs_cond import ( @@ -25,7 +25,6 @@ import ( "strconv" "strings" - "github.com/go-spring/spring-core/expr" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/util" ) @@ -76,7 +75,7 @@ func (c *onProperty) Matches(ctx gs.CondContext) (bool, error) { } val := ctx.Prop(c.name) - if !strings.HasPrefix(c.havingValue, "go:") { + if !strings.HasPrefix(c.havingValue, "expr:") { return val == c.havingValue, nil } @@ -95,7 +94,7 @@ func (c *onProperty) Matches(ctx gs.CondContext) (bool, error) { } return val } - return expr.Eval(c.havingValue[3:], getValue(val)) + return evalExpr(c.havingValue[5:], getValue(val)) } // onMissingProperty is a Condition that returns true when a property doesn't exist. diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index ca101045..cf412253 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -42,10 +42,10 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector gs.BeanSelector) ([]gs.BeanDefinition, error) { +func (m *MockContext) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]gs.BeanDefinition) + ret0, _ := ret[0].([]*gs.BeanDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/gs/internal/gs_cond/cond_test.go b/gs/internal/gs_cond/cond_test.go index c7364577..afccdd03 100644 --- a/gs/internal/gs_cond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -16,445 +16,452 @@ package gs_cond_test -// func TestOK(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.OK().Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// } -// -// func TestNot(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.Not(cond.OK()).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// } -// -// func TestOnProperty(t *testing.T) { -// t.Run("no property & no HavingValue & no MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(false) -// ok, err := cond.OnProperty("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("has property & no HavingValue & no MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ok, err := cond.OnProperty("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("no property & has HavingValue & no MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(false) -// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("diff property & has HavingValue & no MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ctx.EXPECT().Prop("a").Return("b") -// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("same property & has HavingValue & no MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ctx.EXPECT().Prop("a").Return("a") -// ok, err := cond.OnProperty("a", cond.HavingValue("a")).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("no property & no HavingValue & has MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(false) -// ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("has property & no HavingValue & has MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ok, err := cond.OnProperty("a", cond.MatchIfMissing()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("no property & has HavingValue & has MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(false) -// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("diff property & has HavingValue & has MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ctx.EXPECT().Prop("a").Return("b") -// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("same property & has HavingValue & has MatchIfMissing", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ctx.EXPECT().Prop("a").Return("a") -// ok, err := cond.OnProperty("a", cond.HavingValue("a"), cond.MatchIfMissing()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("go expression", func(t *testing.T) { -// testcases := []struct { -// propValue string -// expression string -// expectResult bool -// }{ -// { -// "a", -// "go:$==\"a\"", -// true, -// }, -// { -// "a", -// "go:$==\"b\"", -// false, -// }, -// { -// "3", -// "go:$==3", -// true, -// }, -// { -// "3", -// "go:$==4", -// false, -// }, -// { -// "3", -// "go:$>1&&$<5", -// true, -// }, -// { -// "false", -// "go:$", -// false, -// }, -// { -// "false", -// "go:!$", -// true, -// }, -// } -// for _, testcase := range testcases { -// ctrl := gomock.NewController(t) -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ctx.EXPECT().Prop("a").Return(testcase.propValue) -// ok, err := cond.OnProperty("a", cond.HavingValue(testcase.expression)).Matches(ctx) -// assert.Nil(t, err) -// assert.Equal(t, ok, testcase.expectResult) -// ctrl.Finish() -// } -// }) -// } -// -// func TestOnMissingProperty(t *testing.T) { -// t.Run("no property", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(false) -// ok, err := cond.OnMissingProperty("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("has property", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("a").Return(true) -// ok, err := cond.OnMissingProperty("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// } -// -// func TestOnBean(t *testing.T) { -// t.Run("return error", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) -// ok, err := cond.OnBean("a").Matches(ctx) -// assert.Error(t, err, "error") -// assert.False(t, ok) -// }) -// t.Run("no bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, nil) -// ok, err := cond.OnBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("one bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("more than one beans", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// } -// -// func TestOnMissingBean(t *testing.T) { -// t.Run("return error", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) -// ok, err := cond.OnMissingBean("a").Matches(ctx) -// assert.Error(t, err, "error") -// assert.False(t, ok) -// }) -// t.Run("no bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, nil) -// ok, err := cond.OnMissingBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("one bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnMissingBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("more than one beans", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnMissingBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// } -// -// func TestOnSingleBean(t *testing.T) { -// t.Run("return error", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, errors.New("error")) -// ok, err := cond.OnSingleBean("a").Matches(ctx) -// assert.Error(t, err, "error") -// assert.False(t, ok) -// }) -// t.Run("no bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return(nil, nil) -// ok, err := cond.OnSingleBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("one bean", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnSingleBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("more than one beans", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Find("a").Return([]gsutil.BeanDefinition{ -// gsutil.NewMockBeanDefinition(nil), -// gsutil.NewMockBeanDefinition(nil), -// }, nil) -// ok, err := cond.OnSingleBean("a").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// } -// -// func TestOnExpression(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.OnExpression("").Matches(ctx) -// assert.Error(t, err, "unimplemented method") -// assert.False(t, ok) -// } -// -// func TestOnMatches(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.OnMatches(func(ctx cond.Context) (bool, error) { -// return false, nil -// }).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// } -// -// func TestOnProfile(t *testing.T) { -// t.Run("no property", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("spring.profiles.active").Return(false) -// ok, err := cond.OnProfile("test").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("diff property", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("spring.profiles.active").Return(true) -// ctx.EXPECT().Prop("spring.profiles.active").Return("dev") -// ok, err := cond.OnProfile("test").Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("same property", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ctx.EXPECT().Has("spring.profiles.active").Return(true) -// ctx.EXPECT().Prop("spring.profiles.active").Return("test") -// ok, err := cond.OnProfile("test").Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// } -// -// func TestConditional(t *testing.T) { -// t.Run("ok && ", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.On(cond.OK()).And().Matches(ctx) -// assert.Error(t, err, "no condition in last node") -// assert.False(t, ok) -// }) -// t.Run("ok && !ok", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.On(cond.OK()).And().On(cond.Not(cond.OK())).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("ok || ", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.On(cond.OK()).Or().Matches(ctx) -// assert.Error(t, err, "no condition in last node") -// assert.False(t, ok) -// }) -// t.Run("ok || !ok", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.On(cond.OK()).Or().On(cond.Not(cond.OK())).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// } -// -// func TestGroup(t *testing.T) { -// t.Run("ok && ", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.Group(cond.And, cond.OK()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("ok && !ok", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.Group(cond.And, cond.OK(), cond.Not(cond.OK())).Matches(ctx) -// assert.Nil(t, err) -// assert.False(t, ok) -// }) -// t.Run("ok || ", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.Group(cond.Or, cond.OK()).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// t.Run("ok || !ok", func(t *testing.T) { -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// ctx := cond.NewMockContext(ctrl) -// ok, err := cond.Group(cond.Or, cond.OK(), cond.Not(cond.OK())).Matches(ctx) -// assert.Nil(t, err) -// assert.True(t, ok) -// }) -// } +import ( + "errors" + "testing" + + "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestOK(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.OK().Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) +} + +func TestNot(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.Not(gs_cond.OK()).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) +} + +func TestOnProperty(t *testing.T) { + t.Run("no property & no HavingValue & no MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(false) + ok, err := gs_cond.OnProperty("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("has property & no HavingValue & no MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ok, err := gs_cond.OnProperty("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("no property & has HavingValue & no MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(false) + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a")).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("diff property & has HavingValue & no MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ctx.EXPECT().Prop("a").Return("b") + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a")).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("same property & has HavingValue & no MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ctx.EXPECT().Prop("a").Return("a") + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a")).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("no property & no HavingValue & has MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(false) + ok, err := gs_cond.OnProperty("a", gs_cond.MatchIfMissing()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("has property & no HavingValue & has MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ok, err := gs_cond.OnProperty("a", gs_cond.MatchIfMissing()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("no property & has HavingValue & has MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(false) + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a"), gs_cond.MatchIfMissing()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("diff property & has HavingValue & has MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ctx.EXPECT().Prop("a").Return("b") + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a"), gs_cond.MatchIfMissing()).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("same property & has HavingValue & has MatchIfMissing", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ctx.EXPECT().Prop("a").Return("a") + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue("a"), gs_cond.MatchIfMissing()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("go expression", func(t *testing.T) { + testcases := []struct { + propValue string + expression string + expectResult bool + }{ + { + "a", + "expr:$==\"a\"", + true, + }, + { + "a", + "expr:$==\"b\"", + false, + }, + { + "3", + "expr:$==3", + true, + }, + { + "3", + "expr:$==4", + false, + }, + { + "3", + "expr:$>1&&$<5", + true, + }, + { + "false", + "expr:$", + false, + }, + { + "false", + "expr:!$", + true, + }, + } + for _, testcase := range testcases { + ctrl := gomock.NewController(t) + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ctx.EXPECT().Prop("a").Return(testcase.propValue) + ok, err := gs_cond.OnProperty("a", gs_cond.HavingValue(testcase.expression)).Matches(ctx) + assert.Nil(t, err) + assert.Equal(t, ok, testcase.expectResult) + ctrl.Finish() + } + }) +} + +func TestOnMissingProperty(t *testing.T) { + t.Run("no property", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(false) + ok, err := gs_cond.OnMissingProperty("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("has property", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("a").Return(true) + ok, err := gs_cond.OnMissingProperty("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) +} + +func TestOnBean(t *testing.T) { + t.Run("return error", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, errors.New("error")) + ok, err := gs_cond.OnBean("a").Matches(ctx) + assert.Error(t, err, "error") + assert.False(t, ok) + }) + t.Run("no bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, nil) + ok, err := gs_cond.OnBean("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("one bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, + }, nil) + ok, err := gs_cond.OnBean("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("more than one beans", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, nil, + }, nil) + ok, err := gs_cond.OnBean("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) +} + +func TestOnMissingBean(t *testing.T) { + t.Run("return error", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, errors.New("error")) + ok, err := gs_cond.OnMissingBean("a").Matches(ctx) + assert.Error(t, err, "error") + assert.False(t, ok) + }) + t.Run("no bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, nil) + ok, err := gs_cond.OnMissingBean("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("one bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, + }, nil) + ok, err := gs_cond.OnMissingBean("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("more than one beans", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, nil, + }, nil) + ok, err := gs_cond.OnMissingBean("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) +} + +func TestOnSingleBean(t *testing.T) { + t.Run("return error", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, errors.New("error")) + ok, err := gs_cond.OnSingleBean("a").Matches(ctx) + assert.Error(t, err, "error") + assert.False(t, ok) + }) + t.Run("no bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return(nil, nil) + ok, err := gs_cond.OnSingleBean("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("one bean", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, + }, nil) + ok, err := gs_cond.OnSingleBean("a").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("more than one beans", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + nil, nil, + }, nil) + ok, err := gs_cond.OnSingleBean("a").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) +} + +func TestOnExpression(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.OnExpression("").Matches(ctx) + assert.Error(t, err, "unimplemented method") + assert.False(t, ok) +} + +func TestOnMatches(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.OnMatches(func(ctx gs.CondContext) (bool, error) { + return false, nil + }).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) +} + +func TestOnProfile(t *testing.T) { + t.Run("no property", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("spring.profiles.active").Return(false) + ok, err := gs_cond.OnProfile("test").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("diff property", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("spring.profiles.active").Return(true) + ctx.EXPECT().Prop("spring.profiles.active").Return("dev") + ok, err := gs_cond.OnProfile("test").Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("same property", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ctx.EXPECT().Has("spring.profiles.active").Return(true) + ctx.EXPECT().Prop("spring.profiles.active").Return("test") + ok, err := gs_cond.OnProfile("test").Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) +} + +func TestConditional(t *testing.T) { + t.Run("ok && ", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.On(gs_cond.OK()).And().Matches(ctx) + assert.Error(t, err, "no condition in last node") + assert.False(t, ok) + }) + t.Run("ok && !ok", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.On(gs_cond.OK()).And().On(gs_cond.Not(gs_cond.OK())).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("ok || ", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.On(gs_cond.OK()).Or().Matches(ctx) + assert.Error(t, err, "no condition in last node") + assert.False(t, ok) + }) + t.Run("ok || !ok", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.On(gs_cond.OK()).Or().On(gs_cond.Not(gs_cond.OK())).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) +} + +func TestGroup(t *testing.T) { + t.Run("ok && ", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.Group(gs_cond.And, gs_cond.OK()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("ok && !ok", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.Group(gs_cond.And, gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) + assert.Nil(t, err) + assert.False(t, ok) + }) + t.Run("ok || ", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.Group(gs_cond.Or, gs_cond.OK()).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) + t.Run("ok || !ok", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := gs_cond.NewMockContext(ctrl) + ok, err := gs_cond.Group(gs_cond.Or, gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) + assert.Nil(t, err) + assert.True(t, ok) + }) +} diff --git a/expr/expr.go b/gs/internal/gs_cond/expr.go similarity index 60% rename from expr/expr.go rename to gs/internal/gs_cond/expr.go index 74f60e5f..daa3902f 100644 --- a/expr/expr.go +++ b/gs/internal/gs_cond/expr.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package expr +package gs_cond import ( "fmt" @@ -22,26 +22,19 @@ import ( "github.com/expr-lang/expr" ) -type Validator struct{} +var exprFuncs = map[string]interface{}{} -// Name returns the name of the validator. -func (d *Validator) Name() string { - return "expr" +func RegisterExprFunc(name string, fn interface{}) { + exprFuncs[name] = fn } -// Field validates the field with the given tag and value. -func (d *Validator) Field(tag string, i interface{}) error { - if ret, err := Eval(tag, i); err != nil { - return err - } else if !ret { - return fmt.Errorf("validate failed on %q for value %v", tag, i) +// evalExpr returns the value for the expression expr. +func evalExpr(input string, val interface{}) (bool, error) { + env := map[string]interface{}{"$": val} + for k, v := range exprFuncs { + env[k] = v } - return nil -} - -// Eval returns the value for the expression expr. -func Eval(input string, val interface{}) (bool, error) { - r, err := expr.Eval(input, map[string]interface{}{"$": val}) + r, err := expr.Eval(input, env) if err != nil { return false, fmt.Errorf("eval %q returns error, %w", input, err) } diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index 76615518..9c735daf 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -14,7 +14,7 @@ * limitations under the License. */ -// Package gsioc 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App +// Package gs_ctx 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App // 以及全局 App 对象封装三个部分,可以应用于多种使用场景。 package gs_ctx @@ -35,7 +35,6 @@ import ( "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/go-spring/spring-core/util" ) type BeanInit interface { @@ -338,7 +337,7 @@ func (s *wiringStack) sortDestroyers() []func() { for _, d := range s.destroyerMap { destroyers.PushBack(d) } - destroyers = util.TripleSort(destroyers, getBeforeDestroyers) + destroyers = TripleSort(destroyers, getBeforeDestroyers) var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { diff --git a/gs/internal/gs_ctx/util.go b/gs/internal/gs_ctx/util.go index b4cefca3..56d82611 100644 --- a/gs/internal/gs_ctx/util.go +++ b/gs/internal/gs_ctx/util.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package gs_ctx import ( + "container/list" + "errors" "reflect" "unsafe" ) @@ -35,3 +37,71 @@ func PatchValue(v reflect.Value) reflect.Value { *ptrFlag = *ptrFlag &^ flagRO return v } + +// GetBeforeItems 获取 sorting 中排在 current 前面的元素 +type GetBeforeItems func(sorting *list.List, current interface{}) *list.List + +// TripleSort 三路排序 +func TripleSort(sorting *list.List, fn GetBeforeItems) *list.List { + + toSort := list.New() // 待排序列表 + sorted := list.New() // 已排序列表 + processing := list.New() // 正在处理列表 + + toSort.PushBackList(sorting) + + for toSort.Len() > 0 { // 递归选出依赖链条最前端的元素 + tripleSortByAfter(sorting, toSort, sorted, processing, nil, fn) + } + return sorted +} + +// searchInList 在列表中查询指定元素,存在则返回列表项指针,不存在返回 nil。 +func searchInList(l *list.List, v interface{}) *list.Element { + for e := l.Front(); e != nil; e = e.Next() { + if e.Value == v { + return e + } + } + return nil +} + +// tripleSortByAfter 递归选出依赖链条最前端的元素 +func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, + processing *list.List, current interface{}, fn GetBeforeItems) { + + if current == nil { + current = toSort.Remove(toSort.Front()) + } + + // 将当前元素标记为正在处理 + processing.PushBack(current) + + // 获取排在当前元素前面的列表项,然后依次对它们进行排序 + for e := fn(sorting, current).Front(); e != nil; e = e.Next() { + c := e.Value + + // 自己不可能是自己前面的元素,除非出现了循环依赖,因此抛出 Panic + if searchInList(processing, c) != nil { + panic(errors.New("found sorting cycle")) + } + + inSorted := searchInList(sorted, c) != nil + inToSort := searchInList(toSort, c) != nil + + if !inSorted && inToSort { // 如果是待排元素则对其进行排序 + tripleSortByAfter(sorting, toSort, sorted, processing, c, fn) + } + } + + if e := searchInList(processing, current); e != nil { + processing.Remove(e) + } + + if e := searchInList(toSort, current); e != nil { + toSort.Remove(e) + } + + // 将当前元素标记为已完成 + sorted.PushBack(current) +} diff --git a/util/error.go b/util/error.go index a0e424e6..e52a45af 100644 --- a/util/error.go +++ b/util/error.go @@ -19,8 +19,27 @@ package util import ( "errors" "fmt" + "runtime" + "sync" ) +var frameMap sync.Map + +// FileLine returns the file name and line of the call point. +// In reality FileLine here costs less time than debug.Stack. +func FileLine() string { + rpc := make([]uintptr, 1) + runtime.Callers(3, rpc[:]) + pc := rpc[0] + if v, ok := frameMap.Load(pc); ok { + e := v.(*runtime.Frame) + return fmt.Sprintf("%s:%d", e.File, e.Line) + } + e, _ := runtime.CallersFrames(rpc).Next() + frameMap.Store(pc, &e) + return fmt.Sprintf("%s:%d", e.File, e.Line) +} + // ForbiddenMethod throws this error when calling a method is prohibited. var ForbiddenMethod = errors.New("forbidden method") diff --git a/util/fileline.go b/util/fileline.go deleted file mode 100644 index 15eddc3d..00000000 --- a/util/fileline.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import ( - "fmt" - "runtime" - "sync" -) - -var frameMap sync.Map - -func fileLine() (string, int) { - rpc := make([]uintptr, 1) - runtime.Callers(3, rpc[:]) - pc := rpc[0] - if v, ok := frameMap.Load(pc); ok { - e := v.(*runtime.Frame) - return e.File, e.Line - } - frame, _ := runtime.CallersFrames(rpc).Next() - frameMap.Store(pc, &frame) - return frame.File, frame.Line -} - -// FileLine returns the file name and line of the call point. -// In reality FileLine here costs less time than debug.Stack. -func FileLine() string { - file, line := fileLine() - return fmt.Sprintf("%s:%d", file, line) -} diff --git a/util/triple_sort.go b/util/triple_sort.go deleted file mode 100644 index 50df19f6..00000000 --- a/util/triple_sort.go +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import ( - "container/list" - "errors" -) - -// GetBeforeItems 获取 sorting 中排在 current 前面的元素 -type GetBeforeItems func(sorting *list.List, current interface{}) *list.List - -// TripleSort 三路排序 -func TripleSort(sorting *list.List, fn GetBeforeItems) *list.List { - - toSort := list.New() // 待排序列表 - sorted := list.New() // 已排序列表 - processing := list.New() // 正在处理列表 - - toSort.PushBackList(sorting) - - for toSort.Len() > 0 { // 递归选出依赖链条最前端的元素 - tripleSortByAfter(sorting, toSort, sorted, processing, nil, fn) - } - return sorted -} - -// searchInList 在列表中查询指定元素,存在则返回列表项指针,不存在返回 nil。 -func searchInList(l *list.List, v interface{}) *list.Element { - for e := l.Front(); e != nil; e = e.Next() { - if e.Value == v { - return e - } - } - return nil -} - -// tripleSortByAfter 递归选出依赖链条最前端的元素 -func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, - processing *list.List, current interface{}, fn GetBeforeItems) { - - if current == nil { - current = toSort.Remove(toSort.Front()) - } - - // 将当前元素标记为正在处理 - processing.PushBack(current) - - // 获取排在当前元素前面的列表项,然后依次对它们进行排序 - for e := fn(sorting, current).Front(); e != nil; e = e.Next() { - c := e.Value - - // 自己不可能是自己前面的元素,除非出现了循环依赖,因此抛出 Panic - if searchInList(processing, c) != nil { - panic(errors.New("found sorting cycle")) - } - - inSorted := searchInList(sorted, c) != nil - inToSort := searchInList(toSort, c) != nil - - if !inSorted && inToSort { // 如果是待排元素则对其进行排序 - tripleSortByAfter(sorting, toSort, sorted, processing, c, fn) - } - } - - if e := searchInList(processing, current); e != nil { - processing.Remove(e) - } - - if e := searchInList(toSort, current); e != nil { - toSort.Remove(e) - } - - // 将当前元素标记为已完成 - sorted.PushBack(current) -} From 2e8283fcf8a59ed86e6dcd61bc6566599a84e723 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 2 Jan 2025 21:07:47 +0800 Subject: [PATCH 26/75] add refreshable bean --- dync/dync.go | 173 +++++++++++++++++++-------------- dync/dync_test.go | 10 +- gs/internal/gs_ctx/ctx.go | 2 +- gs/internal/gs_ctx/ctx_test.go | 8 +- 4 files changed, 112 insertions(+), 81 deletions(-) diff --git a/dync/dync.go b/dync/dync.go index 9c77c0d7..eed07853 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -22,13 +22,14 @@ import ( "reflect" "sort" "strings" + "sync" + "sync/atomic" "github.com/go-spring/spring-core/conf" - "go.uber.org/atomic" ) -// ValueInterface 可动态刷新的对象 -type ValueInterface interface { +// Refreshable 可动态刷新的对象 +type Refreshable interface { OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error } @@ -37,6 +38,12 @@ type Value[T interface{}] struct { v atomic.Value } +// Value 获取值 +func (r *Value[T]) Value() T { + return r.v.Load().(T) +} + +// OnRefresh 实现 Refreshable 接口 func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { var o T v := reflect.ValueOf(&o).Elem() @@ -48,38 +55,46 @@ func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) return nil } +// MarshalJSON 实现 json.Marshaler 接口 func (r *Value[T]) MarshalJSON() ([]byte, error) { return json.Marshal(r.v.Load()) } -type Field struct { - value ValueInterface - param conf.BindParam +// refreshObject 绑定的可刷新对象 +type refreshObject struct { + target Refreshable + param conf.BindParam } // Properties 动态属性 type Properties struct { - value atomic.Value - fields []*Field + prop conf.ReadOnlyProperties + lock sync.RWMutex + objects []*refreshObject } +// New 创建一个 Properties 对象 func New() *Properties { - p := &Properties{} - p.value.Store(conf.New()) - return p -} - -func (p *Properties) load() *conf.Properties { - return p.value.Load().(*conf.Properties) + return &Properties{ + prop: conf.New(), + } } +// Data 获取属性列表 func (p *Properties) Data() conf.ReadOnlyProperties { - return p.load() + p.lock.RLock() + defer p.lock.RUnlock() + return p.prop } +// Refresh 更新属性列表以及绑定的可刷新对象 func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { + p.lock.Lock() + defer p.lock.Unlock() + + old := p.prop + p.prop = prop - old := p.load() oldKeys := old.Keys() newKeys := prop.Keys() @@ -102,69 +117,95 @@ func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { keys = append(keys, k) } sort.Strings(keys) - return p.refreshKeys(prop, keys) + return p.refreshKeys(keys) } -func (p *Properties) refreshKeys(prop conf.ReadOnlyProperties, keys []string) (err error) { - - updateIndexes := make(map[int]*Field) +func (p *Properties) refreshKeys(keys []string) (err error) { + var objects []*refreshObject for _, key := range keys { - for index, field := range p.fields { - s := strings.TrimPrefix(key, field.param.Key) - if len(s) == len(key) { + for _, obj := range p.objects { + if !strings.HasPrefix(key, obj.param.Key) { continue } + s := strings.TrimPrefix(key, obj.param.Key) if len(s) == 0 || s[0] == '.' || s[0] == '[' { - if _, ok := updateIndexes[index]; !ok { - updateIndexes[index] = field - } + objects = append(objects, obj) } } } + if len(objects) == 0 { + return nil + } + return p.refreshObjects(objects) +} - updateFields := make([]*Field, 0, len(updateIndexes)) - { - ints := make([]int, 0, len(updateIndexes)) - for k := range updateIndexes { - ints = append(ints, k) - } - sort.Ints(ints) - for _, k := range ints { - updateFields = append(updateFields, updateIndexes[k]) - } +// Errors 错误列表 +type Errors struct { + arr []error +} + +// Len 错误数量 +func (e *Errors) Len() int { + return len(e.arr) +} + +// Append 添加一个错误 +func (e *Errors) Append(err error) { + if err != nil { + e.arr = append(e.arr, err) } +} - return p.refreshFields(prop, updateFields) +// Error 实现 error 接口 +func (e *Errors) Error() string { + var sb strings.Builder + for _, err := range e.arr { + sb.WriteString(err.Error()) + sb.WriteString("\n") + } + return sb.String() } -func (p *Properties) refreshFields(prop conf.ReadOnlyProperties, fields []*Field) (err error) { +func (p *Properties) refreshObjects(objects []*refreshObject) error { + ret := &Errors{} + for _, f := range objects { + err := p.safeRefreshObject(f) + ret.Append(err) + } + if ret.Len() == 0 { + return nil + } + return ret +} - old := p.load() +func (p *Properties) safeRefreshObject(f *refreshObject) (err error) { defer func() { - if r := recover(); err != nil || r != nil { - if err == nil { - err = fmt.Errorf("%v", r) - } - p.value.Store(old) - _ = refreshFields(old, fields) + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) } }() + return f.target.OnRefresh(p.prop, f.param) +} - p.value.Store(prop) - return refreshFields(p.load(), fields) +// AddBean 添加一个可刷新对象 +func (p *Properties) AddBean(v Refreshable, param conf.BindParam) error { + p.lock.Lock() + defer p.lock.Unlock() + return p.addObjectNoLock(v, param) } -func refreshFields(prop conf.ReadOnlyProperties, fields []*Field) error { - for _, f := range fields { - err := f.value.OnRefresh(prop, f.param) - if err != nil { - return err - } - } - return nil +func (p *Properties) addObjectNoLock(v Refreshable, param conf.BindParam) error { + p.objects = append(p.objects, &refreshObject{ + target: v, + param: param, + }) + return v.OnRefresh(p.prop, param) } -func (p *Properties) BindValue(v reflect.Value, param conf.BindParam) error { +// AddField 添加一个 bean 的 field +func (p *Properties) AddField(v reflect.Value, param conf.BindParam) error { + p.lock.Lock() + defer p.lock.Unlock() if v.Kind() == reflect.Ptr { ok, err := p.bindValue(v.Interface(), param) if err != nil { @@ -174,25 +215,13 @@ func (p *Properties) BindValue(v reflect.Value, param conf.BindParam) error { return nil } } - return conf.BindValue(p.load(), v.Elem(), v.Elem().Type(), param, p.bindValue) + return conf.BindValue(p.prop, v.Elem(), v.Elem().Type(), param, p.bindValue) } func (p *Properties) bindValue(i interface{}, param conf.BindParam) (bool, error) { - - v, ok := i.(ValueInterface) + v, ok := i.(Refreshable) if !ok { return false, nil } - - prop := p.load() - err := v.OnRefresh(prop, param) - if err != nil { - return false, err - } - - p.fields = append(p.fields, &Field{ - value: v, - param: param, - }) - return true, nil + return true, p.addObjectNoLock(v, param) } diff --git a/dync/dync_test.go b/dync/dync_test.go index e279ff17..cafdcfc1 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -44,7 +44,7 @@ type Config struct { func newTest() (*dync.Properties, *Config, error) { mgr := dync.New() cfg := new(Config) - err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) if err != nil { return nil, nil, err } @@ -106,7 +106,7 @@ func TestDynamic(t *testing.T) { // fmt.Println("event fired.") // return nil // }) - err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) if err != nil { t.Fatal(err) } @@ -144,7 +144,7 @@ func TestDynamic(t *testing.T) { // fmt.Println("event fired.") // return nil // }) - err := mgr.BindValue(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) if err != nil { t.Fatal(err) } @@ -176,7 +176,7 @@ func TestDynamic(t *testing.T) { assert.Error(t, err, "validate failed on \"\\$<6\" for value 6") b, _ = json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":1,"Float":5.4,"Map":{"a":"3","b":"7"},"Slice":["2","9"],"Object":{"Str":"abc","Int":3}}`) + assert.Equal(t, string(b), `{"Int":1,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Object":{"Str":"abc","Int":3}}`) p = conf.New() p.Set("int", 4) @@ -187,6 +187,8 @@ func TestDynamic(t *testing.T) { p.Set("slice[1]", 4) mgr.Refresh(p) + assert.Equal(t, cfg.Int.Value(), int64(4)) + b, _ = json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"],"Object":{"Str":"abc","Int":3}}`) }) diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index 9c735daf..ed3b6404 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -930,7 +930,7 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar return err } } else { - err := c.p.BindValue(fv.Addr(), subParam) + err := c.p.AddField(fv.Addr(), subParam) if err != nil { return err } diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index 372e45c9..f246bbf7 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -2999,9 +2999,9 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) + assert.Equal(t, string(b), `{"Int":6,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":8.4,"Map":{"a":"3","b":"4"},"Slice":["2","1"]}}`) } { @@ -3024,9 +3024,9 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) + assert.Equal(t, string(b), `{"Int":1,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":8.4,"Map":{"a":"3","b":"4"},"Slice":["2","1"]}}`) } } From acdae41c72db3f1b4c0fccc90bf4e266ae1d0e6e Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 3 Jan 2025 08:43:03 +0800 Subject: [PATCH 27/75] add refreshable bean --- dync/dync.go | 31 ++++++++++++++++++++++++------- go.mod | 1 - go.sum | 2 -- gs/internal/gs/bean.go | 25 +++++++++++++++++++++++++ gs/internal/gs/gs.go | 3 +++ gs/internal/gs_ctx/ctx.go | 13 +++++++++++-- gs/internal/gs_ctx/ctx_test.go | 2 +- 7 files changed, 64 insertions(+), 13 deletions(-) diff --git a/dync/dync.go b/dync/dync.go index eed07853..c4418cec 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -121,22 +121,39 @@ func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { } func (p *Properties) refreshKeys(keys []string) (err error) { - var objects []*refreshObject + + // 找出需要更新的对象,一个对象可能对应多个 key,因此需要去重 + updateIndexes := make(map[int]*refreshObject) for _, key := range keys { - for _, obj := range p.objects { - if !strings.HasPrefix(key, obj.param.Key) { + for index, o := range p.objects { + s := strings.TrimPrefix(key, o.param.Key) + if len(s) == len(key) { // 是否去除了前缀 continue } - s := strings.TrimPrefix(key, obj.param.Key) if len(s) == 0 || s[0] == '.' || s[0] == '[' { - objects = append(objects, obj) + if _, ok := updateIndexes[index]; !ok { + updateIndexes[index] = o + } } } } - if len(objects) == 0 { + + updateObjects := make([]*refreshObject, 0, len(updateIndexes)) + { + ints := make([]int, 0, len(updateIndexes)) + for k := range updateIndexes { + ints = append(ints, k) + } + sort.Ints(ints) + for _, k := range ints { + updateObjects = append(updateObjects, updateIndexes[k]) + } + } + + if len(updateObjects) == 0 { return nil } - return p.refreshObjects(objects) + return p.refreshObjects(updateObjects) } // Errors 错误列表 diff --git a/go.mod b/go.mod index da758ee1..00e7c32d 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/spf13/cast v1.7.1 github.com/stretchr/testify v1.10.0 - go.uber.org/atomic v1.11.0 go.uber.org/mock v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index b79c2c1e..9549e4b9 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,6 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index b1852d60..ecc6d2f4 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -4,8 +4,13 @@ import ( "errors" "fmt" "reflect" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/dync" ) +var refreshableType = reflect.TypeFor[dync.Refreshable]() + type BeanStatus int8 const ( @@ -78,6 +83,14 @@ func (d *BeanDefinition) GetExcludeMethod() []string { return d.excludeMethod } +func (d *BeanDefinition) IsRefreshEnable() bool { + return d.enableRefresh +} + +func (d *BeanDefinition) GetRefreshParam() conf.BindParam { + return d.refreshParam +} + func (d *BeanDefinition) Callable() Callable { return d.f } @@ -267,6 +280,18 @@ func (d *BeanDefinition) Configuration(includes []string, excludes []string) *Be return d } +// EnableRefresh 设置 bean 为可刷新的。 +func (d *BeanDefinition) EnableRefresh(tag string) { + if !d.Type().Implements(refreshableType) { + panic(errors.New("must implement dync.Refreshable interface")) + } + d.enableRefresh = true + err := d.refreshParam.BindTag(tag, "") + if err != nil { + panic(err) + } +} + // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index a6f450be..7ac0bff6 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -35,6 +35,9 @@ type BeanDefinition struct { configuration bool // 是否扫描成员方法 includeMethod []string // 包含哪些成员方法 excludeMethod []string // 排除那些成员方法 + + enableRefresh bool + refreshParam conf.BindParam } // CondContext defines some methods of IoC container that conditions use. diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index ed3b6404..1ea4154d 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -782,6 +782,7 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { return err } + // 如果 bean 有初始化函数,则执行其初始化函数。 if b.GetInit() != nil { fnValue := reflect.ValueOf(b.GetInit()) out := fnValue.Call([]reflect.Value{b.Value()}) @@ -790,12 +791,21 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { } } + // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 if f, ok := b.Interface().(BeanInit); ok { if err = f.OnInit(c); err != nil { return err } } + // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 + if b.IsRefreshEnable() { + i := b.Interface().(dync.Refreshable) + if err = c.p.AddBean(i, b.GetRefreshParam()); err != nil { + return err + } + } + b.SetStatus(gs.Wired) stack.popBack() return nil @@ -920,8 +930,7 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar } if tag, ok = ft.Tag.Lookup("value"); ok { - // validateTag, _ := ft.Tag.Lookup(validate.TagName()) - if err := subParam.BindTag(tag, ""); err != nil { + if err := subParam.BindTag(tag, ft.Tag); err != nil { return err } if ft.Anonymous { diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index f246bbf7..85ec227f 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -2999,7 +2999,7 @@ func TestDynamic(t *testing.T) { { b, _ := json.Marshal(cfg) - assert.Equal(t, string(b), `{"Int":6,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) + assert.Equal(t, string(b), `{"Int":4,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) b, _ = json.Marshal(wrapper) assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":8.4,"Map":{"a":"3","b":"4"},"Slice":["2","1"]}}`) } From 5596faaabace59714803098474dcdf7be86964da Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 3 Jan 2025 14:31:17 +0800 Subject: [PATCH 28/75] add refresh test --- gs/internal/gs_ctx/ctx.go | 3 +- gs/internal/gs_ctx/ctx_test.go | 56 ++++++++++++++-------------------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_ctx/ctx.go index 1ea4154d..aeb4e03d 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_ctx/ctx.go @@ -35,6 +35,7 @@ import ( "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_cond" ) type BeanInit interface { @@ -429,7 +430,7 @@ func (c *Container) Refresh() (err error) { v = v.Elem() } t := v.Type() - b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()) + b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) break } diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_ctx/ctx_test.go index 85ec227f..71de0903 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_ctx/ctx_test.go @@ -2909,42 +2909,32 @@ type DynamicConfig struct { Slice dync.Value[[]string] `value:"${slice:=}"` } +var _ dync.Refreshable = (*DynamicConfig)(nil) + +func (d *DynamicConfig) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { + fmt.Println("DynamicConfig.OnRefresh") + return nil +} + type DynamicConfigWrapper struct { - Wrapper DynamicConfig `value:"${wrapper}"` + Wrapper DynamicConfig `value:"${wrapper}"` // struct 自身的 refresh 会抑制 fields 的 refresh +} + +var _ dync.Refreshable = (*DynamicConfigWrapper)(nil) + +func (d *DynamicConfigWrapper) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { + fmt.Println("DynamicConfig.OnRefresh") + return nil } func TestDynamic(t *testing.T) { - var cfg *DynamicConfig + cfg := new(DynamicConfig) wrapper := new(DynamicConfigWrapper) c := gs_ctx.New() - c.Provide(func() *DynamicConfig { - config := new(DynamicConfig) - // config.Int.OnValidate(func(v int64) error { - // if v < 3 { - // return errors.New("should greeter than 3") - // } - // return nil - // }) - // config.Slice.Init(make([]string, 0)) - // config.Map.Init(make(map[string]string)) - // config.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - // fmt.Println("event fired.") - // return nil - // }) - return config - }).Init(func(config *DynamicConfig) { - cfg = config - }) - c.Object(wrapper).Init(func(p *DynamicConfigWrapper) { - // p.Wrapper.Slice.Init(make([]string, 0)) - // p.Wrapper.Map.Init(make(map[string]string)) - // p.Wrapper.Event.OnEvent(func(prop conf.ReadOnlyProperties, param conf.BindParam) error { - // fmt.Println("event fired.") - // return nil - // }) - }) + c.Object(cfg) + c.Object(wrapper) err := c.Refresh() assert.Nil(t, err) @@ -2952,7 +2942,7 @@ func TestDynamic(t *testing.T) { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":3,"Float":1.2,"Map":{},"Slice":[]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.2,"Map":{},"Slice":[]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":null,"Float":null,"Map":null,"Slice":null}}`) } { @@ -2976,7 +2966,7 @@ func TestDynamic(t *testing.T) { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":4,"Float":2.3,"Map":{"a":"1","b":"2"},"Slice":["3","4"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":1.5,"Map":{"a":"9","b":"8"},"Slice":["4","6"]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":null,"Float":null,"Map":null,"Slice":null}}`) } { @@ -3001,7 +2991,7 @@ func TestDynamic(t *testing.T) { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":4,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":8.4,"Map":{"a":"3","b":"4"},"Slice":["2","1"]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":null,"Float":null,"Map":null,"Slice":null}}`) } { @@ -3019,14 +3009,14 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.slice[0]", 2) p.Set("wrapper.slice[1]", 1) err = c.RefreshProperties(p) - assert.Error(t, err, "should greeter than 3") + assert.Nil(t, err) } { b, _ := json.Marshal(cfg) assert.Equal(t, string(b), `{"Int":1,"Float":5.1,"Map":{"a":"9","b":"8"},"Slice":["7","6"]}`) b, _ = json.Marshal(wrapper) - assert.Equal(t, string(b), `{"Wrapper":{"Int":3,"Float":8.4,"Map":{"a":"3","b":"4"},"Slice":["2","1"]}}`) + assert.Equal(t, string(b), `{"Wrapper":{"Int":null,"Float":null,"Map":null,"Slice":null}}`) } } From 6932d5eb9d88a1097b2dcb932ad3c18437f58533 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 3 Jan 2025 20:59:32 +0800 Subject: [PATCH 29/75] code refactor --- conf/conf.go | 80 +++---- gs/gs.go | 12 +- gs/internal/gs_app/app.go | 18 +- gs/internal/gs_app/boot.go | 18 +- gs/internal/gs_conf/conf.go | 52 +++-- gs/internal/{gs => gs_core}/bean_test.go | 83 +------ .../{gs_ctx/ctx.go => gs_core/core.go} | 2 +- .../ctx_test.go => gs_core/core_test.go} | 218 +++++++++--------- gs/internal/{gs_ctx => gs_core}/util.go | 2 +- 9 files changed, 213 insertions(+), 272 deletions(-) rename gs/internal/{gs => gs_core}/bean_test.go (84%) rename gs/internal/{gs_ctx/ctx.go => gs_core/core.go} (99%) rename gs/internal/{gs_ctx/ctx_test.go => gs_core/core_test.go} (96%) rename gs/internal/{gs_ctx => gs_core}/util.go (99%) diff --git a/conf/conf.go b/conf/conf.go index 0253f2e6..8d3a207c 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -24,7 +24,6 @@ import ( "reflect" "sort" "strings" - "sync/atomic" "time" "github.com/go-spring/spring-core/conf/reader/json" @@ -89,6 +88,34 @@ func RegisterConverter(fn Converter) { converters[t.Out(0)] = fn } +// ReadOnlyProperties is the interface for read-only properties. +type ReadOnlyProperties interface { + + // Data returns key-value pairs of the properties. + Data() map[string]string + + // Keys returns keys of the properties. + Keys() []string + + // Has returns whether the key exists. + Has(key string) bool + + // SubKeys returns the sorted sub keys of the key. + SubKeys(key string) ([]string, error) + + // Get returns key's value, using Def to return a default value. + Get(key string, opts ...GetOption) string + + // Resolve resolves string that contains references. + Resolve(s string) (string, error) + + // Bind binds properties into a value. + Bind(i interface{}, args ...BindArg) error + + // CopyTo copies properties into another by override. + CopyTo(out *Properties) error +} + var _ ReadOnlyProperties = (*Properties)(nil) // Properties stores the data with map[string]string and the keys are case-sensitive, @@ -330,54 +357,3 @@ func (p *Properties) Bind(i interface{}, args ...BindArg) error { func (p *Properties) CopyTo(out *Properties) error { return out.merge(p.storage.RawData()) } - -// ReadOnlyProperties is the interface for read-only properties. -type ReadOnlyProperties interface { - - // Data returns key-value pairs of the properties. - Data() map[string]string - - // Keys returns keys of the properties. - Keys() []string - - // Has returns whether the key exists. - Has(key string) bool - - // SubKeys returns the sorted sub keys of the key. - SubKeys(key string) ([]string, error) - - // Get returns key's value, using Def to return a default value. - Get(key string, opts ...GetOption) string - - // Resolve resolves string that contains references. - Resolve(s string) (string, error) - - // Bind binds properties into a value. - Bind(i interface{}, args ...BindArg) error - - // CopyTo copies properties into another by override. - CopyTo(out *Properties) error -} - -// AtomicProperties is a thread-safe version of Properties. -type AtomicProperties struct { - v atomic.Pointer[Properties] -} - -// NewAtomicProperties creates a new atomic properties. -func NewAtomicProperties() *AtomicProperties { - return new(AtomicProperties) -} - -// Load loads as read-only properties. -func (p *AtomicProperties) Load() ReadOnlyProperties { - if s := p.v.Load(); s != nil { - return s - } - return nil -} - -// Store stores a new properties. -func (p *AtomicProperties) Store(v *Properties) { - p.v.Store(v) -} diff --git a/gs/gs.go b/gs/gs.go index 5fbd8935..3ed4b18a 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -23,7 +23,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_core" ) const ( @@ -44,7 +44,7 @@ type ( var boot *gs_app.Boot -func runBoot() error { +func bootRun() error { if boot != nil { if err := boot.Run(); err != nil { return err @@ -69,7 +69,7 @@ var app = gs_app.NewApp() // Start 启动程序。 func Start() (gs.Context, error) { printBanner() - err := runBoot() + err := bootRun() if err != nil { return nil, err } @@ -84,7 +84,7 @@ func Stop() { // Run 启动程序。 func Run() error { printBanner() - err := runBoot() + err := bootRun() if err != nil { return err } @@ -98,12 +98,12 @@ func ShutDown(msg ...string) { // Object 参考 Container.Object 的解释。 func Object(i interface{}) *gs.BeanDefinition { - return app.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) + return app.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return app.Accept(gs_ctx.NewBean(ctor, args...)) + return app.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 43554592..3bb38e22 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -26,7 +26,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_core" ) type AppRunner interface { @@ -40,8 +40,8 @@ type AppServer interface { // App 应用 type App struct { - c *gs_ctx.Container - p *gs_conf.Configuration + c *gs_core.Container + p *gs_conf.AppConfig exitChan chan struct{} @@ -51,22 +51,26 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { app := &App{ - c: gs_ctx.New(), - p: gs_conf.NewConfiguration(), + c: gs_core.New(), + p: gs_conf.NewAppConfig(), exitChan: make(chan struct{}), } app.Object(app) return app } +func (app *App) Config() *gs_conf.AppConfig { + return app.p +} + // Object 参考 Container.Object 的解释。 func (app *App) Object(i interface{}) *gs.BeanDefinition { - return app.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) + return app.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return app.c.Accept(gs_ctx.NewBean(ctor, args...)) + return app.c.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 6bfffc26..f75760e4 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -21,7 +21,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_core" ) type BootRunner interface { @@ -29,27 +29,31 @@ type BootRunner interface { } type Boot struct { - c *gs_ctx.Container - p *gs_conf.Bootstrap + c *gs_core.Container + p *gs_conf.BootConfig } func NewBoot() *Boot { b := &Boot{ - c: gs_ctx.New(), - p: gs_conf.NewBootstrap(), + c: gs_core.New(), + p: gs_conf.NewBootConfig(), } b.c.Object(b) return b } +func (b *Boot) Config() *gs_conf.BootConfig { + return b.p +} + // Object 参考 Container.Object 的解释。 func (b *Boot) Object(i interface{}) *gs.BeanDefinition { - return b.c.Accept(gs_ctx.NewBean(reflect.ValueOf(i))) + return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return b.c.Accept(gs_ctx.NewBean(ctor, args...)) + return b.c.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 diff --git a/gs/internal/gs_conf/conf.go b/gs/internal/gs_conf/conf.go index e786e9cc..fa681c72 100644 --- a/gs/internal/gs_conf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -20,25 +20,51 @@ import ( "fmt" "os" "strings" + "sync/atomic" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/sysconf" ) -// Configuration is a layered configuration manager. -type Configuration struct { +// AtomicProperties is a thread-safe version of Properties. +type AtomicProperties struct { + v atomic.Pointer[conf.Properties] +} + +// NewAtomicProperties creates a new atomic properties. +func NewAtomicProperties() *AtomicProperties { + return new(AtomicProperties) +} + +// Load loads as read-only properties. +func (p *AtomicProperties) Load() conf.ReadOnlyProperties { + if s := p.v.Load(); s != nil { + return s + } + return nil +} + +// Store stores a new properties. +func (p *AtomicProperties) Store(v *conf.Properties) { + p.v.Store(v) +} + +/******************************** AppConfig **********************************/ + +// AppConfig is a layered app configuration. +type AppConfig struct { LocalFile *PropertySources RemoteFile *PropertySources - RemoteProp *conf.AtomicProperties + RemoteProp *AtomicProperties Environment *Environment CommandArgs *CommandArgs } -func NewConfiguration() *Configuration { - return &Configuration{ +func NewAppConfig() *AppConfig { + return &AppConfig{ LocalFile: NewPropertySources(ConfigTypeLocal, "application"), RemoteFile: NewPropertySources(ConfigTypeRemote, "application"), - RemoteProp: conf.NewAtomicProperties(), + RemoteProp: NewAtomicProperties(), Environment: NewEnvironment(), CommandArgs: NewCommandArgs(), } @@ -58,7 +84,7 @@ func merge(out *conf.Properties, sources ...interface { } // Refresh merges all layers into a properties as read-only. -func (c *Configuration) Refresh() (conf.ReadOnlyProperties, error) { +func (c *AppConfig) Refresh() (conf.ReadOnlyProperties, error) { p := sysconf.Clone() err := merge(p, c.Environment, c.CommandArgs) @@ -97,17 +123,17 @@ func (c *Configuration) Refresh() (conf.ReadOnlyProperties, error) { return p, nil } -/******************************** Bootstrap **********************************/ +/******************************** BootConfig *********************************/ -// Bootstrap is a layered bootstrap configuration. -type Bootstrap struct { +// BootConfig is a layered boot configuration. +type BootConfig struct { LocalFile *PropertySources Environment *Environment CommandArgs *CommandArgs } -func NewBootstrap() *Bootstrap { - return &Bootstrap{ +func NewBootConfig() *BootConfig { + return &BootConfig{ LocalFile: NewPropertySources(ConfigTypeLocal, "bootstrap"), Environment: NewEnvironment(), CommandArgs: NewCommandArgs(), @@ -115,7 +141,7 @@ func NewBootstrap() *Bootstrap { } // Refresh merges all layers into a properties as read-only. -func (c *Bootstrap) Refresh() (conf.ReadOnlyProperties, error) { +func (c *BootConfig) Refresh() (conf.ReadOnlyProperties, error) { p := sysconf.Clone() err := merge(p, c.Environment, c.CommandArgs) diff --git a/gs/internal/gs/bean_test.go b/gs/internal/gs_core/bean_test.go similarity index 84% rename from gs/internal/gs/bean_test.go rename to gs/internal/gs_core/bean_test.go index e921c0d0..551b17dc 100644 --- a/gs/internal/gs/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_test +package gs_core_test import ( "fmt" @@ -24,7 +24,7 @@ import ( "testing" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_core" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/stretchr/testify/assert" @@ -32,7 +32,7 @@ import ( // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { - return gs_ctx.NewBean(objOrCtor, ctorArgs...) + return gs_core.NewBean(objOrCtor, ctorArgs...) } // func TestParseSingletonTag(t *testing.T) { @@ -133,75 +133,6 @@ func TestBeanDefinition_Match(t *testing.T) { } } -type Teacher interface { - Course() string -} - -type historyTeacher struct { - name string -} - -func newHistoryTeacher(name string) *historyTeacher { - return &historyTeacher{name: name} -} - -func newTeacher(course string, name string) Teacher { - switch course { - case "history": - return &historyTeacher{name: name} - default: - return nil - } -} - -func (t *historyTeacher) Course() string { - return "history" -} - -type Student struct { - Teacher Teacher - Room string -} - -// 入参可以进行注入或者属性绑定,返回值可以是 struct、map、slice、func 等。 -func NewStudent(teacher Teacher, room string) Student { - return Student{ - Teacher: teacher, - Room: room, - } -} - -// 入参可以进行注入或者属性绑定,返回值可以是 struct、map、slice、func 等。 -func NewPtrStudent(teacher Teacher, room string) *Student { - return &Student{ - Teacher: teacher, - Room: room, - } -} - -type BeanZero struct { - Int int -} - -type BeanOne struct { - Zero *BeanZero `autowire:""` -} - -type BeanTwo struct { - One *BeanOne `autowire:""` -} - -func (t *BeanTwo) Group() { -} - -type BeanThree struct { - One *BeanTwo `autowire:""` -} - -func (t *BeanThree) String() string { - return "" -} - func TestObjectBean(t *testing.T) { // t.Run("bean must be ref type", func(t *testing.T) { @@ -238,7 +169,7 @@ func TestObjectBean(t *testing.T) { newBean(newHistoryTeacher("")): { "historyTeacher", - "github.com/go-spring/spring-core/gs/internal/gs/gs_test.historyTeacher", + "github.com/go-spring/spring-core/gs/internal/gs_core/gs_core_test.historyTeacher", }, newBean(new(pkg2.SamePkg)): { @@ -262,10 +193,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.Type().String(), "*gs_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.Type().String(), "*gs_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -281,7 +212,7 @@ func TestConstructorBean(t *testing.T) { interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.Type().String(), "gs_test.Teacher") + assert.Equal(t, bd.Type().String(), "gs_core_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_ctx/ctx.go b/gs/internal/gs_core/core.go similarity index 99% rename from gs/internal/gs_ctx/ctx.go rename to gs/internal/gs_core/core.go index aeb4e03d..6e7b1546 100755 --- a/gs/internal/gs_ctx/ctx.go +++ b/gs/internal/gs_core/core.go @@ -16,7 +16,7 @@ // Package gs_ctx 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App // 以及全局 App 对象封装三个部分,可以应用于多种使用场景。 -package gs_ctx +package gs_core import ( "bytes" diff --git a/gs/internal/gs_ctx/ctx_test.go b/gs/internal/gs_core/core_test.go similarity index 96% rename from gs/internal/gs_ctx/ctx_test.go rename to gs/internal/gs_core/core_test.go index 71de0903..721e941e 100755 --- a/gs/internal/gs_ctx/ctx_test.go +++ b/gs/internal/gs_core/core_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_ctx_test +package gs_core_test import ( "encoding/json" @@ -33,7 +33,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" - "github.com/go-spring/spring-core/gs/internal/gs_ctx" + "github.com/go-spring/spring-core/gs/internal/gs_core" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" @@ -42,7 +42,7 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c *gs_ctx.Container, fn func(gs.Context)) error { +func runTest(c *gs_core.Container, fn func(gs.Context)) error { type PandoraAware struct{} c.Provide(func(p gs.Context) PandoraAware { fn(p) @@ -67,7 +67,7 @@ func TestApplicationContext(t *testing.T) { // 自定义数据类型 t.Run("pkg1.SamePkg", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() e := pkg1.SamePkg{} // assert.Panic(t, func() { @@ -83,7 +83,7 @@ func TestApplicationContext(t *testing.T) { }) t.Run("pkg2.SamePkg", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() e := pkg2.SamePkg{} // assert.Panic(t, func() { @@ -143,7 +143,7 @@ type TestObject struct { func TestApplicationContext_AutoWireBeans(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() obj := &TestObject{} c.Object(obj) @@ -202,7 +202,7 @@ type Setting struct { } func TestApplicationContext_ValueTag(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() p := conf.New() p.Set("int", int(3)) @@ -270,7 +270,7 @@ func (s *PrototypeBeanService) Service(name string) { } func TestApplicationContext_PrototypeBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() greetingService := &GreetingService{} c.Object(greetingService) @@ -332,7 +332,7 @@ type DbConfig struct { func TestApplicationContext_TypeConverter(t *testing.T) { prop, _ := conf.Load("../../testdata/config/application.yaml") - c := gs_ctx.New() + c := gs_core.New() b := &EnvEnumBean{} c.Object(b) @@ -378,7 +378,7 @@ type ProxyGrouper struct { } func TestApplicationContext_NestedBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(MyGrouper)).Export((*Grouper)(nil)) c.Object(new(ProxyGrouper)) err := c.Refresh() @@ -395,7 +395,7 @@ type SamePkgHolder struct { } func TestApplicationContext_SameNameBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(SamePkgHolder)) c.Object(&pkg1.SamePkg{}).Export((*Pkg)(nil)) c.Object(&pkg2.SamePkg{}).Export((*Pkg)(nil)) @@ -419,11 +419,11 @@ func (d *DiffPkgTwo) Package() { type DiffPkgHolder struct { // Pkg `autowire:"same"` // 如果两个 Object 不小心重名了,也会找到多个符合条件的 Object - Pkg `autowire:"github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.DiffPkgTwo:same"` + Pkg `autowire:"github.com/go-spring/spring-core/gs/internal/gs_core/gs_core_test.DiffPkgTwo:same"` } func TestApplicationContext_DiffNameBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&DiffPkgOne{}).Name("same").Export((*Pkg)(nil)) c.Object(&DiffPkgTwo{}).Name("same").Export((*Pkg)(nil)) c.Object(new(DiffPkgHolder)) @@ -433,7 +433,7 @@ func TestApplicationContext_DiffNameBean(t *testing.T) { func TestApplicationContext_LoadProperties(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() prop, _ := conf.Load("../../testdata/config/application.yaml") p, _ := conf.Load("../../testdata/config/application.properties") @@ -477,7 +477,7 @@ func (t *BeanThree) String() string { func TestApplicationContext_Get(t *testing.T) { t.Run("panic", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() err := runTest(c, func(p gs.Context) { { var s fmt.Stringer @@ -494,7 +494,7 @@ func TestApplicationContext_Get(t *testing.T) { }) t.Run("success", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).Export((*Grouper)(nil)) @@ -532,10 +532,10 @@ func TestApplicationContext_Get(t *testing.T) { err = p.Get(&grouper, ":BeanTwo") assert.Nil(t, err) - err = p.Get(&two, "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.BeanTwo:BeanTwo") + err = p.Get(&two, "github.com/go-spring/spring-core/gs/internal/gs_core/gs_core_test.BeanTwo:BeanTwo") assert.Nil(t, err) - err = p.Get(&grouper, "github.com/go-spring/spring-core/gs/internal/gs_ctx/gs_ctx_test.BeanTwo:BeanTwo") + err = p.Get(&grouper, "github.com/go-spring/spring-core/gs/internal/gs_core/gs_core_test.BeanTwo:BeanTwo") assert.Nil(t, err) err = p.Get(&two, "xxx:BeanTwo") @@ -646,7 +646,7 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { prop := conf.New() prop.Set("room", "Class 3 Grade 1") - c := gs_ctx.New() + c := gs_core.New() // 用接口注册时实际使用的是原始类型 c.Object(Teacher(newHistoryTeacher(""))).Export((*Teacher)(nil)) @@ -700,7 +700,7 @@ func TestApplicationContext_RegisterBeanFn(t *testing.T) { func TestApplicationContext_Profile(t *testing.T) { t.Run("bean:_c:", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) err := runTest(c, func(p gs.Context) { var b *BeanZero @@ -714,7 +714,7 @@ func TestApplicationContext_Profile(t *testing.T) { prop := conf.New() prop.Set("spring.profiles.active", "test") - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.RefreshProperties(prop) @@ -732,7 +732,7 @@ type BeanFour struct{} func TestApplicationContext_DependsOn(t *testing.T) { t.Run("random", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)) @@ -747,7 +747,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { "github.com/go-spring/spring-core/gs/gs_test.BeanZero:BeanZero", } - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanFour)).DependsOn(dependsOn...) @@ -759,7 +759,7 @@ func TestApplicationContext_DependsOn(t *testing.T) { func TestApplicationContext_Primary(t *testing.T) { t.Run("duplicate", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}) c.Object(new(BeanOne)) @@ -769,7 +769,7 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("duplicate", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) // primary 是在多个候选 bean 里面选择,而不是允许同名同类型的两个 bean c.Object(&BeanZero{6}).Primary() @@ -780,7 +780,7 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("not primary", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)) @@ -794,7 +794,7 @@ func TestApplicationContext_Primary(t *testing.T) { }) t.Run("primary", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(&BeanZero{6}).Name("zero_6").Primary() c.Object(new(BeanOne)) @@ -814,7 +814,7 @@ type FuncObj struct { } func TestDefaultProperties_WireFunc(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(func(int) int { return 6 }) obj := new(FuncObj) c.Object(obj) @@ -862,7 +862,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { prop := conf.New() prop.Set("manager.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewPtrManager) c.RefreshProperties(prop) @@ -885,7 +885,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { prop := conf.New() prop.Set("manager.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.RefreshProperties(prop) bd := c.Provide(NewManager) @@ -907,18 +907,18 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { t.Run("manager return error", func(t *testing.T) { prop := conf.New() prop.Set("manager.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewManagerRetError) c.RefreshProperties(prop) err := c.Refresh() - assert.Error(t, err, "ctx_test.go:\\d* error") + assert.Error(t, err, "core_test.go:\\d* error") }) t.Run("manager return error nil", func(t *testing.T) { prop := conf.New() prop.Set("manager.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewManagerRetErrorNil) c.RefreshProperties(prop) @@ -930,7 +930,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { prop := conf.New() prop.Set("manager.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewNullPtrManager) c.RefreshProperties(prop) @@ -988,7 +988,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(callDestroy)).Init((*callDestroy).Init) err := runTest(c, func(p gs.Context) { var d *callDestroy @@ -1002,7 +1002,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call init with error", func(t *testing.T) { { - c := gs_ctx.New() + c := gs_core.New() c.Object(&callDestroy{i: 1}).Init((*callDestroy).InitWithError) err := c.Refresh() assert.Error(t, err, "error") @@ -1011,7 +1011,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { prop := conf.New() prop.Set("int", 0) - c := gs_ctx.New() + c := gs_core.New() c.Object(&callDestroy{}).Init((*callDestroy).InitWithError) c.RefreshProperties(prop) @@ -1025,7 +1025,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call interface init", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Provide(func() destroyable { return new(callDestroy) }).Init(destroyable.Init) err := runTest(c, func(p gs.Context) { var d destroyable @@ -1039,7 +1039,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { t.Run("call interface init with error", func(t *testing.T) { { - c := gs_ctx.New() + c := gs_core.New() c.Provide(func() destroyable { return &callDestroy{i: 1} }).Init(destroyable.InitWithError) err := c.Refresh() assert.Error(t, err, "error") @@ -1048,7 +1048,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { prop := conf.New() prop.Set("int", 0) - c := gs_ctx.New() + c := gs_core.New() c.Provide(func() destroyable { return &callDestroy{} }).Init(destroyable.InitWithError) c.RefreshProperties(prop) @@ -1062,7 +1062,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested init", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(nestedCallDestroy)).Init((*nestedCallDestroy).Init) err := runTest(c, func(p gs.Context) { var d *nestedCallDestroy @@ -1074,7 +1074,7 @@ func TestRegisterBean_InitFunc(t *testing.T) { }) t.Run("call nested interface init", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&nestedDestroyable{ destroyable: new(callDestroy), }).Init((*nestedDestroyable).Init) @@ -1096,7 +1096,7 @@ func TestApplicationContext_ValueBincoreng(t *testing.T) { prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(RecoresCluster)) c.RefreshProperties(prop) @@ -1112,7 +1112,7 @@ func TestApplicationContext_ValueBincoreng(t *testing.T) { func TestApplicationContext_Collect(t *testing.T) { t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&struct { Events []ServerInterface `autowire:""` }{}) @@ -1121,7 +1121,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() err := runTest(c, func(ctx gs.Context) { var Events []ServerInterface err := ctx.Get(&Events) @@ -1131,7 +1131,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&struct { Events []ServerInterface `autowire:"?"` }{}) @@ -1140,7 +1140,7 @@ func TestApplicationContext_Collect(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() err := runTest(c, func(ctx gs.Context) { var Events []ServerInterface err := ctx.Get(&Events, "?") @@ -1153,7 +1153,7 @@ func TestApplicationContext_Collect(t *testing.T) { prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(RecoresCluster)).Name("one") c.Object(new(RecoresCluster)) @@ -1171,7 +1171,7 @@ func TestApplicationContext_Collect(t *testing.T) { prop := conf.New() prop.Set("redis.endpoints", "redis://localhost:6379") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(RecoresCluster)).Name("a").Order(1) c.Object(new(RecoresCluster)).Name("b").Order(2) @@ -1299,7 +1299,7 @@ func TestOptionConstructorArg(t *testing.T) { prop := conf.New() prop.Set("president", "CaiYuanPei") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom) c.RefreshProperties(prop) @@ -1318,7 +1318,7 @@ func TestOptionConstructorArg(t *testing.T) { prop := conf.New() prop.Set("president", "CaiYuanPei") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}")) c.RefreshProperties(prop) @@ -1339,7 +1339,7 @@ func TestOptionConstructorArg(t *testing.T) { prop.Set("class_name", "二年级03班") prop.Set("president", "CaiYuanPei") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom, gs_arg.Option(withStudents)) c.Object(new(Student)).Name("Student1") c.Object(new(Student)).Name("Student2") @@ -1362,7 +1362,7 @@ func TestOptionConstructorArg(t *testing.T) { prop.Set("class_name", "二年级06班") prop.Set("president", "CaiYuanPei") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom, gs_arg.Option(withStudents), gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), @@ -1434,7 +1434,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Object(new(Server)) bd := c.Provide((*Server).Consumer, parent) @@ -1461,7 +1461,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Object(new(Server)).On(gs_cond.Not(gs_cond.OK())) bd := c.Provide((*Server).Consumer, parent) @@ -1484,7 +1484,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Object(new(Server)) c.Provide((*Server).ConsumerArg, parent, "${i:=9}") @@ -1510,7 +1510,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Provide(NewServerInterface) c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) @@ -1564,7 +1564,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) @@ -1582,7 +1582,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(Server)) c.RefreshProperties(prop) @@ -1599,7 +1599,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*Server)(nil)) @@ -1625,7 +1625,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, (*int)(nil)) @@ -1638,7 +1638,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "Server") @@ -1664,7 +1664,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() c.Object(new(Server)) c.Provide(func(s *Server) *Consumer { return s.Consumer() }, "NULL") @@ -1685,7 +1685,7 @@ func TestApplicationContext_UserDefinedTypeProperty(t *testing.T) { Complex complex64 // `value:"${complex}"` } - c := gs_ctx.New() + c := gs_core.New() conf.RegisterConverter(func(v string) (level, error) { if v == "debug" { @@ -1725,7 +1725,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { // 直接创建的 Object 直接发生循环依赖是没有关系的。 t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Object(new(CircleC)) @@ -1734,7 +1734,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(CircleA)) c.Object(new(CircleB)) c.Provide(func() *CircleC { @@ -1745,7 +1745,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(CircleA)) c.Provide(func() *CircleB { return new(CircleB) @@ -1758,7 +1758,7 @@ func TestApplicationContext_CircleAutowire(t *testing.T) { }) t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Provide(func(b *CircleB) *CircleA { return new(CircleA) }) @@ -1840,7 +1840,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { prop := conf.New() prop.Set("var.obj", "description") - c := gs_ctx.New() + c := gs_core.New() c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewNilVarObj, gs_arg.Nil()) @@ -1860,7 +1860,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { prop := conf.New() prop.Set("var.obj", "description") - c := gs_ctx.New() + c := gs_core.New() c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, "${var.obj}", gs_arg.Option(withVar, "v1")) @@ -1881,7 +1881,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { prop := conf.New() prop.Set("var.obj", "description") - c := gs_ctx.New() + c := gs_core.New() c.Object(&Var{"v1"}).Name("v1") c.Object(&Var{"v2"}).Name("v2") c.Provide(NewVarObj, gs_arg.Value("description"), gs_arg.Option(withVar, "v1", "v2")) @@ -1900,7 +1900,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1")) @@ -1914,7 +1914,7 @@ func TestApplicationContext_RegisterOptionBean(t *testing.T) { }) t.Run("variable option interface param 1", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&Var{"v1"}).Name("v1").Export((*interface{})(nil)) c.Object(&Var{"v2"}).Name("v2").Export((*interface{})(nil)) c.Provide(NewVarInterfaceObj, gs_arg.Option(withVarInterface, "v1", "v2")) @@ -1956,7 +1956,7 @@ func TestApplicationContext_Close(t *testing.T) { t.Run("call destroy fn", func(t *testing.T) { called := false - c := gs_ctx.New() + c := gs_core.New() c.Object(func() {}).Destroy(func(f func()) { called = true }) err := c.Refresh() assert.Nil(t, err) @@ -1966,7 +1966,7 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call destroy", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() d := new(callDestroy) c.Object(d).Destroy((*callDestroy).Destroy) err := runTest(c, func(p gs.Context) { @@ -1983,7 +1983,7 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gs_ctx.New() + c := gs_core.New() d := &callDestroy{i: 1} c.Object(d).Destroy((*callDestroy).DestroyWithError) err := runTest(c, func(p gs.Context) { @@ -1998,7 +1998,7 @@ func TestApplicationContext_Close(t *testing.T) { // nil { - c := gs_ctx.New() + c := gs_core.New() d := &callDestroy{} c.Object(d).Destroy((*callDestroy).DestroyWithError) err := runTest(c, func(p gs.Context) { @@ -2013,7 +2013,7 @@ func TestApplicationContext_Close(t *testing.T) { }) t.Run("call interface destroy", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() bd := c.Provide(func() destroyable { return new(callDestroy) }).Destroy(destroyable.Destroy) err := runTest(c, func(p gs.Context) { var d destroyable @@ -2030,7 +2030,7 @@ func TestApplicationContext_Close(t *testing.T) { // error { - c := gs_ctx.New() + c := gs_core.New() bd := c.Provide(func() destroyable { return &callDestroy{i: 1} }).Destroy(destroyable.DestroyWithError) err := runTest(c, func(p gs.Context) { var d destroyable @@ -2048,7 +2048,7 @@ func TestApplicationContext_Close(t *testing.T) { prop := conf.New() prop.Set("int", 0) - c := gs_ctx.New() + c := gs_core.New() bd := c.Provide(func() destroyable { return &callDestroy{} }).Destroy(destroyable.DestroyWithError) c.RefreshProperties(prop) @@ -2077,7 +2077,7 @@ type PtrNestedAutowireBean struct { } func TestApplicationContext_NestedAutowireBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(NestedAutowireBean)) c.Object(&PtrNestedAutowireBean{ SubNestedAutowireBean: new(SubNestedAutowireBean), @@ -2123,7 +2123,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { prop.Set("sdk.wx.auto-create", true) prop.Set("sdk.wx.enable", true) - c := gs_ctx.New() + c := gs_core.New() c.Object(new(wxChannel)) c.RefreshProperties(prop) @@ -2142,7 +2142,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { prop.Set("sdk.wx.auto-create", true) prop.Set("sdk.wx.enable", true) - c := gs_ctx.New() + c := gs_core.New() c.Object(new(WXChannel)) c.RefreshProperties(prop) @@ -2160,7 +2160,7 @@ func TestApplicationContext_NestValueField(t *testing.T) { func TestApplicationContext_FnArgCollectBean(t *testing.T) { t.Run("interface type", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Provide(newHistoryTeacher("t1")).Name("t1").Export((*Teacher)(nil)) c.Provide(newHistoryTeacher("t2")).Name("t2").Export((*Teacher)(nil)) c.Provide(func(teachers []Teacher) func() { @@ -2191,7 +2191,7 @@ func (_ *filterImpl) Filter(input string) string { func TestApplicationContext_BeanCache(t *testing.T) { t.Run("not implement interface", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(func() {}).Export((*filter)(nil)) err := c.Refresh() assert.Error(t, err, "doesn't implement interface gs_test.filter") @@ -2204,7 +2204,7 @@ func TestApplicationContext_BeanCache(t *testing.T) { F2 filter `autowire:"f2"` } - c := gs_ctx.New() + c := gs_core.New() c.Provide(func() filter { return new(filterImpl) }).Name("f1") c.Object(new(filterImpl)).Export((*filter)(nil)).Name("f2") c.Object(&server) @@ -2225,7 +2225,7 @@ func (i Integer) Value() int { } func TestApplicationContext_IntInterface(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Provide(func() IntInterface { return Integer(5) }) err := c.Refresh() assert.Nil(t, err) @@ -2252,7 +2252,7 @@ func TestApplicationContext_Properties(t *testing.T) { t.Run("array properties", func(t *testing.T) { b := new(ArrayProperties) - c := gs_ctx.New() + c := gs_core.New() c.Object(b) err := c.Refresh() assert.Nil(t, err) @@ -2273,7 +2273,7 @@ func TestApplicationContext_Properties(t *testing.T) { prop.Set("int_a", "3") prop.Set("int_b", "4") - c := gs_ctx.New() + c := gs_core.New() c.Object(&obj) c.RefreshProperties(prop) @@ -2306,7 +2306,7 @@ func TestApplicationContext_Destroy(t *testing.T) { destroyIndex := 0 destroyArray := []int{0, 0, 0, 0} - c := gs_ctx.New() + c := gs_core.New() c.Object(new(FirstDestroy)).Destroy( func(_ *FirstDestroy) { fmt.Println("::FirstDestroy") @@ -2347,7 +2347,7 @@ type ObjFactory struct{} func (factory *ObjFactory) NewObj(i int) *Obj { return &Obj{i: i} } func TestApplicationContext_CreateBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&ObjFactory{}) err := runTest(c, func(p gs.Context) { b, err := p.Wire((*ObjFactory).NewObj, gs_arg.R1("${i:=5}")) @@ -2360,7 +2360,7 @@ func TestDefaultSpringContext(t *testing.T) { t.Run("bean:test_ctx:", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}).On(gs_cond. OnProfile("test"). @@ -2382,7 +2382,7 @@ func TestDefaultSpringContext(t *testing.T) { prop := conf.New() prop.Set("spring.profiles.active", "test") - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) c.RefreshProperties(prop) @@ -2398,7 +2398,7 @@ func TestDefaultSpringContext(t *testing.T) { prop := conf.New() prop.Set("spring.profiles.active", "stable") - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}).On(gs_cond.OnProfile("test")) c.RefreshProperties(prop) @@ -2416,7 +2416,7 @@ func TestDefaultSpringContext(t *testing.T) { prop.Set("president", "CaiYuanPei") prop.Set("class_floor", 2) - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}", @@ -2440,7 +2440,7 @@ func TestDefaultSpringContext(t *testing.T) { prop.Set("president", "CaiYuanPei") onProperty := gs_cond.OnProperty("class_name_enable") - c := gs_ctx.New() + c := gs_core.New() c.Provide(NewClassRoom, gs_arg.Option(withClassName, "${class_name:=二年级03班}", @@ -2465,7 +2465,7 @@ func TestDefaultSpringContext(t *testing.T) { prop := conf.New() prop.Set("server.version", "1.0.0") - c := gs_ctx.New() + c := gs_core.New() parent := c.Object(new(Server)) c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) @@ -2504,7 +2504,7 @@ func TestDefaultSpringContext(t *testing.T) { // } func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c1 := gs_cond.OnProperty("null", gs_cond.MatchIfMissing()).Or().OnProfile("test") @@ -2528,7 +2528,7 @@ func TestDefaultSpringContext_ConditionOnBean(t *testing.T) { func TestDefaultSpringContext_ConditionOnMissingBean(t *testing.T) { for i := 0; i < 20; i++ { // 测试 Find 无需绑定,不要排序 - c := gs_ctx.New() + c := gs_core.New() c.Object(&BeanZero{5}) c.Object(new(BeanOne)) c.Object(new(BeanTwo)).On(gs_cond.OnMissingBean("BeanOne")) @@ -2732,7 +2732,7 @@ func TestApplicationContext_Invoke(t *testing.T) { prop := conf.New() prop.Set("version", "v0.0.1") - c := gs_ctx.New() + c := gs_core.New() c.Object(func() {}) c.RefreshProperties(prop) @@ -2749,7 +2749,7 @@ func TestApplicationContext_Invoke(t *testing.T) { prop.Set("version", "v0.0.1") prop.Set("spring.profiles.active", "dev") - c := gs_ctx.New() + c := gs_core.New() c.Object(func() {}) c.RefreshProperties(prop) @@ -2769,7 +2769,7 @@ type emptyStructB struct{} func TestEmptyStruct(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() objA := &emptyStructA{} c.Object(objA) objB := &emptyStructB{} @@ -2789,7 +2789,7 @@ func TestMapCollection(t *testing.T) { } t.Run("", func(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&mapValue{"a"}).Name("a").Order(1) c.Object(&mapValue{"b"}).Name("b").Order(2) c.Object(&mapValue{"c"}).Name("c").On(gs_cond.Not(gs_cond.OK())) @@ -2830,7 +2830,7 @@ func TestLazy(t *testing.T) { prop := conf.New() prop.Set("spring.main.allow-circular-references", "true") - c := gs_ctx.New() + c := gs_core.New() c.Provide(newCircularA) c.Provide(newCircularB) d := struct { @@ -2872,7 +2872,7 @@ func (t *table) OnDestroy() { } func TestDestroyDependence(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(new(memory)) c.Object(new(table)).Name("aaa") c.Object(new(table)).Name("bbb") @@ -2881,7 +2881,7 @@ func TestDestroyDependence(t *testing.T) { } type ContextAware struct { - gs_ctx.ContextAware + gs_core.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -2892,7 +2892,7 @@ func TestContextAware(t *testing.T) { prop := conf.New() prop.Set("prefix", "hello") - c := gs_ctx.New() + c := gs_core.New() b := c.Object(new(ContextAware)) c.RefreshProperties(prop) @@ -2932,7 +2932,7 @@ func TestDynamic(t *testing.T) { cfg := new(DynamicConfig) wrapper := new(DynamicConfigWrapper) - c := gs_ctx.New() + c := gs_core.New() c.Object(cfg) c.Object(wrapper) err := c.Refresh() @@ -3037,7 +3037,7 @@ func (c *ConfigurationBean) NewChild() *ChildBean { } func TestConfiguration(t *testing.T) { - c := gs_ctx.New() + c := gs_core.New() c.Object(&ConfigurationBean{"123"}).Configuration(nil, nil).Name("123") c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") if err := c.Refresh(); err != nil { diff --git a/gs/internal/gs_ctx/util.go b/gs/internal/gs_core/util.go similarity index 99% rename from gs/internal/gs_ctx/util.go rename to gs/internal/gs_core/util.go index 56d82611..87e802ee 100644 --- a/gs/internal/gs_ctx/util.go +++ b/gs/internal/gs_core/util.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_ctx +package gs_core import ( "container/list" From d6a110a6475cc51f8368566201c5dc6ac4d4fbaa Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 4 Jan 2025 09:38:05 +0800 Subject: [PATCH 30/75] code refactor --- conf/conf.go | 9 +- gs/internal/gs/bean.go | 12 +- gs/internal/gs_core/bean.go | 110 ++++ gs/internal/gs_core/core.go | 1005 +++-------------------------------- gs/internal/gs_core/wire.go | 773 +++++++++++++++++++++++++++ 5 files changed, 974 insertions(+), 935 deletions(-) create mode 100644 gs/internal/gs_core/bean.go create mode 100644 gs/internal/gs_core/wire.go diff --git a/conf/conf.go b/conf/conf.go index 8d3a207c..df535a24 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -37,7 +37,7 @@ import ( var ( readers = map[string]Reader{} splitters = map[string]Splitter{} - converters = map[reflect.Type]Converter{} + converters = map[reflect.Type]interface{}{} ) func init() { @@ -76,15 +76,12 @@ func RegisterSplitter(name string, fn Splitter) { // Converter converts string value into user-defined value. It should be function // type, and its prototype is func(string)(type,error). -type Converter interface{} +type Converter[T any] func(string) (T, error) // RegisterConverter registers its converter for non-primitive type such as // time.Time, time.Duration, or other user-defined value type. -func RegisterConverter(fn Converter) { +func RegisterConverter[T any](fn Converter[T]) { t := reflect.TypeOf(fn) - if !IsConverter(t) { - panic(errors.New("converter is func(string)(type,error)")) - } converters[t.Out(0)] = fn } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index ecc6d2f4..43ff3e02 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -238,14 +238,6 @@ func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { // Export 设置 bean 的导出接口。 func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { - err := d.export(exports...) - if err != nil { - panic(err) - } - return d -} - -func (d *BeanDefinition) export(exports ...interface{}) error { for _, o := range exports { t, ok := o.(reflect.Type) if !ok { @@ -255,7 +247,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { } } if t.Kind() != reflect.Interface { - return errors.New("only interface type can be exported") + panic(errors.New("only interface type can be exported")) } exported := false for _, export := range d.exports { @@ -269,7 +261,7 @@ func (d *BeanDefinition) export(exports ...interface{}) error { } d.exports = append(d.exports, t) } - return nil + return d } // Configuration 设置 bean 为配置类。 diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go new file mode 100644 index 00000000..a6001c26 --- /dev/null +++ b/gs/internal/gs_core/bean.go @@ -0,0 +1,110 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gs_core + +import ( + "errors" + "fmt" + "reflect" + "runtime" + "strings" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_arg" +) + +// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 +func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { + + var v reflect.Value + var fromValue bool + var method bool + var name string + + switch i := objOrCtor.(type) { + case reflect.Value: + fromValue = true + v = i + default: + v = reflect.ValueOf(i) + } + + t := v.Type() + if !gs.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + if !v.IsValid() || v.IsNil() { + panic(errors.New("bean can't be nil")) + } + + const skip = 2 + var f gs.Callable + _, file, line, _ := runtime.Caller(skip) + + // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 + if !fromValue && t.Kind() == reflect.Func { + + if !gs.IsConstructor(t) { + t1 := "func(...)bean" + t2 := "func(...)(bean, error)" + panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) + } + + var err error + f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) + if err != nil { + panic(err) + } + + out0 := t.Out(0) + v = reflect.New(out0) + if gs.IsBeanType(out0) { + v = v.Elem() + } + + t = v.Type() + if !gs.IsBeanType(t) { + panic(errors.New("bean must be ref type")) + } + + // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 + fnPtr := reflect.ValueOf(objOrCtor).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + funcName := fnInfo.Name() + name = funcName[strings.LastIndex(funcName, "/")+1:] + name = name[strings.Index(name, ".")+1:] + if name[0] == '(' { + name = name[strings.Index(name, ".")+1:] + } + method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 + } + + if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { + panic(errors.New("bean should be *val but not *ref")) + } + + // Type.String() 一般返回 *pkg.Type 形式的字符串, + // 我们只取最后的类型名,如有需要请自定义 bean 名称。 + if name == "" { + s := strings.Split(t.String(), ".") + name = strings.TrimPrefix(s[len(s)-1], "*") + } + + return gs.NewBean(t, v, f, name, method, file, line) +} diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 6e7b1546..762089ec 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -19,14 +19,11 @@ package gs_core import ( - "bytes" - "container/list" "context" "errors" "fmt" "reflect" "regexp" - "runtime" "sort" "strings" "sync" @@ -55,10 +52,6 @@ const ( Refreshed // 已刷新 ) -var ( - contextType = reflect.TypeOf((*gs.Context)(nil)).Elem() -) - // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { GSContext gs.Context `autowire:""` @@ -100,87 +93,6 @@ func New() *Container { return c } -// NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { - - var v reflect.Value - var fromValue bool - var method bool - var name string - - switch i := objOrCtor.(type) { - case reflect.Value: - fromValue = true - v = i - default: - v = reflect.ValueOf(i) - } - - t := v.Type() - if !gs.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - if !v.IsValid() || v.IsNil() { - panic(errors.New("bean can't be nil")) - } - - const skip = 2 - var f gs.Callable - _, file, line, _ := runtime.Caller(skip) - - // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 - if !fromValue && t.Kind() == reflect.Func { - - if !gs.IsConstructor(t) { - t1 := "func(...)bean" - t2 := "func(...)(bean, error)" - panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) - } - - var err error - f, err = gs_arg.Bind(objOrCtor, ctorArgs, skip) - if err != nil { - panic(err) - } - - out0 := t.Out(0) - v = reflect.New(out0) - if gs.IsBeanType(out0) { - v = v.Elem() - } - - t = v.Type() - if !gs.IsBeanType(t) { - panic(errors.New("bean must be ref type")) - } - - // 成员方法一般是 xxx/gs_test.(*Server).Consumer 形式命名 - fnPtr := reflect.ValueOf(objOrCtor).Pointer() - fnInfo := runtime.FuncForPC(fnPtr) - funcName := fnInfo.Name() - name = funcName[strings.LastIndex(funcName, "/")+1:] - name = name[strings.Index(name, ".")+1:] - if name[0] == '(' { - name = name[strings.Index(name, ".")+1:] - } - method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 - } - - if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { - panic(errors.New("bean should be *val but not *ref")) - } - - // Type.String() 一般返回 *pkg.Type 形式的字符串, - // 我们只取最后的类型名,如有需要请自定义 bean 名称。 - if name == "" { - s := strings.Split(t.String(), ".") - name = strings.TrimPrefix(s[len(s)-1], "*") - } - - return gs.NewBean(t, v, f, name, method, file, line) -} - // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Object(i interface{}) *gs.BeanDefinition { return c.Accept(NewBean(reflect.ValueOf(i))) @@ -228,129 +140,6 @@ func (c *Container) Bind(i interface{}, opts ...conf.BindArg) error { return c.p.Data().Bind(i, opts...) } -// destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 -type destroyer struct { - current *gs.BeanDefinition - earlier []*gs.BeanDefinition -} - -func (d *destroyer) foundEarlier(b *gs.BeanDefinition) bool { - for _, c := range d.earlier { - if c == b { - return true - } - } - return false -} - -// after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 -func (d *destroyer) after(b *gs.BeanDefinition) { - if d.foundEarlier(b) { - return - } - d.earlier = append(d.earlier, b) -} - -// getBeforeDestroyers 获取排在 i 前面的 destroyer,用于 sort.Triple 排序。 -func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { - d := i.(*destroyer) - result := list.New() - for e := destroyers.Front(); e != nil; e = e.Next() { - c := e.Value.(*destroyer) - if d.foundEarlier(c.current) { - result.PushBack(c) - } - } - return result -} - -type lazyField struct { - v reflect.Value - path string - tag string -} - -// wiringStack 记录 bean 的注入路径。 -type wiringStack struct { - destroyers *list.List - destroyerMap map[string]*destroyer - beans []*gs.BeanDefinition - lazyFields []lazyField -} - -func newWiringStack() *wiringStack { - return &wiringStack{ - destroyers: list.New(), - destroyerMap: make(map[string]*destroyer), - } -} - -// pushBack 添加一个即将注入的 bean 。 -func (s *wiringStack) pushBack(b *gs.BeanDefinition) { - // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) - s.beans = append(s.beans, b) -} - -// popBack 删除一个已经注入的 bean 。 -func (s *wiringStack) popBack() { - n := len(s.beans) - // b := s.beans[n-1] - s.beans = s.beans[:n-1] - // s.logger.Tracef("pop %s %s", b, getStatusString(b.status)) -} - -// path 返回 bean 的注入路径。 -func (s *wiringStack) path() (path string) { - for _, b := range s.beans { - path += fmt.Sprintf("=> %s ↩\n", b) - } - return path[:len(path)-1] -} - -// saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 -func (s *wiringStack) saveDestroyer(b *gs.BeanDefinition) *destroyer { - d, ok := s.destroyerMap[b.ID()] - if !ok { - d = &destroyer{current: b} - s.destroyerMap[b.ID()] = d - } - return d -} - -// sortDestroyers 对具有销毁函数的 bean 按照销毁函数的依赖顺序进行排序。 -func (s *wiringStack) sortDestroyers() []func() { - - destroy := func(v reflect.Value, fn interface{}) func() { - return func() { - if fn == nil { - v.Interface().(BeanDestroy).OnDestroy() - } else { - fnValue := reflect.ValueOf(fn) - out := fnValue.Call([]reflect.Value{v}) - if len(out) > 0 && !out[0].IsNil() { - // s.logger.Error(out[0].Interface().(error)) - } - } - } - } - - destroyers := list.New() - for _, d := range s.destroyerMap { - destroyers.PushBack(d) - } - destroyers = TripleSort(destroyers, getBeforeDestroyers) - - var ret []func() - for e := destroyers.Front(); e != nil; e = e.Next() { - d := e.Value.(*destroyer).current - ret = append(ret, destroy(d.Value(), d.GetDestroy())) - } - return ret -} - -func (c *Container) clear() { -} - func (c *Container) RefreshProperties(p conf.ReadOnlyProperties) error { return c.p.Refresh(p) } @@ -364,6 +153,7 @@ func (c *Container) Refresh() (err error) { // start := time.Now() + // 处理 group 逻辑 for _, fn := range c.groupFuncs { var beans []*gs.BeanDefinition beans, err = fn(c.p.Data()) @@ -373,71 +163,18 @@ func (c *Container) Refresh() (err error) { c.beans = append(c.beans, beans...) } - var newBeans []*gs.BeanDefinition + // 处理 configuration 逻辑 for _, bd := range c.beans { if !bd.IsConfiguration() { continue } - var ( - includes []*regexp.Regexp - excludes []*regexp.Regexp - ) - ss := bd.GetIncludeMethod() - if len(ss) == 0 { - ss = []string{"New*"} - } - for _, s := range ss { - var x *regexp.Regexp - x, err = regexp.Compile(s) - if err != nil { - return err - } - includes = append(includes, x) - } - ss = bd.GetExcludeMethod() - for _, s := range ss { - var x *regexp.Regexp - x, err = regexp.Compile(s) - if err != nil { - return err - } - excludes = append(excludes, x) - } - n := bd.Type().NumMethod() - for i := 0; i < n; i++ { - m := bd.Type().Method(i) - skip := false - for _, x := range excludes { - if x.MatchString(m.Name) { - skip = true - break - } - } - if skip { - continue - } - for _, x := range includes { - if x.MatchString(m.Name) { - var f gs.Callable - f, err = gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd}, 0) - if err != nil { - return err - } - name := bd.GetName() + "_" + m.Name - out0 := m.Type.Out(0) - v := reflect.New(out0) - if gs.IsBeanType(out0) { - v = v.Elem() - } - t := v.Type() - b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) - newBeans = append(newBeans, b) - break - } - } + var newBeans []*gs.BeanDefinition + newBeans, err = c.scanConfiguration(bd) + if err != nil { + return err } + c.beans = append(c.beans, newBeans...) } - c.beans = append(c.beans, newBeans...) c.state = Refreshing @@ -518,6 +255,69 @@ func (c *Container) Refresh() (err error) { return nil } +func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefinition, error) { + var ( + includes []*regexp.Regexp + excludes []*regexp.Regexp + ) + ss := bd.GetIncludeMethod() + if len(ss) == 0 { + ss = []string{"New*"} + } + for _, s := range ss { + var x *regexp.Regexp + x, err := regexp.Compile(s) + if err != nil { + return nil, err + } + includes = append(includes, x) + } + ss = bd.GetExcludeMethod() + for _, s := range ss { + var x *regexp.Regexp + x, err := regexp.Compile(s) + if err != nil { + return nil, err + } + excludes = append(excludes, x) + } + var newBeans []*gs.BeanDefinition + n := bd.Type().NumMethod() + for i := 0; i < n; i++ { + m := bd.Type().Method(i) + skip := false + for _, x := range excludes { + if x.MatchString(m.Name) { + skip = true + break + } + } + if skip { + continue + } + for _, x := range includes { + if x.MatchString(m.Name) { + var f gs.Callable + f, err := gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd}, 0) + if err != nil { + return nil, err + } + name := bd.GetName() + "_" + m.Name + out0 := m.Type.Out(0) + v := reflect.New(out0) + if gs.IsBeanType(out0) { + v = v.Elem() + } + t := v.Type() + b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) + newBeans = append(newBeans, b) + break + } + } + } + return newBeans, nil +} + func (c *Container) registerBean(b *gs.BeanDefinition) { // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) @@ -574,78 +374,6 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { return nil } -// wireTag 注入语法的 tag 分解式,字符串形式的完整格式为 TypeName:BeanName? 。 -// 注入语法的字符串表示形式分为三个部分,TypeName 是原始类型的全限定名,BeanName -// 是 bean 注册时设置的名称,? 表示注入结果允许为空。 -type wireTag struct { - typeName string - beanName string - nullable bool -} - -func parseWireTag(str string) (tag wireTag) { - - if str == "" { - return - } - - if n := len(str) - 1; str[n] == '?' { - tag.nullable = true - str = str[:n] - } - - i := strings.Index(str, ":") - if i < 0 { - tag.beanName = str - return - } - - tag.typeName = str[:i] - tag.beanName = str[i+1:] - return -} - -func (tag wireTag) String() string { - b := bytes.NewBuffer(nil) - if tag.typeName != "" { - b.WriteString(tag.typeName) - b.WriteString(":") - } - b.WriteString(tag.beanName) - if tag.nullable { - b.WriteString("?") - } - return b.String() -} - -func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { - switch s := selector.(type) { - case string: - s, err := c.resolveTag(s) - if err != nil { - return wireTag{}, err - } - return parseWireTag(s), nil - case gs.BeanDefinition: - return parseWireTag(s.ID()), nil - case *gs.BeanDefinition: - return parseWireTag(s.ID()), nil - default: - return parseWireTag(gs.TypeName(s) + ":"), nil - } -} - -func toWireString(tags []wireTag) string { - var buf bytes.Buffer - for i, tag := range tags { - buf.WriteString(tag.String()) - if i < len(tags)-1 { - buf.WriteByte(',') - } - } - return buf.String() -} - // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { @@ -702,568 +430,6 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) }) } -// wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 -// 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 -// 实例化被依赖的 bean 然后对它们进行注入。 -func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { - - if b.GetStatus() == gs.Deleted { - return fmt.Errorf("bean:%q have been deleted", b.ID()) - } - - // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.GetStatus() == gs.Wired { - return nil - } - - haveDestroy := false - - defer func() { - if haveDestroy { - stack.destroyers.Remove(stack.destroyers.Back()) - } - }() - - // 记录注入路径上的销毁函数及其执行的先后顺序。 - if _, ok := b.Interface().(BeanDestroy); ok || b.GetDestroy() != nil { - haveDestroy = true - d := stack.saveDestroyer(b) - if i := stack.destroyers.Back(); i != nil { - d.after(i.Value.(*gs.BeanDefinition)) - } - stack.destroyers.PushBack(b) - } - - stack.pushBack(b) - - if b.GetStatus() == gs.Creating && b.Callable() != nil { - prev := stack.beans[len(stack.beans)-2] - if prev.GetStatus() == gs.Creating { - return errors.New("found circle autowire") - } - } - - if b.GetStatus() >= gs.Creating { - stack.popBack() - return nil - } - - b.SetStatus(gs.Creating) - - // 对当前 bean 的间接依赖项进行注入。 - for _, s := range b.GetDepends() { - beans, err := c.Find(s) - if err != nil { - return err - } - for _, d := range beans { - err = c.wireBean(d, stack) - if err != nil { - return err - } - } - } - - v, err := c.getBeanValue(b, stack) - if err != nil { - return err - } - - b.SetStatus(gs.Created) - - t := v.Type() - for _, typ := range b.GetExports() { - if !t.Implements(typ) { - return fmt.Errorf("%s doesn't implement interface %s", b, typ) - } - } - - err = c.wireBeanValue(v, t, stack) - if err != nil { - return err - } - - // 如果 bean 有初始化函数,则执行其初始化函数。 - if b.GetInit() != nil { - fnValue := reflect.ValueOf(b.GetInit()) - out := fnValue.Call([]reflect.Value{b.Value()}) - if len(out) > 0 && !out[0].IsNil() { - return out[0].Interface().(error) - } - } - - // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 - if f, ok := b.Interface().(BeanInit); ok { - if err = f.OnInit(c); err != nil { - return err - } - } - - // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 - if b.IsRefreshEnable() { - i := b.Interface().(dync.Refreshable) - if err = c.p.AddBean(i, b.GetRefreshParam()); err != nil { - return err - } - } - - b.SetStatus(gs.Wired) - stack.popBack() - return nil -} - -type argContext struct { - c *Container - stack *wiringStack -} - -func (a *argContext) Matches(c gs.Condition) (bool, error) { - return c.Matches(a.c) -} - -func (a *argContext) Bind(v reflect.Value, tag string) error { - return a.c.p.Data().Bind(v, conf.Tag(tag)) -} - -func (a *argContext) Wire(v reflect.Value, tag string) error { - return a.c.wireByTag(v, tag, a.stack) -} - -// getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { - - if b.Callable() == nil { - return b.Value(), nil - } - - out, err := b.Callable().Call(&argContext{c: c, stack: stack}) - if err != nil { - return reflect.Value{}, err /* fmt.Errorf("%s:%s return error: %v", b.getClass(), b.ID(), err) */ - } - - // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 - if val := out[0]; gs.IsBeanType(val.Type()) { - // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 - if !val.IsNil() && val.Kind() == reflect.Interface && conf.IsValueType(val.Elem().Type()) { - v := reflect.New(val.Elem().Type()) - v.Elem().Set(val.Elem()) - b.Value().Set(v) - } else { - b.Value().Set(val) - } - } else { - b.Value().Elem().Set(val) - } - - if b.Value().IsNil() { - return reflect.Value{}, fmt.Errorf("%s:%q return nil", b.GetClass(), b.FileLine()) - } - - v := b.Value() - // 结果以接口类型返回时需要将原始值取出来才能进行注入。 - if b.Type().Kind() == reflect.Interface { - v = v.Elem() - } - return v, nil -} - -// wireBeanValue 对 v 进行属性绑定和依赖注入,v 在传入时应该是一个已经初始化的值。 -func (c *Container) wireBeanValue(v reflect.Value, t reflect.Type, stack *wiringStack) error { - - if v.Kind() == reflect.Ptr { - v = v.Elem() - t = t.Elem() - } - - // 如整数指针类型的 bean 是无需注入的。 - if v.Kind() != reflect.Struct { - return nil - } - - typeName := t.Name() - if typeName == "" { // 简单类型没有名字 - typeName = t.String() - } - - param := conf.BindParam{Path: typeName} - return c.wireStruct(v, t, param, stack) -} - -// wireStruct 对结构体进行依赖注入,需要注意的是这里不需要进行属性绑定。 -func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *wiringStack) error { - - for i := 0; i < t.NumField(); i++ { - ft := t.Field(i) - fv := v.Field(i) - - if !fv.CanInterface() { - fv = PatchValue(fv) - if !fv.CanInterface() { - continue - } - } - - fieldPath := opt.Path + "." + ft.Name - - // 支持 autowire 和 inject 两个标签。 - tag, ok := ft.Tag.Lookup("autowire") - if !ok { - tag, ok = ft.Tag.Lookup("inject") - } - if ok { - if strings.HasSuffix(tag, ",lazy") { - f := lazyField{v: fv, path: fieldPath, tag: tag} - stack.lazyFields = append(stack.lazyFields, f) - } else { - if ft.Type == contextType { - c.ContextAware = true - } - if err := c.wireByTag(fv, tag, stack); err != nil { - return fmt.Errorf("%q wired error: %w", fieldPath, err) - } - } - continue - } - - subParam := conf.BindParam{ - Key: opt.Key, - Path: fieldPath, - } - - if tag, ok = ft.Tag.Lookup("value"); ok { - if err := subParam.BindTag(tag, ft.Tag); err != nil { - return err - } - if ft.Anonymous { - err := c.wireStruct(fv, ft.Type, subParam, stack) - if err != nil { - return err - } - } else { - err := c.p.AddField(fv.Addr(), subParam) - if err != nil { - return err - } - } - continue - } - - if ft.Anonymous && ft.Type.Kind() == reflect.Struct { - if err := c.wireStruct(fv, ft.Type, subParam, stack); err != nil { - return err - } - } - } - return nil -} - -// resolveTag tag 预处理,可能通过属性值进行指定。 -func (c *Container) resolveTag(tag string) (string, error) { - if strings.HasPrefix(tag, "${") { - s, err := c.p.Data().Resolve(tag) - if err != nil { - return "", err - } - return s, nil - } - return tag, nil -} - -func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { - - tag, err := c.resolveTag(tag) - if err != nil { - return err - } - - if tag == "" { - return c.autowire(v, nil, false, stack) - } - - var tags []wireTag - if tag != "?" { - for _, s := range strings.Split(tag, ",") { - var g wireTag - g, err = c.toWireTag(s) - if err != nil { - return err - } - tags = append(tags, g) - } - } - return c.autowire(v, tags, tag == "?", stack) -} - -func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { - switch v.Kind() { - case reflect.Map, reflect.Slice, reflect.Array: - return c.collectBeans(v, tags, nullable, stack) - default: - var tag wireTag - if len(tags) > 0 { - tag = tags[0] - } else if nullable { - tag.nullable = true - } - return c.getBean(v, tag, stack) - } -} - -// getBean 获取 tag 对应的 bean 然后赋值给 v,因此 v 应该是一个未初始化的值。 -func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) error { - - if !v.IsValid() { - return fmt.Errorf("receiver must be ref type, bean:%q", tag) - } - - t := v.Type() - if !gs.IsBeanReceiver(t) { - return fmt.Errorf("%s is not valid receiver type", t.String()) - } - - var foundBeans []*gs.BeanDefinition - for _, b := range c.beansByType[t] { - if b.GetStatus() == gs.Deleted { - continue - } - if !b.Match(tag.typeName, tag.beanName) { - continue - } - foundBeans = append(foundBeans, b) - } - - // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 - if t.Kind() == reflect.Interface && tag.beanName != "" { - for _, b := range c.beansByName[tag.beanName] { - if b.GetStatus() == gs.Deleted { - continue - } - if !b.Type().AssignableTo(t) { - continue - } - if !b.Match(tag.typeName, tag.beanName) { - continue - } - - found := false // 对结果排重 - for _, r := range foundBeans { - if r == b { - found = true - break - } - } - if !found { - foundBeans = append(foundBeans, b) - // c.logger.Warnf("you should call Export() on %s", b) - } - } - } - - if len(foundBeans) == 0 { - if tag.nullable { - return nil - } - return fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) - } - - // 优先使用设置成主版本的 bean - var primaryBeans []*gs.BeanDefinition - - for _, b := range foundBeans { - if b.IsPrimary() { - primaryBeans = append(primaryBeans, b) - } - } - - if len(primaryBeans) > 1 { - msg := fmt.Sprintf("found %d primary beans, bean:%q type:%q [", len(primaryBeans), tag, t) - for _, b := range primaryBeans { - msg += "( " + b.String() + " ), " - } - msg = msg[:len(msg)-2] + "]" - return errors.New(msg) - } - - if len(primaryBeans) == 0 && len(foundBeans) > 1 { - msg := fmt.Sprintf("found %d beans, bean:%q type:%q [", len(foundBeans), tag, t) - for _, b := range foundBeans { - msg += "( " + b.String() + " ), " - } - msg = msg[:len(msg)-2] + "]" - return errors.New(msg) - } - - var result *gs.BeanDefinition - if len(primaryBeans) == 1 { - result = primaryBeans[0] - } else { - result = foundBeans[0] - } - - // 确保找到的 bean 已经完成依赖注入。 - err := c.wireBean(result, stack) - if err != nil { - return err - } - - v.Set(result.Value()) - return nil -} - -// filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*gs.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { - - var found []int - for i, b := range beans { - if b.Match(tag.typeName, tag.beanName) { - found = append(found, i) - } - } - - if len(found) > 1 { - msg := fmt.Sprintf("found %d beans, bean:%q type:%q [", len(found), tag, t) - for _, i := range found { - msg += "( " + beans[i].String() + " ), " - } - msg = msg[:len(msg)-2] + "]" - return -1, errors.New(msg) - } - - if len(found) > 0 { - i := found[0] - return i, nil - } - - if tag.nullable { - return -1, nil - } - - return -1, fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) -} - -type byOrder []*gs.BeanDefinition - -func (b byOrder) Len() int { return len(b) } -func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } -func (b byOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } - -func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { - - t := v.Type() - if t.Kind() != reflect.Slice && t.Kind() != reflect.Map { - return fmt.Errorf("should be slice or map in collection mode") - } - - et := t.Elem() - if !gs.IsBeanReceiver(et) { - return fmt.Errorf("%s is not valid receiver type", t.String()) - } - - var beans []*gs.BeanDefinition - if et.Kind() == reflect.Interface && et.NumMethod() == 0 { - beans = c.beans - } else { - beans = c.beansByType[et] - } - - { - var arr []*gs.BeanDefinition - for _, b := range beans { - if b.GetStatus() == gs.Deleted { - continue - } - arr = append(arr, b) - } - beans = arr - } - - if len(tags) > 0 { - - var ( - anyBeans []*gs.BeanDefinition - afterAny []*gs.BeanDefinition - beforeAny []*gs.BeanDefinition - ) - - foundAny := false - for _, item := range tags { - - // 是否遇到了"无序"标记 - if item.beanName == "*" { - if foundAny { - return fmt.Errorf("more than one * in collection %q", tags) - } - foundAny = true - continue - } - - index, err := filterBean(beans, item, et) - if err != nil { - return err - } - if index < 0 { - continue - } - - if foundAny { - afterAny = append(afterAny, beans[index]) - } else { - beforeAny = append(beforeAny, beans[index]) - } - - tmpBeans := append([]*gs.BeanDefinition{}, beans[:index]...) - beans = append(tmpBeans, beans[index+1:]...) - } - - if foundAny { - anyBeans = append(anyBeans, beans...) - } - - n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*gs.BeanDefinition, 0, n) - arr = append(arr, beforeAny...) - arr = append(arr, anyBeans...) - arr = append(arr, afterAny...) - beans = arr - } - - if len(beans) == 0 && !nullable { - if len(tags) == 0 { - return fmt.Errorf("no beans collected for %q", toWireString(tags)) - } - for _, tag := range tags { - if !tag.nullable { - return fmt.Errorf("no beans collected for %q", toWireString(tags)) - } - } - return nil - } - - for _, b := range beans { - if err := c.wireBean(b, stack); err != nil { - return err - } - } - - var ret reflect.Value - switch t.Kind() { - case reflect.Slice: - sort.Sort(byOrder(beans)) - ret = reflect.MakeSlice(t, 0, 0) - for _, b := range beans { - ret = reflect.Append(ret, b.Value()) - } - case reflect.Map: - ret = reflect.MakeMap(t) - for _, b := range beans { - ret.SetMapIndex(reflect.ValueOf(b.GetName()), b.Value()) - } - default: - } - v.Set(ret) - return nil -} - // Get 根据类型和选择器获取符合条件的 bean 对象。当 i 是一个基础类型的 bean 接收 // 者时,表示符合条件的 bean 对象只能有一个,没有找到或者多于一个时会返回 error。 // 当 i 是一个 map 类型的 bean 接收者时,表示获取任意数量的 bean 对象,map 的 @@ -1324,6 +490,7 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} return b.Interface(), nil } +// Invoke 调用函数,函数的参数会自动注入,函数的返回值也会自动注入。 func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { if !gs.IsFuncType(reflect.TypeOf(fn)) { @@ -1355,6 +522,21 @@ func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error return a, nil } +// Go 创建安全可等待的 goroutine,fn 要求的 ctx 对象由 IoC 容器提供,当 IoC 容 +// 器关闭时 ctx会 发出 Done 信号, fn 在接收到此信号后应当立即退出。 +func (c *Container) Go(fn func(ctx context.Context)) { + c.wg.Add(1) + go func() { + defer c.wg.Done() + defer func() { + if r := recover(); r != nil { + // c.logger.Panic(r) + } + }() + fn(c.ctx) + }() +} + // Close 关闭容器,此方法必须在 Refresh 之后调用。该方法会触发 ctx 的 Done 信 // 号,然后等待所有 goroutine 结束,最后按照被依赖先销毁的原则执行所有的销毁函数。 func (c *Container) Close() { @@ -1370,18 +552,3 @@ func (c *Container) Close() { // c.logger.Info("Container closed") } - -// Go 创建安全可等待的 goroutine,fn 要求的 ctx 对象由 IoC 容器提供,当 IoC 容 -// 器关闭时 ctx会 发出 Done 信号, fn 在接收到此信号后应当立即退出。 -func (c *Container) Go(fn func(ctx context.Context)) { - c.wg.Add(1) - go func() { - defer c.wg.Done() - defer func() { - if r := recover(); r != nil { - // c.logger.Panic(r) - } - }() - fn(c.ctx) - }() -} diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go new file mode 100644 index 00000000..ee5aa599 --- /dev/null +++ b/gs/internal/gs_core/wire.go @@ -0,0 +1,773 @@ +package gs_core + +import ( + "bytes" + "container/list" + "errors" + "fmt" + "reflect" + "sort" + "strings" + + "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/dync" + "github.com/go-spring/spring-core/gs/internal/gs" +) + +var ( + GsContextType = reflect.TypeOf((*gs.Context)(nil)).Elem() +) + +type lazyField struct { + v reflect.Value + path string + tag string +} + +// destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 +type destroyer struct { + current *gs.BeanDefinition + earlier []*gs.BeanDefinition +} + +func (d *destroyer) foundEarlier(b *gs.BeanDefinition) bool { + for _, c := range d.earlier { + if c == b { + return true + } + } + return false +} + +// after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 +func (d *destroyer) after(b *gs.BeanDefinition) { + if d.foundEarlier(b) { + return + } + d.earlier = append(d.earlier, b) +} + +// getBeforeDestroyers 获取排在 i 前面的 destroyer,用于 sort.Triple 排序。 +func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { + d := i.(*destroyer) + result := list.New() + for e := destroyers.Front(); e != nil; e = e.Next() { + c := e.Value.(*destroyer) + if d.foundEarlier(c.current) { + result.PushBack(c) + } + } + return result +} + +// wiringStack 记录 bean 的注入路径。 +type wiringStack struct { + destroyers *list.List + destroyerMap map[string]*destroyer + beans []*gs.BeanDefinition + lazyFields []lazyField +} + +func newWiringStack() *wiringStack { + return &wiringStack{ + destroyers: list.New(), + destroyerMap: make(map[string]*destroyer), + } +} + +// pushBack 添加一个即将注入的 bean 。 +func (s *wiringStack) pushBack(b *gs.BeanDefinition) { + // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) + s.beans = append(s.beans, b) +} + +// popBack 删除一个已经注入的 bean 。 +func (s *wiringStack) popBack() { + n := len(s.beans) + // b := s.beans[n-1] + s.beans = s.beans[:n-1] + // s.logger.Tracef("pop %s %s", b, getStatusString(b.status)) +} + +// path 返回 bean 的注入路径。 +func (s *wiringStack) path() (path string) { + for _, b := range s.beans { + path += fmt.Sprintf("=> %s ↩\n", b) + } + return path[:len(path)-1] +} + +// saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 +func (s *wiringStack) saveDestroyer(b *gs.BeanDefinition) *destroyer { + d, ok := s.destroyerMap[b.ID()] + if !ok { + d = &destroyer{current: b} + s.destroyerMap[b.ID()] = d + } + return d +} + +// sortDestroyers 对具有销毁函数的 bean 按照销毁函数的依赖顺序进行排序。 +func (s *wiringStack) sortDestroyers() []func() { + + destroy := func(v reflect.Value, fn interface{}) func() { + return func() { + if fn == nil { + v.Interface().(BeanDestroy).OnDestroy() + } else { + fnValue := reflect.ValueOf(fn) + out := fnValue.Call([]reflect.Value{v}) + if len(out) > 0 && !out[0].IsNil() { + // s.logger.Error(out[0].Interface().(error)) + } + } + } + } + + destroyers := list.New() + for _, d := range s.destroyerMap { + destroyers.PushBack(d) + } + destroyers = TripleSort(destroyers, getBeforeDestroyers) + + var ret []func() + for e := destroyers.Front(); e != nil; e = e.Next() { + d := e.Value.(*destroyer).current + ret = append(ret, destroy(d.Value(), d.GetDestroy())) + } + return ret +} + +// wireTag 注入语法的 tag 分解式,字符串形式的完整格式为 TypeName:BeanName? 。 +// 注入语法的字符串表示形式分为三个部分,TypeName 是原始类型的全限定名,BeanName +// 是 bean 注册时设置的名称,? 表示注入结果允许为空。 +type wireTag struct { + typeName string + beanName string + nullable bool +} + +func parseWireTag(str string) (tag wireTag) { + + if str == "" { + return + } + + if n := len(str) - 1; str[n] == '?' { + tag.nullable = true + str = str[:n] + } + + i := strings.Index(str, ":") + if i < 0 { + tag.beanName = str + return + } + + tag.typeName = str[:i] + tag.beanName = str[i+1:] + return +} + +func (tag wireTag) String() string { + b := bytes.NewBuffer(nil) + if tag.typeName != "" { + b.WriteString(tag.typeName) + b.WriteString(":") + } + b.WriteString(tag.beanName) + if tag.nullable { + b.WriteString("?") + } + return b.String() +} + +func toWireString(tags []wireTag) string { + var buf bytes.Buffer + for i, tag := range tags { + buf.WriteString(tag.String()) + if i < len(tags)-1 { + buf.WriteByte(',') + } + } + return buf.String() +} + +// resolveTag tag 预处理,可能通过属性值进行指定。 +func (c *Container) resolveTag(tag string) (string, error) { + if strings.HasPrefix(tag, "${") { + s, err := c.p.Data().Resolve(tag) + if err != nil { + return "", err + } + return s, nil + } + return tag, nil +} + +func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { + switch s := selector.(type) { + case string: + s, err := c.resolveTag(s) + if err != nil { + return wireTag{}, err + } + return parseWireTag(s), nil + case gs.BeanDefinition: + return parseWireTag(s.ID()), nil + case *gs.BeanDefinition: + return parseWireTag(s.ID()), nil + default: + return parseWireTag(gs.TypeName(s) + ":"), nil + } +} + +func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { + switch v.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return c.collectBeans(v, tags, nullable, stack) + default: + var tag wireTag + if len(tags) > 0 { + tag = tags[0] + } else if nullable { + tag.nullable = true + } + return c.getBean(v, tag, stack) + } +} + +type byOrder []*gs.BeanDefinition + +func (b byOrder) Len() int { return len(b) } +func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } +func (b byOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +// filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 +func filterBean(beans []*gs.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { + + var found []int + for i, b := range beans { + if b.Match(tag.typeName, tag.beanName) { + found = append(found, i) + } + } + + if len(found) > 1 { + msg := fmt.Sprintf("found %d beans, bean:%q type:%q [", len(found), tag, t) + for _, i := range found { + msg += "( " + beans[i].String() + " ), " + } + msg = msg[:len(msg)-2] + "]" + return -1, errors.New(msg) + } + + if len(found) > 0 { + i := found[0] + return i, nil + } + + if tag.nullable { + return -1, nil + } + + return -1, fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) +} + +func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { + + t := v.Type() + if t.Kind() != reflect.Slice && t.Kind() != reflect.Map { + return fmt.Errorf("should be slice or map in collection mode") + } + + et := t.Elem() + if !gs.IsBeanReceiver(et) { + return fmt.Errorf("%s is not valid receiver type", t.String()) + } + + var beans []*gs.BeanDefinition + if et.Kind() == reflect.Interface && et.NumMethod() == 0 { + beans = c.beans + } else { + beans = c.beansByType[et] + } + + { + var arr []*gs.BeanDefinition + for _, b := range beans { + if b.GetStatus() == gs.Deleted { + continue + } + arr = append(arr, b) + } + beans = arr + } + + if len(tags) > 0 { + + var ( + anyBeans []*gs.BeanDefinition + afterAny []*gs.BeanDefinition + beforeAny []*gs.BeanDefinition + ) + + foundAny := false + for _, item := range tags { + + // 是否遇到了"无序"标记 + if item.beanName == "*" { + if foundAny { + return fmt.Errorf("more than one * in collection %q", tags) + } + foundAny = true + continue + } + + index, err := filterBean(beans, item, et) + if err != nil { + return err + } + if index < 0 { + continue + } + + if foundAny { + afterAny = append(afterAny, beans[index]) + } else { + beforeAny = append(beforeAny, beans[index]) + } + + tmpBeans := append([]*gs.BeanDefinition{}, beans[:index]...) + beans = append(tmpBeans, beans[index+1:]...) + } + + if foundAny { + anyBeans = append(anyBeans, beans...) + } + + n := len(beforeAny) + len(anyBeans) + len(afterAny) + arr := make([]*gs.BeanDefinition, 0, n) + arr = append(arr, beforeAny...) + arr = append(arr, anyBeans...) + arr = append(arr, afterAny...) + beans = arr + } + + if len(beans) == 0 && !nullable { + if len(tags) == 0 { + return fmt.Errorf("no beans collected for %q", toWireString(tags)) + } + for _, tag := range tags { + if !tag.nullable { + return fmt.Errorf("no beans collected for %q", toWireString(tags)) + } + } + return nil + } + + for _, b := range beans { + if err := c.wireBean(b, stack); err != nil { + return err + } + } + + var ret reflect.Value + switch t.Kind() { + case reflect.Slice: + sort.Sort(byOrder(beans)) + ret = reflect.MakeSlice(t, 0, 0) + for _, b := range beans { + ret = reflect.Append(ret, b.Value()) + } + case reflect.Map: + ret = reflect.MakeMap(t) + for _, b := range beans { + ret.SetMapIndex(reflect.ValueOf(b.GetName()), b.Value()) + } + default: + } + v.Set(ret) + return nil +} + +// getBean 获取 tag 对应的 bean 然后赋值给 v,因此 v 应该是一个未初始化的值。 +func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) error { + + if !v.IsValid() { + return fmt.Errorf("receiver must be ref type, bean:%q", tag) + } + + t := v.Type() + if !gs.IsBeanReceiver(t) { + return fmt.Errorf("%s is not valid receiver type", t.String()) + } + + var foundBeans []*gs.BeanDefinition + for _, b := range c.beansByType[t] { + if b.GetStatus() == gs.Deleted { + continue + } + if !b.Match(tag.typeName, tag.beanName) { + continue + } + foundBeans = append(foundBeans, b) + } + + // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 + if t.Kind() == reflect.Interface && tag.beanName != "" { + for _, b := range c.beansByName[tag.beanName] { + if b.GetStatus() == gs.Deleted { + continue + } + if !b.Type().AssignableTo(t) { + continue + } + if !b.Match(tag.typeName, tag.beanName) { + continue + } + + found := false // 对结果排重 + for _, r := range foundBeans { + if r == b { + found = true + break + } + } + if !found { + foundBeans = append(foundBeans, b) + // c.logger.Warnf("you should call Export() on %s", b) + } + } + } + + if len(foundBeans) == 0 { + if tag.nullable { + return nil + } + return fmt.Errorf("can't find bean, bean:%q type:%q", tag, t) + } + + // 优先使用设置成主版本的 bean + var primaryBeans []*gs.BeanDefinition + + for _, b := range foundBeans { + if b.IsPrimary() { + primaryBeans = append(primaryBeans, b) + } + } + + if len(primaryBeans) > 1 { + msg := fmt.Sprintf("found %d primary beans, bean:%q type:%q [", len(primaryBeans), tag, t) + for _, b := range primaryBeans { + msg += "( " + b.String() + " ), " + } + msg = msg[:len(msg)-2] + "]" + return errors.New(msg) + } + + if len(primaryBeans) == 0 && len(foundBeans) > 1 { + msg := fmt.Sprintf("found %d beans, bean:%q type:%q [", len(foundBeans), tag, t) + for _, b := range foundBeans { + msg += "( " + b.String() + " ), " + } + msg = msg[:len(msg)-2] + "]" + return errors.New(msg) + } + + var result *gs.BeanDefinition + if len(primaryBeans) == 1 { + result = primaryBeans[0] + } else { + result = foundBeans[0] + } + + // 确保找到的 bean 已经完成依赖注入。 + err := c.wireBean(result, stack) + if err != nil { + return err + } + + v.Set(result.Value()) + return nil +} + +// wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 +// 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 +// 实例化被依赖的 bean 然后对它们进行注入。 +func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { + + if b.GetStatus() == gs.Deleted { + return fmt.Errorf("bean:%q have been deleted", b.ID()) + } + + // 运行时 Get 或者 Wire 会出现下面这种情况。 + if c.state == Refreshed && b.GetStatus() == gs.Wired { + return nil + } + + haveDestroy := false + + defer func() { + if haveDestroy { + stack.destroyers.Remove(stack.destroyers.Back()) + } + }() + + // 记录注入路径上的销毁函数及其执行的先后顺序。 + if _, ok := b.Interface().(BeanDestroy); ok || b.GetDestroy() != nil { + haveDestroy = true + d := stack.saveDestroyer(b) + if i := stack.destroyers.Back(); i != nil { + d.after(i.Value.(*gs.BeanDefinition)) + } + stack.destroyers.PushBack(b) + } + + stack.pushBack(b) + + if b.GetStatus() == gs.Creating && b.Callable() != nil { + prev := stack.beans[len(stack.beans)-2] + if prev.GetStatus() == gs.Creating { + return errors.New("found circle autowire") + } + } + + if b.GetStatus() >= gs.Creating { + stack.popBack() + return nil + } + + b.SetStatus(gs.Creating) + + // 对当前 bean 的间接依赖项进行注入。 + for _, s := range b.GetDepends() { + beans, err := c.Find(s) + if err != nil { + return err + } + for _, d := range beans { + err = c.wireBean(d, stack) + if err != nil { + return err + } + } + } + + v, err := c.getBeanValue(b, stack) + if err != nil { + return err + } + + b.SetStatus(gs.Created) + + t := v.Type() + for _, typ := range b.GetExports() { + if !t.Implements(typ) { + return fmt.Errorf("%s doesn't implement interface %s", b, typ) + } + } + + err = c.wireBeanValue(v, t, stack) + if err != nil { + return err + } + + // 如果 bean 有初始化函数,则执行其初始化函数。 + if b.GetInit() != nil { + fnValue := reflect.ValueOf(b.GetInit()) + out := fnValue.Call([]reflect.Value{b.Value()}) + if len(out) > 0 && !out[0].IsNil() { + return out[0].Interface().(error) + } + } + + // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 + if f, ok := b.Interface().(BeanInit); ok { + if err = f.OnInit(c); err != nil { + return err + } + } + + // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 + if b.IsRefreshEnable() { + i := b.Interface().(dync.Refreshable) + if err = c.p.AddBean(i, b.GetRefreshParam()); err != nil { + return err + } + } + + b.SetStatus(gs.Wired) + stack.popBack() + return nil +} + +type argContext struct { + c *Container + stack *wiringStack +} + +func (a *argContext) Matches(c gs.Condition) (bool, error) { + return c.Matches(a.c) +} + +func (a *argContext) Bind(v reflect.Value, tag string) error { + return a.c.p.Data().Bind(v, conf.Tag(tag)) +} + +func (a *argContext) Wire(v reflect.Value, tag string) error { + return a.c.wireByTag(v, tag, a.stack) +} + +// getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 +func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { + + if b.Callable() == nil { + return b.Value(), nil + } + + out, err := b.Callable().Call(&argContext{c: c, stack: stack}) + if err != nil { + return reflect.Value{}, err /* fmt.Errorf("%s:%s return error: %v", b.getClass(), b.ID(), err) */ + } + + // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 + if val := out[0]; gs.IsBeanType(val.Type()) { + // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 + if !val.IsNil() && val.Kind() == reflect.Interface && conf.IsValueType(val.Elem().Type()) { + v := reflect.New(val.Elem().Type()) + v.Elem().Set(val.Elem()) + b.Value().Set(v) + } else { + b.Value().Set(val) + } + } else { + b.Value().Elem().Set(val) + } + + if b.Value().IsNil() { + return reflect.Value{}, fmt.Errorf("%s:%q return nil", b.GetClass(), b.FileLine()) + } + + v := b.Value() + // 结果以接口类型返回时需要将原始值取出来才能进行注入。 + if b.Type().Kind() == reflect.Interface { + v = v.Elem() + } + return v, nil +} + +// wireBeanValue 对 v 进行属性绑定和依赖注入,v 在传入时应该是一个已经初始化的值。 +func (c *Container) wireBeanValue(v reflect.Value, t reflect.Type, stack *wiringStack) error { + + if v.Kind() == reflect.Ptr { + v = v.Elem() + t = t.Elem() + } + + // 如整数指针类型的 bean 是无需注入的。 + if v.Kind() != reflect.Struct { + return nil + } + + typeName := t.Name() + if typeName == "" { // 简单类型没有名字 + typeName = t.String() + } + + param := conf.BindParam{Path: typeName} + return c.wireStruct(v, t, param, stack) +} + +// wireStruct 对结构体进行依赖注入,需要注意的是这里不需要进行属性绑定。 +func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *wiringStack) error { + + for i := 0; i < t.NumField(); i++ { + ft := t.Field(i) + fv := v.Field(i) + + if !fv.CanInterface() { + fv = PatchValue(fv) + if !fv.CanInterface() { + continue + } + } + + fieldPath := opt.Path + "." + ft.Name + + // 支持 autowire 和 inject 两个标签。 + tag, ok := ft.Tag.Lookup("autowire") + if !ok { + tag, ok = ft.Tag.Lookup("inject") + } + if ok { + if strings.HasSuffix(tag, ",lazy") { + f := lazyField{v: fv, path: fieldPath, tag: tag} + stack.lazyFields = append(stack.lazyFields, f) + } else { + if ft.Type == GsContextType { + c.ContextAware = true + } + if err := c.wireByTag(fv, tag, stack); err != nil { + return fmt.Errorf("%q wired error: %w", fieldPath, err) + } + } + continue + } + + subParam := conf.BindParam{ + Key: opt.Key, + Path: fieldPath, + } + + if tag, ok = ft.Tag.Lookup("value"); ok { + if err := subParam.BindTag(tag, ft.Tag); err != nil { + return err + } + if ft.Anonymous { + err := c.wireStruct(fv, ft.Type, subParam, stack) + if err != nil { + return err + } + } else { + err := c.p.AddField(fv.Addr(), subParam) + if err != nil { + return err + } + } + continue + } + + if ft.Anonymous && ft.Type.Kind() == reflect.Struct { + if err := c.wireStruct(fv, ft.Type, subParam, stack); err != nil { + return err + } + } + } + return nil +} + +func (c *Container) wireByTag(v reflect.Value, tag string, stack *wiringStack) error { + + tag, err := c.resolveTag(tag) + if err != nil { + return err + } + + if tag == "" { + return c.autowire(v, nil, false, stack) + } + + var tags []wireTag + if tag != "?" { + for _, s := range strings.Split(tag, ",") { + var g wireTag + g, err = c.toWireTag(s) + if err != nil { + return err + } + tags = append(tags, g) + } + } + return c.autowire(v, tags, tag == "?", stack) +} From 1410c78ff88e144c3fa00ffd0addca2831d80334 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 4 Jan 2025 17:11:45 +0800 Subject: [PATCH 31/75] simplify memory --- dync/dync.go | 7 ++ gs/gs.go | 11 +- gs/gstest/gstest.go | 24 ++--- gs/{gs_test.go => gstest/gstest_test.go} | 2 +- gs/internal/gs/bean.go | 122 ++++++++++++++--------- gs/internal/gs/gs.go | 34 +------ gs/internal/gs_app/app.go | 34 +++---- gs/internal/gs_core/core.go | 56 ++++++----- gs/internal/gs_core/core_test.go | 18 ++-- gs/internal/gs_core/wire.go | 10 +- 10 files changed, 166 insertions(+), 152 deletions(-) rename gs/{gs_test.go => gstest/gstest_test.go} (98%) diff --git a/dync/dync.go b/dync/dync.go index c4418cec..a7dd94fb 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -87,6 +87,13 @@ func (p *Properties) Data() conf.ReadOnlyProperties { return p.prop } +// ObjectsCount 绑定的可刷新对象数量 +func (p *Properties) ObjectsCount() int { + p.lock.RLock() + defer p.lock.RUnlock() + return len(p.objects) +} + // Refresh 更新属性列表以及绑定的可刷新对象 func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { p.lock.Lock() diff --git a/gs/gs.go b/gs/gs.go index 3ed4b18a..0856f4fd 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -37,6 +37,7 @@ type ( CondContext = gs.CondContext Arg = gs.Arg Context = gs.Context + ContextAware = gs.ContextAware GroupFunc = gs.GroupFunc ) @@ -67,11 +68,10 @@ func Boot() *gs_app.Boot { var app = gs_app.NewApp() // Start 启动程序。 -func Start() (gs.Context, error) { +func Start() error { printBanner() - err := bootRun() - if err != nil { - return nil, err + if err := bootRun(); err != nil { + return err } return app.Start() } @@ -84,8 +84,7 @@ func Stop() { // Run 启动程序。 func Run() error { printBanner() - err := bootRun() - if err != nil { + if err := bootRun(); err != nil { return err } return app.Run() diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 53992111..4d863dce 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -23,16 +23,12 @@ import ( "github.com/go-spring/spring-core/gs" ) -var ctx gs.Context +var ctx = &gs.ContextAware{} // Init 初始化测试环境 func Init() error { - c, err := gs.Start() - if err != nil { - return err - } - ctx = c - return nil + gs.Object(ctx) + return gs.Start() } // Run 运行测试用例 @@ -43,35 +39,35 @@ func Run(m *testing.M) (code int) { // HasProperty 判断属性是否存在 func HasProperty(key string) bool { - return ctx.Has(key) + return ctx.GSContext.Has(key) } // GetProperty 获取属性值 func GetProperty(key string, opts ...conf.GetOption) string { - return ctx.Prop(key, opts...) + return ctx.GSContext.Prop(key, opts...) } // Resolve 解析字符串 func Resolve(s string) (string, error) { - return ctx.Resolve(s) + return ctx.GSContext.Resolve(s) } // Bind 绑定对象 func Bind(i interface{}, opts ...conf.BindArg) error { - return ctx.Bind(i, opts...) + return ctx.GSContext.Bind(i, opts...) } // Get 获取对象 func Get(i interface{}, selectors ...gs.BeanSelector) error { - return ctx.Get(i, selectors...) + return ctx.GSContext.Get(i, selectors...) } // Wire 注入对象 func Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { - return ctx.Wire(objOrCtor, ctorArgs...) + return ctx.GSContext.Wire(objOrCtor, ctorArgs...) } // Invoke 调用函数 func Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { - return ctx.Invoke(fn, args...) + return ctx.GSContext.Invoke(fn, args...) } diff --git a/gs/gs_test.go b/gs/gstest/gstest_test.go similarity index 98% rename from gs/gs_test.go rename to gs/gstest/gstest_test.go index 9a4fdeb7..b7b76bb6 100644 --- a/gs/gs_test.go +++ b/gs/gstest/gstest_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_test +package gstest_test import ( "os" diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 43ff3e02..80842095 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -23,6 +23,39 @@ const ( Wired // 注入完成 ) +// BeanRegistration bean 注册数据。 +type BeanRegistration struct { + f Callable // 构造函数 + method bool // 是否为成员方法 + cond Condition // 判断条件 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 + file string // 注册点所在文件 + line int // 注册点所在行数 + status BeanStatus // 状态 + + configuration bool // 是否扫描成员方法 + includeMethod []string // 包含哪些成员方法 + excludeMethod []string // 排除那些成员方法 + + enableRefresh bool + refreshParam conf.BindParam +} + +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + r *BeanRegistration + + v reflect.Value // 值 + t reflect.Type // 类型 + + name string // 名称 + typeName string // 原始类型的全限定名 + primary bool // 是否为主版本 +} + func (d *BeanDefinition) GetName() string { return d.name } @@ -32,11 +65,11 @@ func (d *BeanDefinition) GetTypeName() string { } func (d *BeanDefinition) GetStatus() BeanStatus { - return d.status + return d.r.status } func (d *BeanDefinition) SetStatus(status BeanStatus) { - d.status = status + d.r.status = status } func (d *BeanDefinition) IsPrimary() bool { @@ -44,55 +77,51 @@ func (d *BeanDefinition) IsPrimary() bool { } func (d *BeanDefinition) IsMethod() bool { - return d.method + return d.r.method } func (d *BeanDefinition) GetCond() Condition { - return d.cond -} - -func (d *BeanDefinition) GetOrder() float32 { - return d.order + return d.r.cond } func (d *BeanDefinition) GetInit() interface{} { - return d.init + return d.r.init } func (d *BeanDefinition) GetDestroy() interface{} { - return d.destroy + return d.r.destroy } func (d *BeanDefinition) GetDepends() []BeanSelector { - return d.depends + return d.r.depends } func (d *BeanDefinition) GetExports() []reflect.Type { - return d.exports + return d.r.exports } func (d *BeanDefinition) IsConfiguration() bool { - return d.configuration + return d.r.configuration } func (d *BeanDefinition) GetIncludeMethod() []string { - return d.includeMethod + return d.r.includeMethod } func (d *BeanDefinition) GetExcludeMethod() []string { - return d.excludeMethod + return d.r.excludeMethod } func (d *BeanDefinition) IsRefreshEnable() bool { - return d.enableRefresh + return d.r.enableRefresh } func (d *BeanDefinition) GetRefreshParam() conf.BindParam { - return d.refreshParam + return d.r.refreshParam } func (d *BeanDefinition) Callable() Callable { - return d.f + return d.r.f } // Type 返回 bean 的类型。 @@ -127,30 +156,30 @@ func (d *BeanDefinition) TypeName() string { // Created 返回是否已创建。 func (d *BeanDefinition) Created() bool { - return d.status >= Created + return d.r.status >= Created } // Wired 返回 bean 是否已经注入。 func (d *BeanDefinition) Wired() bool { - return d.status == Wired + return d.r.status == Wired } func (d *BeanDefinition) File() string { - return d.file + return d.r.file } func (d *BeanDefinition) Line() int { - return d.line + return d.r.line } // FileLine 返回 bean 的注册点。 func (d *BeanDefinition) FileLine() string { - return fmt.Sprintf("%s:%d", d.file, d.line) + return fmt.Sprintf("%s:%d", d.r.file, d.r.line) } // GetClass 返回 bean 的类型描述。 func (d *BeanDefinition) GetClass() string { - if d.f == nil { + if d.r.f == nil { return "object bean" } return "constructor bean" @@ -184,19 +213,13 @@ func (d *BeanDefinition) Name(name string) *BeanDefinition { // On 设置 bean 的 Condition。 func (d *BeanDefinition) On(cond Condition) *BeanDefinition { - d.cond = cond - return d -} - -// Order 设置 bean 的排序序号,值越小顺序越靠前(优先级越高)。 -func (d *BeanDefinition) Order(order float32) *BeanDefinition { - d.order = order + d.r.cond = cond return d } // DependsOn 设置 bean 的间接依赖项。 func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition { - d.depends = append(d.depends, selectors...) + d.r.depends = append(d.r.depends, selectors...) return d } @@ -221,7 +244,7 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { // Init 设置 bean 的初始化函数。 func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.init = fn + d.r.init = fn return d } panic(errors.New("init should be func(bean) or func(bean)error")) @@ -230,7 +253,7 @@ func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { // Destroy 设置 bean 的销毁函数。 func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.destroy = fn + d.r.destroy = fn return d } panic(errors.New("destroy should be func(bean) or func(bean)error")) @@ -250,7 +273,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { panic(errors.New("only interface type can be exported")) } exported := false - for _, export := range d.exports { + for _, export := range d.r.exports { if t == export { exported = true break @@ -259,16 +282,16 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { if exported { continue } - d.exports = append(d.exports, t) + d.r.exports = append(d.r.exports, t) } return d } // Configuration 设置 bean 为配置类。 func (d *BeanDefinition) Configuration(includes []string, excludes []string) *BeanDefinition { - d.configuration = true - d.includeMethod = includes - d.excludeMethod = excludes + d.r.configuration = true + d.r.includeMethod = includes + d.r.excludeMethod = excludes return d } @@ -277,24 +300,31 @@ func (d *BeanDefinition) EnableRefresh(tag string) { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } - d.enableRefresh = true - err := d.refreshParam.BindTag(tag, "") + d.r.enableRefresh = true + err := d.r.refreshParam.BindTag(tag, "") if err != nil { panic(err) } } +// Simplify 精简内存 +func (d *BeanDefinition) Simplify() { + d.r = nil +} + // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ t: t, v: v, - f: f, name: name, typeName: TypeName(t), - status: Default, - method: method, - file: file, - line: line, + r: &BeanRegistration{ + f: f, + status: Default, + method: method, + file: file, + line: line, + }, } } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 7ac0bff6..78e52d72 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -11,35 +11,6 @@ import ( // `(*error)(nil)`, or a BeanDefinition value. type BeanSelector interface{} -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - v reflect.Value // 值 - t reflect.Type // 类型 - f Callable // 构造函数 - - file string // 注册点所在文件 - line int // 注册点所在行数 - - name string // 名称 - typeName string // 原始类型的全限定名 - status BeanStatus // 状态 - primary bool // 是否为主版本 - method bool // 是否为成员方法 - cond Condition // 判断条件 - order float32 // 收集时的顺序 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 - - configuration bool // 是否扫描成员方法 - includeMethod []string // 包含哪些成员方法 - excludeMethod []string // 排除那些成员方法 - - enableRefresh bool - refreshParam conf.BindParam -} - // CondContext defines some methods of IoC container that conditions use. type CondContext interface { // Has returns whether the IoC container has a property. @@ -101,3 +72,8 @@ type Context interface { } type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) + +// ContextAware injects the Context into a struct as the field GSContext. +type ContextAware struct { + GSContext Context `autowire:""` +} diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 3bb38e22..fe936f38 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -50,13 +50,11 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { - app := &App{ + return &App{ c: gs_core.New(), p: gs_conf.NewAppConfig(), exitChan: make(chan struct{}), } - app.Object(app) - return app } func (app *App) Config() *gs_conf.AppConfig { @@ -83,7 +81,7 @@ func (app *App) Group(fn gs.GroupFunc) { } func (app *App) Run() error { - if _, err := app.Start(); err != nil { + if err := app.Start(); err != nil { return err } go func() { @@ -97,27 +95,27 @@ func (app *App) Run() error { return nil } -func (app *App) Start() (gs.Context, error) { +func (app *App) Start() error { p, err := app.p.Refresh() if err != nil { - return nil, err + return err } err = app.c.RefreshProperties(p) if err != nil { - return nil, err + return err } err = app.c.Refresh() if err != nil { - return nil, err + return err } var runners []AppRunner err = app.c.Get(&runners, "${spring.app.runners:=*?}") if err != nil { - return nil, err + return err } // 执行命令行启动器 @@ -126,22 +124,18 @@ func (app *App) Start() (gs.Context, error) { } // 通知应用启动事件 - for _, event := range app.Servers { - event.OnAppStart(app.c) + for _, svr := range app.Servers { + svr.OnAppStart(app.c) } - // 通知应用停止事件 - app.c.Go(func(ctx context.Context) { - <-ctx.Done() - for _, event := range app.Servers { - event.OnAppStop(context.Background()) - } - }) - - return app.c, nil + app.c.Simplify() + return nil } func (app *App) Stop() { + for _, svr := range app.Servers { + svr.OnAppStop(context.Background()) + } app.c.Close() } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 762089ec..4b33708a 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -52,11 +52,6 @@ const ( Refreshed // 已刷新 ) -// ContextAware injects the Context into a struct as the field GSContext. -type ContextAware struct { - GSContext gs.Context `autowire:""` -} - // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -65,18 +60,17 @@ type ContextAware struct { // go-spring 严格区分了这两种概念,在描述对 bean 的处理时要么单独使用依赖注入或属 // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { - beans []*gs.BeanDefinition - beansByName map[string][]*gs.BeanDefinition - beansByType map[reflect.Type][]*gs.BeanDefinition - groupFuncs []gs.GroupFunc - ctx context.Context - cancel context.CancelFunc - destroyers []func() - state refreshState - wg sync.WaitGroup - p *dync.Properties - ContextAware bool - AllowCircularReferences bool `value:"${spring.main.allow-circular-references:=false}"` + beans []*gs.BeanDefinition + beansByName map[string][]*gs.BeanDefinition + beansByType map[reflect.Type][]*gs.BeanDefinition + groupFuncs []gs.GroupFunc + p *dync.Properties + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + state refreshState + destroyers []func() + ContextAware bool } // New 创建 IoC 容器。 @@ -229,7 +223,12 @@ func (c *Container) Refresh() (err error) { } } - if c.AllowCircularReferences { + var AllowCircularReferences bool + err = c.Bind(&AllowCircularReferences, conf.Tag("${spring.allow-circular-references:=false}")) + if err != nil { + return err + } + if AllowCircularReferences { // 处理被标记为延迟注入的那些 bean 字段 for _, f := range stack.lazyFields { tag := strings.TrimSuffix(f.tag, ",lazy") @@ -246,15 +245,26 @@ func (c *Container) Refresh() (err error) { // cost := time.Now().Sub(start) // c.logger.Infof("refresh %d beans cost %v", len(beansById), cost) - - // if autoClear && !c.ContextAware { - // c.clear() - // } - // c.logger.Info("Container refreshed successfully") return nil } +// Simplify 清理运行时不需要的空间。 +func (c *Container) Simplify() { + for _, bd := range c.beans { + bd.Simplify() + } + c.beans = nil + c.groupFuncs = nil + if !c.ContextAware { + if c.p.ObjectsCount() == 0 { + c.p = nil + } + c.beansByName = nil + c.beansByType = nil + } +} + func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefinition, error) { var ( includes []*regexp.Regexp diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 721e941e..dc2b8063 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -1172,8 +1172,8 @@ func TestApplicationContext_Collect(t *testing.T) { prop.Set("redis.endpoints", "redis://localhost:6379") c := gs_core.New() - c.Object(new(RecoresCluster)).Name("a").Order(1) - c.Object(new(RecoresCluster)).Name("b").Order(2) + c.Object(new(RecoresCluster)).Name("a") + c.Object(new(RecoresCluster)).Name("b") intBean := c.Provide(func(p gs.Context) func() { @@ -2790,8 +2790,8 @@ func TestMapCollection(t *testing.T) { t.Run("", func(t *testing.T) { c := gs_core.New() - c.Object(&mapValue{"a"}).Name("a").Order(1) - c.Object(&mapValue{"b"}).Name("b").Order(2) + c.Object(&mapValue{"a"}).Name("a") + c.Object(&mapValue{"b"}).Name("b") c.Object(&mapValue{"c"}).Name("c").On(gs_cond.Not(gs_cond.OK())) err := runTest(c, func(p gs.Context) { @@ -2828,7 +2828,7 @@ func newCircularB() *circularB { func TestLazy(t *testing.T) { for i := 0; i < 1; i++ { prop := conf.New() - prop.Set("spring.main.allow-circular-references", "true") + prop.Set("spring.allow-circular-references", "true") c := gs_core.New() c.Provide(newCircularA) @@ -2881,7 +2881,7 @@ func TestDestroyDependence(t *testing.T) { } type ContextAware struct { - gs_core.ContextAware + gs.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -3040,16 +3040,18 @@ func TestConfiguration(t *testing.T) { c := gs_core.New() c.Object(&ConfigurationBean{"123"}).Configuration(nil, nil).Name("123") c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") + ctx := &gs.ContextAware{} + c.Object(ctx) if err := c.Refresh(); err != nil { t.Fatal(err) } var b *ChildBean - err := c.Get(&b, "123_NewChild") + err := ctx.GSContext.Get(&b, "123_NewChild") if err != nil { t.Fatal(err) } assert.Equal(t, b.s, "123") - err = c.Get(&b, "456_NewChild") + err = ctx.GSContext.Get(&b, "456_NewChild") if err != nil { t.Fatal(err) } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index ee5aa599..2a5c9107 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -237,11 +237,11 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } } -type byOrder []*gs.BeanDefinition +type byBeanName []*gs.BeanDefinition -func (b byOrder) Len() int { return len(b) } -func (b byOrder) Less(i, j int) bool { return b[i].GetOrder() < b[j].GetOrder() } -func (b byOrder) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b byBeanName) Len() int { return len(b) } +func (b byBeanName) Less(i, j int) bool { return b[i].GetName() < b[j].GetName() } +func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 func filterBean(beans []*gs.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { @@ -375,7 +375,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, var ret reflect.Value switch t.Kind() { case reflect.Slice: - sort.Sort(byOrder(beans)) + sort.Sort(byBeanName(beans)) ret = reflect.MakeSlice(t, 0, 0) for _, b := range beans { ret = reflect.Append(ret, b.Value()) From b4c2f454febf877e736daa8906e9ae7bc7a83c7f Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 4 Jan 2025 17:46:43 +0800 Subject: [PATCH 32/75] stop add refreshable --- conf/bind.go | 6 +++-- dync/dync.go | 48 +++++++++++++++++++++---------------- dync/dync_test.go | 6 ++--- gs/internal/gs_core/wire.go | 7 ++++-- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/conf/bind.go b/conf/bind.go index ec78497d..996c4696 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -112,7 +112,9 @@ func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error { return nil } -type Filter func(i interface{}, param BindParam) (bool, error) +type Filter interface { + Do(i interface{}, param BindParam) (bool, error) +} // BindValue binds properties to a value. func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { @@ -359,7 +361,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } if filter != nil { - ret, err := filter(fv.Addr().Interface(), subParam) + ret, err := filter.Do(fv.Addr().Interface(), subParam) if err != nil { return err } diff --git a/dync/dync.go b/dync/dync.go index a7dd94fb..d19bd4b2 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -211,27 +211,43 @@ func (p *Properties) safeRefreshObject(f *refreshObject) (err error) { return f.target.OnRefresh(p.prop, f.param) } -// AddBean 添加一个可刷新对象 -func (p *Properties) AddBean(v Refreshable, param conf.BindParam) error { +// RefreshBean 刷新一个 bean 对象,根据 watch 的值决定是否添加监听 +func (p *Properties) RefreshBean(v Refreshable, param conf.BindParam, watch bool) error { p.lock.Lock() defer p.lock.Unlock() - return p.addObjectNoLock(v, param) + return p.doRefresh(v, param, watch) } -func (p *Properties) addObjectNoLock(v Refreshable, param conf.BindParam) error { - p.objects = append(p.objects, &refreshObject{ - target: v, - param: param, - }) +func (p *Properties) doRefresh(v Refreshable, param conf.BindParam, watch bool) error { + if watch { + p.objects = append(p.objects, &refreshObject{ + target: v, + param: param, + }) + } return v.OnRefresh(p.prop, param) } -// AddField 添加一个 bean 的 field -func (p *Properties) AddField(v reflect.Value, param conf.BindParam) error { +type filter struct { + *Properties + watch bool +} + +func (f *filter) Do(i interface{}, param conf.BindParam) (bool, error) { + v, ok := i.(Refreshable) + if !ok { + return false, nil + } + return true, f.doRefresh(v, param, f.watch) +} + +// RefreshField 刷新一个 bean 的 field,根据 watch 的值决定是否添加监听 +func (p *Properties) RefreshField(v reflect.Value, param conf.BindParam, watch bool) error { p.lock.Lock() defer p.lock.Unlock() + f := &filter{Properties: p, watch: watch} if v.Kind() == reflect.Ptr { - ok, err := p.bindValue(v.Interface(), param) + ok, err := f.Do(v.Interface(), param) if err != nil { return err } @@ -239,13 +255,5 @@ func (p *Properties) AddField(v reflect.Value, param conf.BindParam) error { return nil } } - return conf.BindValue(p.prop, v.Elem(), v.Elem().Type(), param, p.bindValue) -} - -func (p *Properties) bindValue(i interface{}, param conf.BindParam) (bool, error) { - v, ok := i.(Refreshable) - if !ok { - return false, nil - } - return true, p.addObjectNoLock(v, param) + return conf.BindValue(p.prop, v.Elem(), v.Elem().Type(), param, f) } diff --git a/dync/dync_test.go b/dync/dync_test.go index cafdcfc1..5cc15ff5 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -44,7 +44,7 @@ type Config struct { func newTest() (*dync.Properties, *Config, error) { mgr := dync.New() cfg := new(Config) - err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.RefreshField(reflect.ValueOf(cfg), conf.BindParam{}, true) if err != nil { return nil, nil, err } @@ -106,7 +106,7 @@ func TestDynamic(t *testing.T) { // fmt.Println("event fired.") // return nil // }) - err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.RefreshField(reflect.ValueOf(cfg), conf.BindParam{}, true) if err != nil { t.Fatal(err) } @@ -144,7 +144,7 @@ func TestDynamic(t *testing.T) { // fmt.Println("event fired.") // return nil // }) - err := mgr.AddField(reflect.ValueOf(cfg), conf.BindParam{}) + err := mgr.RefreshField(reflect.ValueOf(cfg), conf.BindParam{}, true) if err != nil { t.Fatal(err) } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 2a5c9107..4c965a26 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -592,7 +592,9 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 if b.IsRefreshEnable() { i := b.Interface().(dync.Refreshable) - if err = c.p.AddBean(i, b.GetRefreshParam()); err != nil { + refreshParam := b.GetRefreshParam() + watch := c.state == Refreshing + if err = c.p.RefreshBean(i, refreshParam, watch); err != nil { return err } } @@ -730,7 +732,8 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar return err } } else { - err := c.p.AddField(fv.Addr(), subParam) + watch := c.state == Refreshing + err := c.p.RefreshField(fv.Addr(), subParam, watch) if err != nil { return err } From de997635c2508dec500f811b83a42f97ecb1f16f Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 4 Jan 2025 18:59:49 +0800 Subject: [PATCH 33/75] code refactor --- gs/internal/gs/gs.go | 15 +++++++++++++-- gs/internal/gs_app/app.go | 15 +++++---------- gs/internal/gs_app/boot.go | 14 +++++--------- gs/internal/gs_core/core.go | 2 +- gs/internal/gs_core/core_test.go | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 78e52d72..9eacbeea 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -50,6 +50,19 @@ type Callable interface { Call(ctx ArgContext) ([]reflect.Value, error) } +type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) + +type Container interface { + Object(i interface{}) *BeanDefinition + Provide(ctor interface{}, args ...Arg) *BeanDefinition + Accept(b *BeanDefinition) *BeanDefinition + Group(fn GroupFunc) + RefreshProperties(p conf.ReadOnlyProperties) error + Refresh() error + Simplify() + Close() +} + // Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 // 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 // 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, @@ -71,8 +84,6 @@ type Context interface { Go(fn func(ctx context.Context)) } -type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) - // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { GSContext Context `autowire:""` diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index fe936f38..19fbe10a 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -40,11 +40,12 @@ type AppServer interface { // App 应用 type App struct { - c *gs_core.Container + c gs.Container p *gs_conf.AppConfig exitChan chan struct{} + Runners []AppRunner `autowire:"${spring.app.runners:=*?}"` Servers []AppServer `autowire:"${spring.app.servers:=*?}"` } @@ -112,20 +113,14 @@ func (app *App) Start() error { return err } - var runners []AppRunner - err = app.c.Get(&runners, "${spring.app.runners:=*?}") - if err != nil { - return err - } - // 执行命令行启动器 - for _, r := range runners { - r.Run(app.c) + for _, r := range app.Runners { + r.Run(app.c.(gs.Context)) } // 通知应用启动事件 for _, svr := range app.Servers { - svr.OnAppStart(app.c) + svr.OnAppStart(app.c.(gs.Context)) } app.c.Simplify() diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index f75760e4..0d1b1582 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -29,8 +29,10 @@ type BootRunner interface { } type Boot struct { - c *gs_core.Container + c gs.Container p *gs_conf.BootConfig + + Runners []AppRunner `autowire:"${spring.boot.runners:=*?}"` } func NewBoot() *Boot { @@ -82,15 +84,9 @@ func (b *Boot) Run() error { return err } - var runners []AppRunner - err = b.c.Get(&runners, "${spring.boot.runners:=*?}") - if err != nil { - return err - } - // 执行命令行启动器 - for _, r := range runners { - r.Run(b.c) + for _, r := range b.Runners { + r.Run(b.c.(gs.Context)) } b.c.Close() diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 4b33708a..c2d8247c 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -74,7 +74,7 @@ type Container struct { } // New 创建 IoC 容器。 -func New() *Container { +func New() gs.Container { ctx, cancel := context.WithCancel(context.Background()) c := &Container{ ctx: ctx, diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index dc2b8063..e4518e24 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -42,7 +42,7 @@ import ( "github.com/stretchr/testify/assert" ) -func runTest(c *gs_core.Container, fn func(gs.Context)) error { +func runTest(c gs.Container, fn func(gs.Context)) error { type PandoraAware struct{} c.Provide(func(p gs.Context) PandoraAware { fn(p) From 8bc7715e43be3b346c9bd314ed74ddae2858fc52 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 4 Jan 2025 21:24:11 +0800 Subject: [PATCH 34/75] bean configuration supports method returning BeanDefinition --- gs/internal/gs_core/core.go | 31 +++++++++++++++++++++++++------ gs/internal/gs_core/core_test.go | 12 +++++++++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index c2d8247c..78d45d54 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -52,6 +52,8 @@ const ( Refreshed // 已刷新 ) +var BeanDefinitionPtrType = reflect.TypeOf((*gs.BeanDefinition)(nil)) + // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -306,23 +308,40 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti continue } for _, x := range includes { - if x.MatchString(m.Name) { + if !x.MatchString(m.Name) { + continue + } + fnType := m.Func.Type() + out0 := fnType.Out(0) + if out0 == BeanDefinitionPtrType { + ret := m.Func.Call([]reflect.Value{bd.Value()}) + if len(ret) > 1 { + if err := ret[1].Interface().(error); err != nil { + return nil, err + } + } + b := ret[0].Interface().(*gs.BeanDefinition) + newBeans = append(newBeans, b) + retBeans, err := c.scanConfiguration(b) + if err != nil { + return nil, err + } + newBeans = append(newBeans, retBeans...) + } else { var f gs.Callable f, err := gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd}, 0) if err != nil { return nil, err } - name := bd.GetName() + "_" + m.Name - out0 := m.Type.Out(0) v := reflect.New(out0) if gs.IsBeanType(out0) { v = v.Elem() } - t := v.Type() - b := gs.NewBean(t, v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) + name := bd.GetName() + "_" + m.Name + b := gs.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) - break } + break } } return newBeans, nil diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index e4518e24..3e000bfd 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -3036,9 +3036,13 @@ func (c *ConfigurationBean) NewChild() *ChildBean { return &ChildBean{c.s} } +func (c *ConfigurationBean) NewBean() *gs.BeanDefinition { + return gs_core.NewBean(&ChildBean{"100"}).Name("100") +} + func TestConfiguration(t *testing.T) { c := gs_core.New() - c.Object(&ConfigurationBean{"123"}).Configuration(nil, nil).Name("123") + c.Object(&ConfigurationBean{"123"}).Configuration(nil, []string{"NewBean"}).Name("123") c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") ctx := &gs.ContextAware{} c.Object(ctx) @@ -3056,4 +3060,10 @@ func TestConfiguration(t *testing.T) { t.Fatal(err) } assert.Equal(t, b.s, "456") + var i *ChildBean + err = ctx.GSContext.Get(&i, "100") + if err != nil { + t.Fatal(err) + } + assert.Equal(t, i.s, "100") } From 8bc9632e3f58eb97403587dc815435787ed4a606 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 08:54:16 +0800 Subject: [PATCH 35/75] code refactor --- conf/bind.go | 6 +++--- conf/conf.go | 12 ++---------- gs/internal/gs_arg/arg.go | 3 +-- gs/internal/gs_core/bean.go | 4 ++-- gs/internal/gs_core/wire.go | 7 ++++--- gs/internal/gs_core/util.go => util/type.go | 2 +- conf/util.go => util/value.go | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) rename gs/internal/gs_core/util.go => util/type.go (99%) rename conf/util.go => util/value.go (99%) diff --git a/conf/bind.go b/conf/bind.go index 996c4696..ddbafa61 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -119,7 +119,7 @@ type Filter interface { // BindValue binds properties to a value. func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { - if !IsValueType(t) { + if !util.IsValueType(t) { err := errors.New("target should be value type") return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) } @@ -263,7 +263,7 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP if param.Tag.Def == "" { return nil, nil } - if !IsPrimitiveValueType(et) && converters[et] == nil { + if !util.IsPrimitiveValueType(et) && converters[et] == nil { return nil, fmt.Errorf("%s: can't find converter for %s", util.FileLine(), et.String()) } strVal = param.Tag.Def @@ -386,7 +386,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin continue } - if IsValueType(ft.Type) { + if util.IsValueType(ft.Type) { if subParam.Key == "" { subParam.Key = ft.Name } else { diff --git a/conf/conf.go b/conf/conf.go index df535a24..992888ff 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -22,7 +22,6 @@ import ( "os" "path/filepath" "reflect" - "sort" "strings" "time" @@ -141,15 +140,8 @@ func New() *Properties { // Map creates *Properties from map. func Map(m map[string]interface{}) (*Properties, error) { p := New() - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - if err := p.Set(k, m[k]); err != nil { - return nil, err - } + if err := p.Merge(m); err != nil { + return nil, err } return p, nil } diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 4b6d58e4..bd35011f 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -25,7 +25,6 @@ import ( "reflect" "runtime" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/util" ) @@ -232,7 +231,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine } // binds properties value by the "value" tag. - if conf.IsValueType(t) { + if util.IsValueType(t) { if tag == "" { tag = "${}" } diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index a6001c26..ddfb9049 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -23,9 +23,9 @@ import ( "runtime" "strings" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/util" ) // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 @@ -95,7 +95,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 } - if t.Kind() == reflect.Ptr && !conf.IsValueType(t.Elem()) { + if t.Kind() == reflect.Ptr && !util.IsValueType(t.Elem()) { panic(errors.New("bean should be *val but not *ref")) } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 4c965a26..4d576f0e 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -12,6 +12,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/util" ) var ( @@ -128,7 +129,7 @@ func (s *wiringStack) sortDestroyers() []func() { for _, d := range s.destroyerMap { destroyers.PushBack(d) } - destroyers = TripleSort(destroyers, getBeforeDestroyers) + destroyers = util.TripleSort(destroyers, getBeforeDestroyers) var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { @@ -636,7 +637,7 @@ func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (refl // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 if val := out[0]; gs.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 - if !val.IsNil() && val.Kind() == reflect.Interface && conf.IsValueType(val.Elem().Type()) { + if !val.IsNil() && val.Kind() == reflect.Interface && util.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) v.Elem().Set(val.Elem()) b.Value().Set(v) @@ -689,7 +690,7 @@ func (c *Container) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindPar fv := v.Field(i) if !fv.CanInterface() { - fv = PatchValue(fv) + fv = util.PatchValue(fv) if !fv.CanInterface() { continue } diff --git a/gs/internal/gs_core/util.go b/util/type.go similarity index 99% rename from gs/internal/gs_core/util.go rename to util/type.go index 87e802ee..e1c12b14 100644 --- a/gs/internal/gs_core/util.go +++ b/util/type.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package gs_core +package util import ( "container/list" diff --git a/conf/util.go b/util/value.go similarity index 99% rename from conf/util.go rename to util/value.go index 7c21b072..458b39a7 100644 --- a/conf/util.go +++ b/util/value.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package conf +package util import ( "reflect" From 3c9fb737bdf89dd45a9c7fef24fd7ef592842bd5 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 09:09:43 +0800 Subject: [PATCH 36/75] code refactor --- conf/conf.go | 5 +++-- conf/expr.go | 4 ++-- conf/expr_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++ gs/gs.go | 8 ++++++++ {conf => util}/flat.go | 2 +- 5 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 conf/expr_test.go rename {conf => util}/flat.go (99%) diff --git a/conf/conf.go b/conf/conf.go index 992888ff..84c7372e 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -30,6 +30,7 @@ import ( "github.com/go-spring/spring-core/conf/reader/toml" "github.com/go-spring/spring-core/conf/reader/yaml" "github.com/go-spring/spring-core/conf/store" + "github.com/go-spring/spring-core/util" "github.com/spf13/cast" ) @@ -179,7 +180,7 @@ func (p *Properties) Bytes(b []byte, ext string) error { // Merge flattens the map and sets all keys and values. func (p *Properties) Merge(m map[string]interface{}) error { - s := FlattenMap(m) + s := util.FlattenMap(m) return p.merge(s) } @@ -252,7 +253,7 @@ func (p *Properties) Set(key string, val interface{}) error { return errors.New("key is empty") } m := make(map[string]string) - FlattenValue(key, val, m) + util.FlattenValue(key, val, m) return p.merge(m) } diff --git a/conf/expr.go b/conf/expr.go index 8e5feba9..90b24369 100644 --- a/conf/expr.go +++ b/conf/expr.go @@ -22,11 +22,11 @@ import ( "github.com/expr-lang/expr" ) -type ValidateFunc interface{} +type ValidateFunc[T interface{}] func(T) bool var validateFuncs = map[string]interface{}{} -func RegisterValidateFunc(name string, fn ValidateFunc) { +func RegisterValidateFunc[T interface{}](name string, fn ValidateFunc[T]) { validateFuncs[name] = fn } diff --git a/conf/expr_test.go b/conf/expr_test.go new file mode 100644 index 00000000..79372a58 --- /dev/null +++ b/conf/expr_test.go @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package conf_test + +import ( + "testing" + + "github.com/go-spring/spring-core/conf" + "github.com/stretchr/testify/assert" +) + +func TestExpr(t *testing.T) { + conf.RegisterValidateFunc("checkInt", func(i int) bool { + return i < 5 + }) + var i struct { + A int `value:"${a}" expr:"checkInt($)"` + } + p, err := conf.Map(map[string]interface{}{ + "a": 4, + }) + if err != nil { + t.Fatal(err) + } + if err = p.Bind(&i); err != nil { + t.Fatal(err) + } + assert.Equal(t, 4, i.A) +} diff --git a/gs/gs.go b/gs/gs.go index 0856f4fd..8a18b62d 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -24,6 +24,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" "github.com/go-spring/spring-core/gs/internal/gs_core" + "github.com/go-spring/spring-core/gs/sysconf" ) const ( @@ -161,3 +162,10 @@ func printBanner() { } fmt.Println(string(padding) + Version + "\n") } + +/********************************** utility **********************************/ + +func AllowCircularReferences(allow bool) { + err := sysconf.Set("spring.allow-circular-references", allow) + _ = err // ignore error +} diff --git a/conf/flat.go b/util/flat.go similarity index 99% rename from conf/flat.go rename to util/flat.go index 6c887607..c5693267 100644 --- a/conf/flat.go +++ b/util/flat.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package conf +package util import ( "fmt" From d7f1fbead2e7681cac8a035b9768a8af0d21506d Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 13:04:33 +0800 Subject: [PATCH 37/75] code refactor --- conf/bind.go | 59 ++-- conf/bind_test.go | 2 +- conf/conf_test.go | 2 +- conf/expr_test.go | 2 +- conf/reader/prop/prop_test.go | 2 +- conf/reader/toml/toml_test.go | 2 +- conf/reader/yaml/yaml_test.go | 2 +- conf/store/path_test.go | 2 +- conf/store/store_test.go | 2 +- dync/dync.go | 6 +- dync/dync_test.go | 2 +- go.mod | 7 - go.sum | 8 - gs/gstest/gstest_test.go | 2 +- gs/internal/gs_arg/arg.go | 21 +- gs/internal/gs_arg/arg_test.go | 2 +- gs/internal/gs_cond/cond_test.go | 2 +- gs/internal/gs_core/bean_test.go | 2 +- gs/internal/gs_core/core_test.go | 22 +- gs/internal/gs_core/util.go | 90 +++++ gs/internal/gs_core/wire.go | 2 +- util/assert/assert.go | 231 +++++++++++++ util/assert/assert_mock.go | 68 ++++ util/assert/assert_test.go | 321 +++++++++++++++++ util/assert/string.go | 71 ++++ util/assert/string_test.go | 79 +++++ util/error.go | 19 -- util/error_test.go | 40 +++ util/macros/fileline.go | 57 ++++ util/macros/fileline_test.go | 125 +++++++ util/panic.go | 48 +++ util/panic_test.go | 43 +++ util/testdata/pkg.go | 33 ++ util/testdata/pkg/bar/pkg.go | 28 ++ util/testdata/pkg/foo/pkg.go | 28 ++ util/type.go | 213 ++++++++---- util/type_mock.go | 192 +++++++++++ util/type_mock_test.go | 62 ++++ util/type_test.go | 567 +++++++++++++++++++++++++++++++ util/value.go | 83 ++--- util/value_test.go | 196 +++++++++++ 41 files changed, 2536 insertions(+), 209 deletions(-) create mode 100644 gs/internal/gs_core/util.go create mode 100644 util/assert/assert.go create mode 100644 util/assert/assert_mock.go create mode 100644 util/assert/assert_test.go create mode 100644 util/assert/string.go create mode 100644 util/assert/string_test.go create mode 100644 util/error_test.go create mode 100644 util/macros/fileline.go create mode 100644 util/macros/fileline_test.go create mode 100644 util/panic.go create mode 100644 util/panic_test.go create mode 100644 util/testdata/pkg.go create mode 100644 util/testdata/pkg/bar/pkg.go create mode 100644 util/testdata/pkg/foo/pkg.go create mode 100644 util/type_mock.go create mode 100644 util/type_mock_test.go create mode 100644 util/type_test.go create mode 100644 util/value_test.go diff --git a/conf/bind.go b/conf/bind.go index ddbafa61..ca79b91b 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/macros" ) var ( @@ -121,7 +122,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind if !util.IsValueType(t) { err := errors.New("target should be value type") - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } defer func() { @@ -143,21 +144,21 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind return bindSlice(p, v, t, param, filter) case reflect.Array: err := errors.New("use slice instead of array") - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) default: // for linter } fn := converters[t] if fn == nil && v.Kind() == reflect.Struct { if err := bindStruct(p, v, t, param, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } return nil } val, err := resolve(p, param) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } if fn != nil { @@ -165,7 +166,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind out := fnValue.Call([]reflect.Value{reflect.ValueOf(val)}) if !out[1].IsNil() { err = out[1].Interface().(error) - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } v.Set(out[0]) return nil @@ -178,28 +179,28 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind v.SetUint(u) return nil } - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 if i, err = strconv.ParseInt(val, 0, 0); err == nil { v.SetInt(i) return nil } - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) case reflect.Float32, reflect.Float64: var f float64 if f, err = strconv.ParseFloat(val, 64); err == nil { v.SetFloat(f) return nil } - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) case reflect.Bool: var b bool if b, err = strconv.ParseBool(val); err == nil { v.SetBool(b) return nil } - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) case reflect.String: v.SetString(val) return nil @@ -207,7 +208,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind } err = fmt.Errorf("unsupported bind type %q", t.String()) - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } // bindSlice binds properties to a slice value. @@ -216,7 +217,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind et := t.Elem() p, err := getSlice(p, et, param) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } slice := reflect.MakeSlice(t, 0, 0) @@ -237,7 +238,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind break } if err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } slice = reflect.Append(slice, e) } @@ -258,13 +259,13 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP strVal = p.Get(param.Key) } else { if !param.Tag.HasDef { - return nil, fmt.Errorf("%s: property %q %w", util.FileLine(), param.Key, ErrNotExist) + return nil, fmt.Errorf("%s: property %q %w", macros.FileLine(), param.Key, ErrNotExist) } if param.Tag.Def == "" { return nil, nil } if !util.IsPrimitiveValueType(et) && converters[et] == nil { - return nil, fmt.Errorf("%s: can't find converter for %s", util.FileLine(), et.String()) + return nil, fmt.Errorf("%s: can't find converter for %s", macros.FileLine(), et.String()) } strVal = param.Tag.Def } @@ -285,10 +286,10 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP } } else if fn, ok := splitters[s]; ok && fn != nil { if arrVal, err = fn(strVal); err != nil { - return nil, fmt.Errorf("%s: split error: %w, value: %q", util.FileLine(), err, strVal) + return nil, fmt.Errorf("%s: split error: %w, value: %q", macros.FileLine(), err, strVal) } } else { - return nil, fmt.Errorf("%s: unknown splitter %q", util.FileLine(), s) + return nil, fmt.Errorf("%s: unknown splitter %q", macros.FileLine(), s) } r := New() @@ -304,7 +305,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("map can't have a non-empty default value") - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } et := t.Elem() @@ -313,7 +314,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa keys, err := p.SubKeys(param.Key) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } for _, key := range keys { @@ -328,7 +329,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa } err = BindValue(p, e, et, subParam, filter) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } ret.SetMapIndex(reflect.ValueOf(key), e) } @@ -340,7 +341,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("struct can't have a non-empty default value") - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } for i := 0; i < t.NumField(); i++ { @@ -358,7 +359,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin if tag, ok := ft.Tag.Lookup("value"); ok { if err := subParam.BindTag(tag, ft.Tag); err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } if filter != nil { ret, err := filter.Do(fv.Addr().Interface(), subParam) @@ -370,7 +371,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin } } if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } continue } @@ -381,7 +382,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin continue } if err := bindStruct(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } continue } @@ -395,7 +396,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin subParam.Key = strings.ToLower(subParam.Key) subParam.Key = strings.ReplaceAll(subParam.Key, "_", ".") if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", util.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) } } } @@ -411,13 +412,13 @@ func resolve(p ReadOnlyProperties, param BindParam) (string, error) { } if p.Has(param.Key) { err := fmt.Errorf("property %q isn't simple value", param.Key) - return "", fmt.Errorf("%s: resolve property %q error, %w", util.FileLine(), param.Key, err) + return "", fmt.Errorf("%s: resolve property %q error, %w", macros.FileLine(), param.Key, err) } if param.Tag.HasDef { return resolveString(p, param.Tag.Def) } err := fmt.Errorf("property %q %w", param.Key, ErrNotExist) - return "", fmt.Errorf("%s: resolve property %q error, %w", util.FileLine(), param.Key, err) + return "", fmt.Errorf("%s: resolve property %q error, %w", macros.FileLine(), param.Key, err) } // resolveString returns property references processed string. @@ -455,7 +456,7 @@ func resolveString(p ReadOnlyProperties, s string) (string, error) { if end < 0 || count > 0 { err := ErrInvalidSyntax - return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) } var param BindParam @@ -463,12 +464,12 @@ func resolveString(p ReadOnlyProperties, s string) (string, error) { s1, err := resolve(p, param) if err != nil { - return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) } s2, err := resolveString(p, s[end+1:]) if err != nil { - return "", fmt.Errorf("%s: resolve string %q error, %w", util.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) } return s[:start] + s1 + s2, nil diff --git a/conf/bind_test.go b/conf/bind_test.go index 7d820635..f7dfdb0e 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) type DB struct { diff --git a/conf/conf_test.go b/conf/conf_test.go index f37a8796..751327a2 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -25,8 +25,8 @@ import ( "time" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/util/assert" "github.com/spf13/cast" - "github.com/stretchr/testify/assert" ) func TestProperties_Load(t *testing.T) { diff --git a/conf/expr_test.go b/conf/expr_test.go index 79372a58..c9d25b41 100644 --- a/conf/expr_test.go +++ b/conf/expr_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestExpr(t *testing.T) { diff --git a/conf/reader/prop/prop_test.go b/conf/reader/prop/prop_test.go index 01c1d285..b6bfffea 100644 --- a/conf/reader/prop/prop_test.go +++ b/conf/reader/prop/prop_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf/reader/prop" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestRead(t *testing.T) { diff --git a/conf/reader/toml/toml_test.go b/conf/reader/toml/toml_test.go index 6ca3606d..d054e186 100644 --- a/conf/reader/toml/toml_test.go +++ b/conf/reader/toml/toml_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf/reader/toml" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestRead(t *testing.T) { diff --git a/conf/reader/yaml/yaml_test.go b/conf/reader/yaml/yaml_test.go index 9f21ed39..36d91892 100644 --- a/conf/reader/yaml/yaml_test.go +++ b/conf/reader/yaml/yaml_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf/reader/yaml" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestRead(t *testing.T) { diff --git a/conf/store/path_test.go b/conf/store/path_test.go index 82b69803..e3afb346 100644 --- a/conf/store/path_test.go +++ b/conf/store/path_test.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf/store" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestSplitPath(t *testing.T) { diff --git a/conf/store/store_test.go b/conf/store/store_test.go index 901c0e4d..fc64c32e 100644 --- a/conf/store/store_test.go +++ b/conf/store/store_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf/store" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestStorage(t *testing.T) { diff --git a/dync/dync.go b/dync/dync.go index d19bd4b2..7594d64e 100644 --- a/dync/dync.go +++ b/dync/dync.go @@ -183,9 +183,11 @@ func (e *Errors) Append(err error) { // Error 实现 error 接口 func (e *Errors) Error() string { var sb strings.Builder - for _, err := range e.arr { + for i, err := range e.arr { sb.WriteString(err.Error()) - sb.WriteString("\n") + if i < len(e.arr)-1 { + sb.WriteString("\n") + } } return sb.String() } diff --git a/dync/dync_test.go b/dync/dync_test.go index 5cc15ff5..73c4bdb8 100644 --- a/dync/dync_test.go +++ b/dync/dync_test.go @@ -23,7 +23,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) // todo 自定义类型通过类型转换器实现刷新机制 diff --git a/go.mod b/go.mod index 00e7c32d..d66dca95 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,6 @@ require ( github.com/magiconair/properties v1.8.9 github.com/pelletier/go-toml v1.9.5 github.com/spf13/cast v1.7.1 - github.com/stretchr/testify v1.10.0 go.uber.org/mock v0.5.0 gopkg.in/yaml.v2 v2.4.0 ) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum index 9549e4b9..9e1eba97 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -14,19 +12,13 @@ github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gs/gstest/gstest_test.go b/gs/gstest/gstest_test.go index b7b76bb6..acaa9a72 100644 --- a/gs/gstest/gstest_test.go +++ b/gs/gstest/gstest_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/go-spring/spring-core/gs/gstest" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) func TestMain(m *testing.M) { diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index bd35011f..51b5a186 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -27,6 +27,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/macros" ) // IndexArg is an Arg that has an index. @@ -106,7 +107,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(util.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(macros.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else { fnArgs[arg.n] = arg.arg } @@ -116,7 +117,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(util.FileLine(), "function has no args but given %d", len(args)) + return nil, util.Errorf(macros.FileLine(), "function has no args but given %d", len(args)) } } } @@ -127,25 +128,25 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if !shouldIndex { - return nil, util.Errorf(util.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(macros.FileLine(), "the Args must have or have no index") } if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(util.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(macros.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else if fnArgs[arg.n] != nil { - return nil, util.Errorf(util.FileLine(), "found same index %d", arg.n) + return nil, util.Errorf(macros.FileLine(), "found same index %d", arg.n) } else { fnArgs[arg.n] = arg.arg } default: if shouldIndex { - return nil, util.Errorf(util.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(macros.FileLine(), "the Args must have or have no index") } if i < fixedArgCount { fnArgs[i] = arg } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(util.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) + return nil, util.Errorf(macros.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) } } } @@ -179,7 +180,7 @@ func (r *argList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, erro // option arg may not return a value when the condition is not met. v, err := r.getArg(ctx, arg, t, fileLine) if err != nil { - return nil, util.Wrapf(err, util.FileLine(), "returns error when getting %d arg", idx) + return nil, util.Wrapf(err, macros.FileLine(), "returns error when getting %d arg", idx) } if v.IsValid() { result = append(result, v) @@ -209,7 +210,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine switch g := arg.(type) { case *Callable: if results, err := g.Call(ctx); err != nil { - return reflect.Value{}, util.Wrapf(err, util.FileLine(), "") + return reflect.Value{}, util.Wrapf(err, macros.FileLine(), "") } else if len(results) < 1 { return reflect.Value{}, errors.New("") } else { @@ -251,7 +252,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return v, nil } - return reflect.Value{}, util.Errorf(util.FileLine(), "error type %s", t.String()) + return reflect.Value{}, util.Errorf(macros.FileLine(), "error type %s", t.String()) } // OptionArg Option 函数的参数绑定。 diff --git a/gs/internal/gs_arg/arg_test.go b/gs/internal/gs_arg/arg_test.go index fb590d2e..922ab78a 100644 --- a/gs/internal/gs_arg/arg_test.go +++ b/gs/internal/gs_arg/arg_test.go @@ -22,7 +22,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" "go.uber.org/mock/gomock" ) diff --git a/gs/internal/gs_cond/cond_test.go b/gs/internal/gs_cond/cond_test.go index afccdd03..e524d082 100644 --- a/gs/internal/gs_cond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -22,7 +22,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_cond" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" "go.uber.org/mock/gomock" ) diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index 551b17dc..74aeb61d 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -27,7 +27,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" - "github.com/stretchr/testify/assert" + "github.com/go-spring/spring-core/util/assert" ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 3e000bfd..6d269437 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -37,9 +37,9 @@ import ( pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" - util2 "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" + "github.com/go-spring/spring-core/util/macros" "github.com/spf13/cast" - "github.com/stretchr/testify/assert" ) func runTest(c gs.Container, fn func(gs.Context)) error { @@ -833,7 +833,7 @@ func NewManager() Manager { } func NewManagerRetError() (Manager, error) { - return localManager{}, util.Error(util2.FileLine(), "error") + return localManager{}, util.Error(macros.FileLine(), "error") } func NewManagerRetErrorNil() (Manager, error) { @@ -965,7 +965,7 @@ func (d *callDestroy) InitWithError() error { d.inited = true return nil } - return util.Error(util2.FileLine(), "error") + return util.Error(macros.FileLine(), "error") } func (d *callDestroy) DestroyWithError() error { @@ -973,7 +973,7 @@ func (d *callDestroy) DestroyWithError() error { d.destroyed = true return nil } - return util.Error(util2.FileLine(), "error") + return util.Error(macros.FileLine(), "error") } type nestedCallDestroy struct { @@ -1470,11 +1470,11 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { var s *Server err := p.Get(&s) - assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_test.Server\"") + assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Server\"") var consumer *Consumer err = p.Get(&consumer) - assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_test.Consumer\"") + assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) assert.Equal(t, bd.BeanName(), "Consumer") @@ -1631,7 +1631,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() - assert.Error(t, err, "can't find bean, bean:\"int:\" type:\"\\*gs_test.Server\"") + assert.Error(t, err, "can't find bean, bean:\"int:\" type:\"\\*gs_core_test.Server\"") }) t.Run("method bean selector beanId", func(t *testing.T) { @@ -1670,7 +1670,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() - assert.Error(t, err, "can't find bean, bean:\"NULL\" type:\"\\*gs_test.Server\"") + assert.Error(t, err, "can't find bean, bean:\"NULL\" type:\"\\*gs_core_test.Server\"") }) } @@ -2194,7 +2194,7 @@ func TestApplicationContext_BeanCache(t *testing.T) { c := gs_core.New() c.Object(func() {}).Export((*filter)(nil)) err := c.Refresh() - assert.Error(t, err, "doesn't implement interface gs_test.filter") + assert.Error(t, err, "doesn't implement interface gs_core_test.filter") }) t.Run("implement interface", func(t *testing.T) { @@ -2984,7 +2984,7 @@ func TestDynamic(t *testing.T) { p.Set("wrapper.slice[0]", 2) p.Set("wrapper.slice[1]", 1) err = c.RefreshProperties(p) - assert.Error(t, err, "validate failed on \"value:\\\"${int:=3}\\\" expr:\\\"$<6\\\"\" for value 9") + assert.Error(t, err, "validate failed on \"\\$<6\" for value 6") } { diff --git a/gs/internal/gs_core/util.go b/gs/internal/gs_core/util.go new file mode 100644 index 00000000..ddc438bf --- /dev/null +++ b/gs/internal/gs_core/util.go @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gs_core + +import ( + "container/list" + "errors" +) + +// GetBeforeItems 获取 sorting 中排在 current 前面的元素 +type GetBeforeItems func(sorting *list.List, current interface{}) *list.List + +// TripleSort 三路排序 +func TripleSort(sorting *list.List, fn GetBeforeItems) *list.List { + + toSort := list.New() // 待排序列表 + sorted := list.New() // 已排序列表 + processing := list.New() // 正在处理列表 + + toSort.PushBackList(sorting) + + for toSort.Len() > 0 { // 递归选出依赖链条最前端的元素 + tripleSortByAfter(sorting, toSort, sorted, processing, nil, fn) + } + return sorted +} + +// searchInList 在列表中查询指定元素,存在则返回列表项指针,不存在返回 nil。 +func searchInList(l *list.List, v interface{}) *list.Element { + for e := l.Front(); e != nil; e = e.Next() { + if e.Value == v { + return e + } + } + return nil +} + +// tripleSortByAfter 递归选出依赖链条最前端的元素 +func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, + processing *list.List, current interface{}, fn GetBeforeItems) { + + if current == nil { + current = toSort.Remove(toSort.Front()) + } + + // 将当前元素标记为正在处理 + processing.PushBack(current) + + // 获取排在当前元素前面的列表项,然后依次对它们进行排序 + for e := fn(sorting, current).Front(); e != nil; e = e.Next() { + c := e.Value + + // 自己不可能是自己前面的元素,除非出现了循环依赖,因此抛出 Panic + if searchInList(processing, c) != nil { + panic(errors.New("found sorting cycle")) + } + + inSorted := searchInList(sorted, c) != nil + inToSort := searchInList(toSort, c) != nil + + if !inSorted && inToSort { // 如果是待排元素则对其进行排序 + tripleSortByAfter(sorting, toSort, sorted, processing, c, fn) + } + } + + if e := searchInList(processing, current); e != nil { + processing.Remove(e) + } + + if e := searchInList(toSort, current); e != nil { + toSort.Remove(e) + } + + // 将当前元素标记为已完成 + sorted.PushBack(current) +} diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 4d576f0e..15ad28a4 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -129,7 +129,7 @@ func (s *wiringStack) sortDestroyers() []func() { for _, d := range s.destroyerMap { destroyers.PushBack(d) } - destroyers = util.TripleSort(destroyers, getBeforeDestroyers) + destroyers = TripleSort(destroyers, getBeforeDestroyers) var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { diff --git a/util/assert/assert.go b/util/assert/assert.go new file mode 100644 index 00000000..d7f266fa --- /dev/null +++ b/util/assert/assert.go @@ -0,0 +1,231 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//go:generate mockgen -build_flags="-mod=mod" -package=assert -source=assert.go -destination=assert_mock.go + +// Package assert provides some useful assertion methods. +package assert + +import ( + "encoding/json" + "fmt" + "reflect" + "regexp" + "strings" +) + +// T is the minimum interface of *testing.T. +type T interface { + Helper() + Error(args ...interface{}) +} + +func fail(t T, str string, msg ...string) { + t.Helper() + args := append([]string{str}, msg...) + t.Error(strings.Join(args, "; ")) +} + +// True assertion failed when got is false. +func True(t T, got bool, msg ...string) { + t.Helper() + if !got { + fail(t, "got false but expect true", msg...) + } +} + +// False assertion failed when got is true. +func False(t T, got bool, msg ...string) { + t.Helper() + if got { + fail(t, "got true but expect false", msg...) + } +} + +// isNil reports v is nil, but will not panic. +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Chan, + reflect.Func, + reflect.Interface, + reflect.Map, + reflect.Ptr, + reflect.Slice, + reflect.UnsafePointer: + return v.IsNil() + } + return !v.IsValid() +} + +// Nil assertion failed when got is not nil. +func Nil(t T, got interface{}, msg ...string) { + t.Helper() + // Why can't we use got==nil to judge?Because if + // a := (*int)(nil) // %T == *int + // b := (interface{})(nil) // %T == + // then a==b is false, because they are different types. + if !isNil(reflect.ValueOf(got)) { + str := fmt.Sprintf("got (%T) %v but expect nil", got, got) + fail(t, str, msg...) + } +} + +// NotNil assertion failed when got is nil. +func NotNil(t T, got interface{}, msg ...string) { + t.Helper() + if isNil(reflect.ValueOf(got)) { + fail(t, "got nil but expect not nil", msg...) + } +} + +// Equal assertion failed when got and expect are not `deeply equal`. +func Equal(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + if !reflect.DeepEqual(got, expect) { + str := fmt.Sprintf("got (%T) %v but expect (%T) %v", got, got, expect, expect) + fail(t, str, msg...) + } +} + +// NotEqual assertion failed when got and expect are `deeply equal`. +func NotEqual(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + if reflect.DeepEqual(got, expect) { + str := fmt.Sprintf("got (%T) %v but expect not (%T) %v", got, got, expect, expect) + fail(t, str, msg...) + } +} + +// JsonEqual assertion failed when got and expect are not `json equal`. +func JsonEqual(t T, got string, expect string, msg ...string) { + t.Helper() + var gotJson interface{} + if err := json.Unmarshal([]byte(got), &gotJson); err != nil { + fail(t, err.Error(), msg...) + return + } + var expectJson interface{} + if err := json.Unmarshal([]byte(expect), &expectJson); err != nil { + fail(t, err.Error(), msg...) + return + } + if !reflect.DeepEqual(gotJson, expectJson) { + str := fmt.Sprintf("got (%T) %v but expect (%T) %v", got, got, expect, expect) + fail(t, str, msg...) + } +} + +// Same assertion failed when got and expect are not same. +func Same(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + if got != expect { + str := fmt.Sprintf("got (%T) %v but expect (%T) %v", got, got, expect, expect) + fail(t, str, msg...) + } +} + +// NotSame assertion failed when got and expect are same. +func NotSame(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + if got == expect { + str := fmt.Sprintf("expect not (%T) %v", expect, expect) + fail(t, str, msg...) + } +} + +// Panic assertion failed when fn doesn't panic or not match expr expression. +func Panic(t T, fn func(), expr string, msg ...string) { + t.Helper() + str := recovery(fn) + if str == "<>" { + fail(t, "did not panic", msg...) + } else { + matches(t, str, expr, msg...) + } +} + +func recovery(fn func()) (str string) { + defer func() { + if r := recover(); r != nil { + str = fmt.Sprint(r) + } + }() + fn() + return "<>" +} + +// Matches assertion failed when got doesn't match expr expression. +func Matches(t T, got string, expr string, msg ...string) { + t.Helper() + matches(t, got, expr, msg...) +} + +// Error assertion failed when got `error` doesn't match expr expression. +func Error(t T, got error, expr string, msg ...string) { + t.Helper() + if got == nil { + fail(t, "expect not nil error", msg...) + return + } + matches(t, got.Error(), expr, msg...) +} + +func matches(t T, got string, expr string, msg ...string) { + t.Helper() + if ok, err := regexp.MatchString(expr, got); err != nil { + fail(t, "invalid pattern", msg...) + } else if !ok { + str := fmt.Sprintf("got %q which does not match %q", got, expr) + fail(t, str, msg...) + } +} + +// TypeOf assertion failed when got and expect are not same type. +func TypeOf(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + e1 := reflect.TypeOf(got) + e2 := reflect.TypeOf(expect) + if e2.Kind() == reflect.Ptr && e2.Elem().Kind() == reflect.Interface { + e2 = e2.Elem() + } + + if !e1.AssignableTo(e2) { + str := fmt.Sprintf("got type (%s) but expect type (%s)", e1, e2) + fail(t, str, msg...) + } +} + +// Implements assertion failed when got doesn't implement expect. +func Implements(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + e1 := reflect.TypeOf(got) + e2 := reflect.TypeOf(expect) + if e2.Kind() == reflect.Ptr { + if e2.Elem().Kind() == reflect.Interface { + e2 = e2.Elem() + } else { + fail(t, "expect should be interface", msg...) + return + } + } + + if !e1.Implements(e2) { + str := fmt.Sprintf("got type (%s) but expect type (%s)", e1, e2) + fail(t, str, msg...) + } +} diff --git a/util/assert/assert_mock.go b/util/assert/assert_mock.go new file mode 100644 index 00000000..7391c0d7 --- /dev/null +++ b/util/assert/assert_mock.go @@ -0,0 +1,68 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: assert.go +// +// Generated by this command: +// +// mockgen -build_flags="-mod=mod" -package=assert -source=assert.go -destination=assert_mock.go +// + +// Package assert is a generated GoMock package. +package assert + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockT is a mock of T interface. +type MockT struct { + ctrl *gomock.Controller + recorder *MockTMockRecorder + isgomock struct{} +} + +// MockTMockRecorder is the mock recorder for MockT. +type MockTMockRecorder struct { + mock *MockT +} + +// NewMockT creates a new mock instance. +func NewMockT(ctrl *gomock.Controller) *MockT { + mock := &MockT{ctrl: ctrl} + mock.recorder = &MockTMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockT) EXPECT() *MockTMockRecorder { + return m.recorder +} + +// Error mocks base method. +func (m *MockT) Error(args ...any) { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Error", varargs...) +} + +// Error indicates an expected call of Error. +func (mr *MockTMockRecorder) Error(args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockT)(nil).Error), args...) +} + +// Helper mocks base method. +func (m *MockT) Helper() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Helper") +} + +// Helper indicates an expected call of Helper. +func (mr *MockTMockRecorder) Helper() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Helper", reflect.TypeOf((*MockT)(nil).Helper)) +} diff --git a/util/assert/assert_test.go b/util/assert/assert_test.go new file mode 100644 index 00000000..fdadb4d4 --- /dev/null +++ b/util/assert/assert_test.go @@ -0,0 +1,321 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package assert_test + +import ( + "bytes" + "errors" + "fmt" + "io" + "testing" + + "github.com/go-spring/spring-core/util/assert" + "go.uber.org/mock/gomock" +) + +func runCase(t *testing.T, f func(g *assert.MockT)) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + g := assert.NewMockT(ctrl) + g.EXPECT().Helper().AnyTimes() + f(g) +} + +func TestTrue(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.True(g, true) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got false but expect true"}) + assert.True(g, false) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got false but expect true; param (index=0)"}) + assert.True(g, false, "param (index=0)") + }) +} + +func TestFalse(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.False(g, false) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got true but expect false"}) + assert.False(g, true) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got true but expect false; param (index=0)"}) + assert.False(g, true, "param (index=0)") + }) +} + +func TestNil(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Nil(g, nil) + }) + runCase(t, func(g *assert.MockT) { + assert.Nil(g, (*int)(nil)) + }) + runCase(t, func(g *assert.MockT) { + var a []string + assert.Nil(g, a) + }) + runCase(t, func(g *assert.MockT) { + var m map[string]string + assert.Nil(g, m) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 3 but expect nil"}) + assert.Nil(g, 3) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 3 but expect nil; param (index=0)"}) + assert.Nil(g, 3, "param (index=0)") + }) +} + +func TestNotNil(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.NotNil(g, 3) + }) + runCase(t, func(g *assert.MockT) { + a := make([]string, 0) + assert.NotNil(g, a) + }) + runCase(t, func(g *assert.MockT) { + m := make(map[string]string) + assert.NotNil(g, m) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got nil but expect not nil"}) + assert.NotNil(g, nil) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got nil but expect not nil; param (index=0)"}) + assert.NotNil(g, nil, "param (index=0)") + }) +} + +func TestEqual(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Equal(g, 0, 0) + }) + runCase(t, func(g *assert.MockT) { + assert.Equal(g, []string{"a"}, []string{"a"}) + }) + runCase(t, func(g *assert.MockT) { + assert.Equal(g, struct { + text string + }{text: "a"}, struct { + text string + }{text: "a"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (struct { Text string }) {a} but expect (struct { Text string \"json:\\\"text\\\"\" }) {a}"}) + assert.Equal(g, struct { + Text string + }{Text: "a"}, struct { + Text string `json:"text"` + }{Text: "a"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (struct { text string }) {a} but expect (struct { msg string }) {a}"}) + assert.Equal(g, struct { + text string + }{text: "a"}, struct { + msg string + }{msg: "a"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 0 but expect (string) 0"}) + assert.Equal(g, 0, "0") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 0 but expect (string) 0; param (index=0)"}) + assert.Equal(g, 0, "0", "param (index=0)") + }) +} + +func TestNotEqual(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.NotEqual(g, "0", 0) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got ([]string) [a] but expect not ([]string) [a]"}) + assert.NotEqual(g, []string{"a"}, []string{"a"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (string) 0 but expect not (string) 0"}) + assert.NotEqual(g, "0", "0") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (string) 0 but expect not (string) 0; param (index=0)"}) + assert.NotEqual(g, "0", "0", "param (index=0)") + }) +} + +func TestJsonEqual(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.JsonEqual(g, `{"a":0,"b":1}`, `{"b":1,"a":0}`) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"invalid character 'h' in literal true (expecting 'r')"}) + assert.JsonEqual(g, `this is an error`, `[{"b":1},{"a":0}]`) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"invalid character 'h' in literal true (expecting 'r')"}) + assert.JsonEqual(g, `{"a":0,"b":1}`, `this is an error`) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (string) {\"a\":0,\"b\":1} but expect (string) [{\"b\":1},{\"a\":0}]"}) + assert.JsonEqual(g, `{"a":0,"b":1}`, `[{"b":1},{"a":0}]`) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (string) {\"a\":0} but expect (string) {\"a\":1}; param (index=0)"}) + assert.JsonEqual(g, `{"a":0}`, `{"a":1}`, "param (index=0)") + }) +} + +func TestSame(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Same(g, "0", "0") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 0 but expect (string) 0"}) + assert.Same(g, 0, "0") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 0 but expect (string) 0; param (index=0)"}) + assert.Same(g, 0, "0", "param (index=0)") + }) +} + +func TestNotSame(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.NotSame(g, "0", 0) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"expect not (string) 0"}) + assert.NotSame(g, "0", "0") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"expect not (string) 0; param (index=0)"}) + assert.NotSame(g, "0", "0", "param (index=0)") + }) +} + +func TestPanic(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Panic(g, func() { panic("this is an error") }, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"did not panic"}) + assert.Panic(g, func() {}, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"invalid pattern"}) + assert.Panic(g, func() { panic("this is an error") }, "an error \\") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\""}) + assert.Panic(g, func() { panic("there's no error") }, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\"; param (index=0)"}) + assert.Panic(g, func() { panic("there's no error") }, "an error", "param (index=0)") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\""}) + assert.Panic(g, func() { panic(errors.New("there's no error")) }, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\""}) + assert.Panic(g, func() { panic(bytes.NewBufferString("there's no error")) }, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"[there's no error]\" which does not match \"an error\""}) + assert.Panic(g, func() { panic([]string{"there's no error"}) }, "an error") + }) +} + +func TestMatches(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Matches(g, "this is an error", "this is an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"invalid pattern"}) + assert.Matches(g, "this is an error", "an error \\") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\""}) + assert.Matches(g, "there's no error", "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\"; param (index=0)"}) + assert.Matches(g, "there's no error", "an error", "param (index=0)") + }) +} + +func TestError(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Error(g, errors.New("this is an error"), "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"invalid pattern"}) + assert.Error(g, errors.New("there's no error"), "an error \\") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"expect not nil error"}) + assert.Error(g, nil, "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"expect not nil error; param (index=0)"}) + assert.Error(g, nil, "an error", "param (index=0)") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\""}) + assert.Error(g, errors.New("there's no error"), "an error") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got \"there's no error\" which does not match \"an error\"; param (index=0)"}) + assert.Error(g, errors.New("there's no error"), "an error", "param (index=0)") + }) +} + +func TestTypeOf(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.TypeOf(g, new(int), (*int)(nil)) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got type (string) but expect type (fmt.Stringer)"}) + assert.TypeOf(g, "string", (*fmt.Stringer)(nil)) + }) +} + +func TestImplements(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.Implements(g, errors.New("error"), (*error)(nil)) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"expect should be interface"}) + assert.Implements(g, new(int), (*int)(nil)) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got type (*int) but expect type (io.Reader)"}) + assert.Implements(g, new(int), (*io.Reader)(nil)) + }) +} diff --git a/util/assert/string.go b/util/assert/string.go new file mode 100644 index 00000000..8304468d --- /dev/null +++ b/util/assert/string.go @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package assert + +import ( + "fmt" + "strings" +) + +// StringAssertion assertion for type string. +type StringAssertion struct { + t T + v string +} + +// String returns an assertion for type string. +func String(t T, v string) *StringAssertion { + return &StringAssertion{ + t: t, + v: v, + } +} + +// EqualFold assertion failed when v doesn't equal to `s` under Unicode case-folding. +func (a *StringAssertion) EqualFold(s string, msg ...string) { + a.t.Helper() + if !strings.EqualFold(a.v, s) { + fail(a.t, fmt.Sprintf("'%s' doesn't equal fold to '%s'", a.v, s), msg...) + } +} + +// HasPrefix assertion failed when v doesn't have prefix `prefix`. +func (a *StringAssertion) HasPrefix(prefix string, msg ...string) *StringAssertion { + a.t.Helper() + if !strings.HasPrefix(a.v, prefix) { + fail(a.t, fmt.Sprintf("'%s' doesn't have prefix '%s'", a.v, prefix), msg...) + } + return a +} + +// HasSuffix assertion failed when v doesn't have suffix `suffix`. +func (a *StringAssertion) HasSuffix(suffix string, msg ...string) *StringAssertion { + a.t.Helper() + if !strings.HasSuffix(a.v, suffix) { + fail(a.t, fmt.Sprintf("'%s' doesn't have suffix '%s'", a.v, suffix), msg...) + } + return a +} + +// Contains assertion failed when v doesn't contain substring `substr`. +func (a *StringAssertion) Contains(substr string, msg ...string) *StringAssertion { + a.t.Helper() + if !strings.Contains(a.v, substr) { + fail(a.t, fmt.Sprintf("'%s' doesn't contain substr '%s'", a.v, substr), msg...) + } + return a +} diff --git a/util/assert/string_test.go b/util/assert/string_test.go new file mode 100644 index 00000000..7473a661 --- /dev/null +++ b/util/assert/string_test.go @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package assert_test + +import ( + "testing" + + "github.com/go-spring/spring-core/util/assert" +) + +func TestString_EqualFold(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.String(g, "hello, world!").EqualFold("Hello, World!") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't equal fold to 'xxx'"}) + assert.String(g, "hello, world!").EqualFold("xxx") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't equal fold to 'xxx'; param (index=0)"}) + assert.String(g, "hello, world!").EqualFold("xxx", "param (index=0)") + }) +} + +func TestString_HasPrefix(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.String(g, "hello, world!").HasPrefix("hello") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't have prefix 'xxx'"}) + assert.String(g, "hello, world!").HasPrefix("xxx") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't have prefix 'xxx'; param (index=0)"}) + assert.String(g, "hello, world!").HasPrefix("xxx", "param (index=0)") + }) +} + +func TestString_HasSuffix(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.String(g, "hello, world!").HasSuffix("world!") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't have suffix 'xxx'"}) + assert.String(g, "hello, world!").HasSuffix("xxx") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't have suffix 'xxx'; param (index=0)"}) + assert.String(g, "hello, world!").HasSuffix("xxx", "param (index=0)") + }) +} + +func TestString_Contains(t *testing.T) { + runCase(t, func(g *assert.MockT) { + assert.String(g, "hello, world!").Contains("hello") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't contain substr 'xxx'"}) + assert.String(g, "hello, world!").Contains("xxx") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"'hello, world!' doesn't contain substr 'xxx'; param (index=0)"}) + assert.String(g, "hello, world!").Contains("xxx", "param (index=0)") + }) +} diff --git a/util/error.go b/util/error.go index e52a45af..a0e424e6 100644 --- a/util/error.go +++ b/util/error.go @@ -19,27 +19,8 @@ package util import ( "errors" "fmt" - "runtime" - "sync" ) -var frameMap sync.Map - -// FileLine returns the file name and line of the call point. -// In reality FileLine here costs less time than debug.Stack. -func FileLine() string { - rpc := make([]uintptr, 1) - runtime.Callers(3, rpc[:]) - pc := rpc[0] - if v, ok := frameMap.Load(pc); ok { - e := v.(*runtime.Frame) - return fmt.Sprintf("%s:%d", e.File, e.Line) - } - e, _ := runtime.CallersFrames(rpc).Next() - frameMap.Store(pc, &e) - return fmt.Sprintf("%s:%d", e.File, e.Line) -} - // ForbiddenMethod throws this error when calling a method is prohibited. var ForbiddenMethod = errors.New("forbidden method") diff --git a/util/error_test.go b/util/error_test.go new file mode 100644 index 00000000..d356478d --- /dev/null +++ b/util/error_test.go @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "testing" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" + "github.com/go-spring/spring-core/util/macros" +) + +func TestError(t *testing.T) { + + e0 := util.Error(macros.FileLine(), "error") + assert.Error(t, e0, ".*/error_test.go:29 error") + + e1 := util.Errorf(macros.FileLine(), "error: %d", 0) + assert.Error(t, e1, ".*/error_test.go:32 error: 0") + + e2 := util.Wrap(e0, macros.FileLine(), "error") + assert.Error(t, e2, ".*/error_test.go:35 error; .*/error_test.go:29 error") + + e3 := util.Wrapf(e1, macros.FileLine(), "error: %d", 1) + assert.Error(t, e3, ".*/error_test.go:38 error: 1; .*/error_test.go:32 error: 0") +} diff --git a/util/macros/fileline.go b/util/macros/fileline.go new file mode 100644 index 00000000..48334ab3 --- /dev/null +++ b/util/macros/fileline.go @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package macros + +import ( + "fmt" + "runtime" + "sync" +) + +var frameMap sync.Map + +func fileLine() (string, int) { + rpc := make([]uintptr, 1) + runtime.Callers(3, rpc[:]) + pc := rpc[0] + if v, ok := frameMap.Load(pc); ok { + e := v.(*runtime.Frame) + return e.File, e.Line + } + frame, _ := runtime.CallersFrames(rpc).Next() + frameMap.Store(pc, &frame) + return frame.File, frame.Line +} + +// File returns the file name of the call point. +func File() string { + file, _ := fileLine() + return file +} + +// Line returns the file line of the call point. +func Line() int { + _, line := fileLine() + return line +} + +// FileLine returns the file name and line of the call point. +// In reality macros.FileLine costs less time than debug.Stack. +func FileLine() string { + file, line := fileLine() + return fmt.Sprintf("%s:%d", file, line) +} diff --git a/util/macros/fileline_test.go b/util/macros/fileline_test.go new file mode 100644 index 00000000..0db0fc52 --- /dev/null +++ b/util/macros/fileline_test.go @@ -0,0 +1,125 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package macros_test + +import ( + "fmt" + "runtime/debug" + "testing" + "time" + + "github.com/go-spring/spring-core/util/assert" + "github.com/go-spring/spring-core/util/macros" +) + +func f1(t *testing.T) ([]string, time.Duration) { + ret, cost := f2(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 32) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f2(t *testing.T) ([]string, time.Duration) { + ret, cost := f3(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 42) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f3(t *testing.T) ([]string, time.Duration) { + ret, cost := f4(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 52) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f4(t *testing.T) ([]string, time.Duration) { + ret, cost := f5(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 62) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f5(t *testing.T) ([]string, time.Duration) { + ret, cost := f6(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 72) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f6(t *testing.T) ([]string, time.Duration) { + ret, cost := f7(t) + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 82) + start := time.Now() + fileLine := macros.FileLine() + cost += time.Since(start) + return append(ret, fileLine), cost +} + +func f7(t *testing.T) ([]string, time.Duration) { + assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") + assert.Equal(t, macros.Line(), 91) + { + start := time.Now() + _ = debug.Stack() + fmt.Println("\t", "debug.Stack cost", time.Since(start)) + } + start := time.Now() + fileLine := macros.FileLine() + cost := time.Since(start) + return []string{fileLine}, cost +} + +func TestFileLine(t *testing.T) { + for i := 0; i < 5; i++ { + fmt.Printf("loop %d\n", i) + ret, cost := f1(t) + fmt.Println("\t", ret) + fmt.Println("\t", "all macros.FileLine cost", cost) + } + // loop 0 + // debug.Stack cost 37.794µs + // all macros.FileLine cost 14.638µs + // loop 1 + // debug.Stack cost 11.699µs + // all macros.FileLine cost 6.398µs + // loop 2 + // debug.Stack cost 20.62µs + // all macros.FileLine cost 4.185µs + // loop 3 + // debug.Stack cost 11.736µs + // all macros.FileLine cost 4.274µs + // loop 4 + // debug.Stack cost 19.821µs + // all macros.FileLine cost 4.061µs +} diff --git a/util/panic.go b/util/panic.go new file mode 100644 index 00000000..2f209f06 --- /dev/null +++ b/util/panic.go @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util + +import ( + "fmt" +) + +// PanicCond panic condition. +type PanicCond struct { + fn func() interface{} +} + +// When throws a panic when `isPanic` is true. +func (p *PanicCond) When(isPanic bool) { + if isPanic { + panic(p.fn()) + } +} + +// NewPanicCond returns a panic condition. +func NewPanicCond(fn func() interface{}) *PanicCond { + return &PanicCond{fn} +} + +// Panic returns a panic condition that throws an error. +func Panic(err interface{}) *PanicCond { + return NewPanicCond(func() interface{} { return err }) +} + +// Panicf returns a panic condition that throws a formatted error. +func Panicf(format string, a ...interface{}) *PanicCond { + return NewPanicCond(func() interface{} { return fmt.Errorf(format, a...) }) +} diff --git a/util/panic_test.go b/util/panic_test.go new file mode 100644 index 00000000..3b468092 --- /dev/null +++ b/util/panic_test.go @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "errors" + "testing" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" +) + +func TestPanicCond(t *testing.T) { + + util.Panic("this is an error").When(false) + assert.Panic(t, func() { + util.Panic("this is an error").When(true) + }, "this is an error") + + util.Panic(errors.New("this is an error")).When(false) + assert.Panic(t, func() { + util.Panic(errors.New("this is an error")).When(true) + }, "this is an error") + + util.Panicf("this is an %s", "error").When(false) + assert.Panic(t, func() { + util.Panicf("this is an %s", "error").When(true) + }, "this is an error") +} diff --git a/util/testdata/pkg.go b/util/testdata/pkg.go new file mode 100644 index 00000000..78356224 --- /dev/null +++ b/util/testdata/pkg.go @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package testdata + +func FnNoArgs() {} + +func FnWithArgs(i int) {} + +type Receiver struct{} + +func (r Receiver) FnNoArgs() {} + +func (r Receiver) FnWithArgs(i int) {} + +func (r *Receiver) PtrFnNoArgs() {} + +func (r *Receiver) PtrFnWithArgs(i int) {} + +func (r *Receiver) String() string { return "" } diff --git a/util/testdata/pkg/bar/pkg.go b/util/testdata/pkg/bar/pkg.go new file mode 100644 index 00000000..f807a4d7 --- /dev/null +++ b/util/testdata/pkg/bar/pkg.go @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pkg + +import ( + "fmt" +) + +// SamePkg golang allows packages with the same name under different paths. +type SamePkg struct{} + +func (p *SamePkg) Package() { + fmt.Println("github.com/go-spring/spring-base/util/testdata/pkg/bar/pkg.SamePkg") +} diff --git a/util/testdata/pkg/foo/pkg.go b/util/testdata/pkg/foo/pkg.go new file mode 100644 index 00000000..4e22cc94 --- /dev/null +++ b/util/testdata/pkg/foo/pkg.go @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package pkg + +import ( + "fmt" +) + +// SamePkg golang allows packages with the same name under different paths. +type SamePkg struct{} + +func (p *SamePkg) Package() { + fmt.Println("github.com/go-spring/spring-base/util/testdata/pkg/foo/pkg.SamePkg") +} diff --git a/util/type.go b/util/type.go index e1c12b14..9d7523b4 100644 --- a/util/type.go +++ b/util/type.go @@ -14,94 +14,183 @@ * limitations under the License. */ +//go:generate mockgen -build_flags="-mod=mod" -package=util -source=type.go -destination=type_mock.go + package util import ( - "container/list" - "errors" + "context" "reflect" - "unsafe" -) - -const ( - flagStickyRO = 1 << 5 - flagEmbedRO = 1 << 6 - flagRO = flagStickyRO | flagEmbedRO + "strings" ) -// PatchValue makes an unexported field can be assignable. -func PatchValue(v reflect.Value) reflect.Value { - rv := reflect.ValueOf(&v) - flag := rv.Elem().FieldByName("flag") - ptrFlag := (*uintptr)(unsafe.Pointer(flag.UnsafeAddr())) - *ptrFlag = *ptrFlag &^ flagRO - return v -} +// errorType the reflection type of error. +var errorType = reflect.TypeOf((*error)(nil)).Elem() -// GetBeforeItems 获取 sorting 中排在 current 前面的元素 -type GetBeforeItems func(sorting *list.List, current interface{}) *list.List +// contextType the reflection type of context.Context. +var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() -// TripleSort 三路排序 -func TripleSort(sorting *list.List, fn GetBeforeItems) *list.List { +// TypeName returns a fully qualified name consisting of package path and type name. +func TypeName(i interface{}) string { - toSort := list.New() // 待排序列表 - sorted := list.New() // 已排序列表 - processing := list.New() // 正在处理列表 + var typ reflect.Type + switch o := i.(type) { + case reflect.Type: + typ = o + case reflect.Value: + typ = o.Type() + default: + typ = reflect.TypeOf(o) + } - toSort.PushBackList(sorting) + for { + if k := typ.Kind(); k == reflect.Ptr || k == reflect.Slice { + typ = typ.Elem() + } else { + break + } + } - for toSort.Len() > 0 { // 递归选出依赖链条最前端的元素 - tripleSortByAfter(sorting, toSort, sorted, processing, nil, fn) + if pkgPath := typ.PkgPath(); pkgPath != "" { + pkgPath = strings.TrimSuffix(pkgPath, "_test") + return pkgPath + "/" + typ.String() } - return sorted + return typ.String() // the path of built-in type is empty } -// searchInList 在列表中查询指定元素,存在则返回列表项指针,不存在返回 nil。 -func searchInList(l *list.List, v interface{}) *list.Element { - for e := l.Front(); e != nil; e = e.Next() { - if e.Value == v { - return e - } - } - return nil +// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as +// `(*error)(nil)`, or a BeanDefinition value. +type BeanSelector interface{} + +// A BeanDefinition describes a bean whose lifecycle is managed by IoC container. +type BeanDefinition interface { + Type() reflect.Type + Value() reflect.Value + Interface() interface{} + ID() string + BeanName() string + TypeName() string + Created() bool + Wired() bool } -// tripleSortByAfter 递归选出依赖链条最前端的元素 -func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, - processing *list.List, current interface{}, fn GetBeforeItems) { +// Converter converts string value into user-defined value. It should be function +// type, and its prototype is func(string)(type,error). +type Converter interface{} + +// IsConverter returns whether `t` is a converter type. +func IsConverter(t reflect.Type) bool { + return IsFuncType(t) && + t.NumIn() == 1 && + t.In(0).Kind() == reflect.String && + t.NumOut() == 2 && + (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) +} - if current == nil { - current = toSort.Remove(toSort.Front()) - } +// IsFuncType returns whether `t` is func type. +func IsFuncType(t reflect.Type) bool { + return t.Kind() == reflect.Func +} - // 将当前元素标记为正在处理 - processing.PushBack(current) +// IsErrorType returns whether `t` is error type. +func IsErrorType(t reflect.Type) bool { + return t == errorType || t.Implements(errorType) +} - // 获取排在当前元素前面的列表项,然后依次对它们进行排序 - for e := fn(sorting, current).Front(); e != nil; e = e.Next() { - c := e.Value +// IsContextType returns whether `t` is context.Context type. +func IsContextType(t reflect.Type) bool { + return t == contextType || t.Implements(contextType) +} - // 自己不可能是自己前面的元素,除非出现了循环依赖,因此抛出 Panic - if searchInList(processing, c) != nil { - panic(errors.New("found sorting cycle")) - } +// ReturnNothing returns whether the function has no return value. +func ReturnNothing(t reflect.Type) bool { + return t.NumOut() == 0 +} - inSorted := searchInList(sorted, c) != nil - inToSort := searchInList(toSort, c) != nil +// ReturnOnlyError returns whether the function returns only error value. +func ReturnOnlyError(t reflect.Type) bool { + return t.NumOut() == 1 && IsErrorType(t.Out(0)) +} - if !inSorted && inToSort { // 如果是待排元素则对其进行排序 - tripleSortByAfter(sorting, toSort, sorted, processing, c, fn) - } +// IsStructPtr returns whether it is the pointer type of structure. +func IsStructPtr(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct +} + +// IsConstructor returns whether `t` is a constructor type. What is a constructor? +// It should be a function first, has any number of inputs and supports the option +// pattern input, has one or two outputs and the second output should be an error. +func IsConstructor(t reflect.Type) bool { + returnError := t.NumOut() == 2 && IsErrorType(t.Out(1)) + return IsFuncType(t) && (t.NumOut() == 1 || returnError) +} + +// HasReceiver returns whether the function has a receiver. +func HasReceiver(t reflect.Type, receiver reflect.Value) bool { + if t.NumIn() < 1 { + return false } + t0 := t.In(0) + if t0.Kind() != reflect.Interface { + return t0 == receiver.Type() + } + return receiver.Type().Implements(t0) +} - if e := searchInList(processing, current); e != nil { - processing.Remove(e) +// IsPrimitiveValueType returns whether `t` is the primitive value type which only is +// int, unit, float, bool, string and complex. +func IsPrimitiveValueType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + case reflect.Complex64, reflect.Complex128: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Bool: + return true } + return false +} - if e := searchInList(toSort, current); e != nil { - toSort.Remove(e) +// IsValueType returns whether the input type is the primitive value type and their +// composite type including array, slice, map and struct, such as []int, [3]string, +// []string, map[int]int, map[string]string, etc. +func IsValueType(t reflect.Type) bool { + fn := func(t reflect.Type) bool { + return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct + } + switch t.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return fn(t.Elem()) + default: + return fn(t) } +} - // 将当前元素标记为已完成 - sorted.PushBack(current) +// IsBeanType returns whether `t` is a bean type. +func IsBeanType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface: + return true + case reflect.Ptr: + return t.Elem().Kind() == reflect.Struct + default: + return false + } +} + +// IsBeanReceiver returns whether the `t` is a bean receiver, a bean receiver can +// be a bean, a map or slice whose elements are beans. +func IsBeanReceiver(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + return IsBeanType(t.Elem()) + default: + return IsBeanType(t) + } } diff --git a/util/type_mock.go b/util/type_mock.go new file mode 100644 index 00000000..8daf8992 --- /dev/null +++ b/util/type_mock.go @@ -0,0 +1,192 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: type.go + +// Package util is a generated GoMock package. +package util + +import ( + reflect "reflect" + + "go.uber.org/mock/gomock" +) + +// MockBeanSelector is a mock of BeanSelector interface. +type MockBeanSelector struct { + ctrl *gomock.Controller + recorder *MockBeanSelectorMockRecorder +} + +// MockBeanSelectorMockRecorder is the mock recorder for MockBeanSelector. +type MockBeanSelectorMockRecorder struct { + mock *MockBeanSelector +} + +// NewMockBeanSelector creates a new mock instance. +func NewMockBeanSelector(ctrl *gomock.Controller) *MockBeanSelector { + mock := &MockBeanSelector{ctrl: ctrl} + mock.recorder = &MockBeanSelectorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBeanSelector) EXPECT() *MockBeanSelectorMockRecorder { + return m.recorder +} + +// MockBeanDefinition is a mock of BeanDefinition interface. +type MockBeanDefinition struct { + ctrl *gomock.Controller + recorder *MockBeanDefinitionMockRecorder +} + +// MockBeanDefinitionMockRecorder is the mock recorder for MockBeanDefinition. +type MockBeanDefinitionMockRecorder struct { + mock *MockBeanDefinition +} + +// NewMockBeanDefinition creates a new mock instance. +func NewMockBeanDefinition(ctrl *gomock.Controller) *MockBeanDefinition { + mock := &MockBeanDefinition{ctrl: ctrl} + mock.recorder = &MockBeanDefinitionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBeanDefinition) EXPECT() *MockBeanDefinitionMockRecorder { + return m.recorder +} + +// BeanName mocks base method. +func (m *MockBeanDefinition) BeanName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeanName") + ret0, _ := ret[0].(string) + return ret0 +} + +// BeanName indicates an expected call of BeanName. +func (mr *MockBeanDefinitionMockRecorder) BeanName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeanName", reflect.TypeOf((*MockBeanDefinition)(nil).BeanName)) +} + +// Created mocks base method. +func (m *MockBeanDefinition) Created() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Created") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Created indicates an expected call of Created. +func (mr *MockBeanDefinitionMockRecorder) Created() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Created", reflect.TypeOf((*MockBeanDefinition)(nil).Created)) +} + +// ID mocks base method. +func (m *MockBeanDefinition) ID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(string) + return ret0 +} + +// ID indicates an expected call of ID. +func (mr *MockBeanDefinitionMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockBeanDefinition)(nil).ID)) +} + +// Interface mocks base method. +func (m *MockBeanDefinition) Interface() interface{} { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Interface") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// Interface indicates an expected call of Interface. +func (mr *MockBeanDefinitionMockRecorder) Interface() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Interface", reflect.TypeOf((*MockBeanDefinition)(nil).Interface)) +} + +// Type mocks base method. +func (m *MockBeanDefinition) Type() reflect.Type { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Type") + ret0, _ := ret[0].(reflect.Type) + return ret0 +} + +// Type indicates an expected call of Type. +func (mr *MockBeanDefinitionMockRecorder) Type() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*MockBeanDefinition)(nil).Type)) +} + +// TypeName mocks base method. +func (m *MockBeanDefinition) TypeName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TypeName") + ret0, _ := ret[0].(string) + return ret0 +} + +// TypeName indicates an expected call of TypeName. +func (mr *MockBeanDefinitionMockRecorder) TypeName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TypeName", reflect.TypeOf((*MockBeanDefinition)(nil).TypeName)) +} + +// Value mocks base method. +func (m *MockBeanDefinition) Value() reflect.Value { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Value") + ret0, _ := ret[0].(reflect.Value) + return ret0 +} + +// Value indicates an expected call of Value. +func (mr *MockBeanDefinitionMockRecorder) Value() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockBeanDefinition)(nil).Value)) +} + +// Wired mocks base method. +func (m *MockBeanDefinition) Wired() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Wired") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Wired indicates an expected call of Wired. +func (mr *MockBeanDefinitionMockRecorder) Wired() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wired", reflect.TypeOf((*MockBeanDefinition)(nil).Wired)) +} + +// MockConverter is a mock of Converter interface. +type MockConverter struct { + ctrl *gomock.Controller + recorder *MockConverterMockRecorder +} + +// MockConverterMockRecorder is the mock recorder for MockConverter. +type MockConverterMockRecorder struct { + mock *MockConverter +} + +// NewMockConverter creates a new mock instance. +func NewMockConverter(ctrl *gomock.Controller) *MockConverter { + mock := &MockConverter{ctrl: ctrl} + mock.recorder = &MockConverterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockConverter) EXPECT() *MockConverterMockRecorder { + return m.recorder +} diff --git a/util/type_mock_test.go b/util/type_mock_test.go new file mode 100644 index 00000000..ad4ae055 --- /dev/null +++ b/util/type_mock_test.go @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "reflect" + "testing" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" + "go.uber.org/mock/gomock" +) + +func TestBeanSelector(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + g := util.NewMockBeanSelector(ctrl) + g.EXPECT() +} + +func TestBeanDefinition(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + g := util.NewMockBeanDefinition(ctrl) + g.EXPECT().Type().Return(reflect.TypeOf(3)) + assert.Equal(t, g.Type(), reflect.TypeOf(3)) + g.EXPECT().Value().Return(reflect.ValueOf(3)) + assert.Equal(t, g.Value(), reflect.ValueOf(3)) + g.EXPECT().BeanName().Return("") + assert.Equal(t, g.BeanName(), "") + g.EXPECT().TypeName().Return("") + assert.Equal(t, g.TypeName(), "") + g.EXPECT().ID().Return("") + assert.Equal(t, g.ID(), "") + g.EXPECT().Created().Return(false) + assert.Equal(t, g.Created(), false) + g.EXPECT().Wired().Return(true) + assert.Equal(t, g.Wired(), true) + g.EXPECT().Interface().Return(nil) + assert.Equal(t, g.Interface(), nil) +} + +func TestConverter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + g := util.NewMockConverter(ctrl) + g.EXPECT() +} diff --git a/util/type_test.go b/util/type_test.go new file mode 100644 index 00000000..c46867aa --- /dev/null +++ b/util/type_test.go @@ -0,0 +1,567 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "reflect" + "testing" + "unsafe" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" + "github.com/go-spring/spring-core/util/testdata" + pkg1 "github.com/go-spring/spring-core/util/testdata/pkg/bar" + pkg2 "github.com/go-spring/spring-core/util/testdata/pkg/foo" +) + +type SamePkg struct{} + +func (p *SamePkg) Package() { + fmt.Println("github.com/go-spring/spring-core/util/util_test.SamePkg") +} + +func TestPkgPath(t *testing.T) { + // the name and package path of built-in type are empty. + + data := []struct { + typ reflect.Type + kind reflect.Kind + name string + pkg string + }{ + { + reflect.TypeOf(false), + reflect.Bool, + "bool", + "", + }, + { + reflect.TypeOf(new(bool)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]bool, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(int(3)), + reflect.Int, + "int", + "", + }, + { + reflect.TypeOf(new(int)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]int, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(uint(3)), + reflect.Uint, + "uint", + "", + }, + { + reflect.TypeOf(new(uint)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]uint, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(float32(3)), + reflect.Float32, + "float32", + "", + }, + { + reflect.TypeOf(new(float32)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]float32, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(complex64(3)), + reflect.Complex64, + "complex64", + "", + }, + { + reflect.TypeOf(new(complex64)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]complex64, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf("3"), + reflect.String, + "string", + "", + }, + { + reflect.TypeOf(new(string)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]string, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(map[int]int{}), + reflect.Map, + "", + "", + }, + { + reflect.TypeOf(new(map[int]int)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]map[int]int, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(pkg1.SamePkg{}), + reflect.Struct, + "SamePkg", + "github.com/go-spring/spring-core/util/testdata/pkg/bar", + }, + { + reflect.TypeOf(new(pkg1.SamePkg)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]pkg1.SamePkg, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(make([]*pkg1.SamePkg, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf(pkg2.SamePkg{}), + reflect.Struct, + "SamePkg", + "github.com/go-spring/spring-core/util/testdata/pkg/foo", + }, + { + reflect.TypeOf(new(pkg2.SamePkg)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf(make([]pkg2.SamePkg, 0)), + reflect.Slice, + "", + "", + }, + { + reflect.TypeOf((*error)(nil)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf((*error)(nil)).Elem(), + reflect.Interface, + "error", + "", + }, + { + reflect.TypeOf((*io.Reader)(nil)), + reflect.Ptr, + "", + "", + }, + { + reflect.TypeOf((*io.Reader)(nil)).Elem(), + reflect.Interface, + "Reader", + "io", + }, + } + + for _, d := range data { + assert.Equal(t, d.typ.Kind(), d.kind) + assert.Equal(t, d.typ.Name(), d.name) + assert.Equal(t, d.typ.PkgPath(), d.pkg) + } +} + +func TestTypeName(t *testing.T) { + + data := map[interface{}]struct { + typeName string + baseName string + }{ + reflect.TypeOf(3): {"int", "int"}, + reflect.TypeOf(new(int)): {"int", "*int"}, + reflect.TypeOf(make([]int, 0)): {"int", "[]int"}, + reflect.TypeOf(&[]int{3}): {"int", "*[]int"}, + reflect.TypeOf(make([]*int, 0)): {"int", "[]*int"}, + reflect.TypeOf(make([][]int, 0)): {"int", "[][]int"}, + reflect.TypeOf(make(map[int]int)): {"map[int]int", "map[int]int"}, + + reflect.TypeOf(int8(3)): {"int8", "int8"}, + reflect.TypeOf(new(int8)): {"int8", "*int8"}, + reflect.TypeOf(make([]int8, 0)): {"int8", "[]int8"}, + reflect.TypeOf(&[]int8{3}): {"int8", "*[]int8"}, + reflect.TypeOf(make(map[int8]int8)): {"map[int8]int8", "map[int8]int8"}, + + reflect.TypeOf(int16(3)): {"int16", "int16"}, + reflect.TypeOf(new(int16)): {"int16", "*int16"}, + reflect.TypeOf(make([]int16, 0)): {"int16", "[]int16"}, + reflect.TypeOf(&[]int16{3}): {"int16", "*[]int16"}, + reflect.TypeOf(make(map[int16]int16)): {"map[int16]int16", "map[int16]int16"}, + + reflect.TypeOf(int32(3)): {"int32", "int32"}, + reflect.TypeOf(new(int32)): {"int32", "*int32"}, + reflect.TypeOf(make([]int32, 0)): {"int32", "[]int32"}, + reflect.TypeOf(&[]int32{3}): {"int32", "*[]int32"}, + reflect.TypeOf(make(map[int32]int32)): {"map[int32]int32", "map[int32]int32"}, + + reflect.TypeOf(int64(3)): {"int64", "int64"}, + reflect.TypeOf(new(int64)): {"int64", "*int64"}, + reflect.TypeOf(make([]int64, 0)): {"int64", "[]int64"}, + reflect.TypeOf(&[]int64{3}): {"int64", "*[]int64"}, + reflect.TypeOf(make(map[int64]int64)): {"map[int64]int64", "map[int64]int64"}, + + reflect.TypeOf(uint(3)): {"uint", "uint"}, + reflect.TypeOf(new(uint)): {"uint", "*uint"}, + reflect.TypeOf(make([]uint, 0)): {"uint", "[]uint"}, + reflect.TypeOf(&[]uint{3}): {"uint", "*[]uint"}, + reflect.TypeOf(make(map[uint]uint)): {"map[uint]uint", "map[uint]uint"}, + + reflect.TypeOf(uint8(3)): {"uint8", "uint8"}, + reflect.TypeOf(new(uint8)): {"uint8", "*uint8"}, + reflect.TypeOf(make([]uint8, 0)): {"uint8", "[]uint8"}, + reflect.TypeOf(&[]uint8{3}): {"uint8", "*[]uint8"}, + reflect.TypeOf(make(map[uint8]uint8)): {"map[uint8]uint8", "map[uint8]uint8"}, + + reflect.ValueOf(uint16(3)): {"uint16", "uint16"}, + reflect.ValueOf(new(uint16)): {"uint16", "*uint16"}, + reflect.ValueOf(make([]uint16, 0)): {"uint16", "[]uint16"}, + reflect.ValueOf(&[]uint16{3}): {"uint16", "*[]uint16"}, + reflect.ValueOf(make(map[uint16]uint16)): {"map[uint16]uint16", "map[uint16]uint16"}, + + reflect.ValueOf(uint32(3)): {"uint32", "uint32"}, + reflect.ValueOf(new(uint32)): {"uint32", "*uint32"}, + reflect.ValueOf(make([]uint32, 0)): {"uint32", "[]uint32"}, + reflect.ValueOf(&[]uint32{3}): {"uint32", "*[]uint32"}, + reflect.ValueOf(make(map[uint32]uint32)): {"map[uint32]uint32", "map[uint32]uint32"}, + + reflect.ValueOf(uint64(3)): {"uint64", "uint64"}, + reflect.ValueOf(new(uint64)): {"uint64", "*uint64"}, + reflect.ValueOf(make([]uint64, 0)): {"uint64", "[]uint64"}, + reflect.ValueOf(&[]uint64{3}): {"uint64", "*[]uint64"}, + reflect.ValueOf(make(map[uint64]uint64)): {"map[uint64]uint64", "map[uint64]uint64"}, + + reflect.ValueOf(true): {"bool", "bool"}, + reflect.ValueOf(new(bool)): {"bool", "*bool"}, + reflect.ValueOf(make([]bool, 0)): {"bool", "[]bool"}, + reflect.ValueOf(&[]bool{true}): {"bool", "*[]bool"}, + reflect.ValueOf(make(map[bool]bool)): {"map[bool]bool", "map[bool]bool"}, + + reflect.ValueOf(float32(3)): {"float32", "float32"}, + reflect.ValueOf(new(float32)): {"float32", "*float32"}, + reflect.ValueOf(make([]float32, 0)): {"float32", "[]float32"}, + reflect.ValueOf(&[]float32{3}): {"float32", "*[]float32"}, + reflect.ValueOf(make(map[float32]float32)): {"map[float32]float32", "map[float32]float32"}, + + float64(3): {"float64", "float64"}, + new(float64): {"float64", "*float64"}, + reflect.TypeOf(make([]float64, 0)): {"float64", "[]float64"}, + reflect.TypeOf(&[]float64{3}): {"float64", "*[]float64"}, + reflect.TypeOf(make(map[float64]float64)): {"map[float64]float64", "map[float64]float64"}, + + complex64(3): {"complex64", "complex64"}, + new(complex64): {"complex64", "*complex64"}, + reflect.TypeOf(make([]complex64, 0)): {"complex64", "[]complex64"}, + reflect.TypeOf(&[]complex64{3}): {"complex64", "*[]complex64"}, + reflect.TypeOf(make(map[complex64]complex64)): {"map[complex64]complex64", "map[complex64]complex64"}, + + complex128(3): {"complex128", "complex128"}, + new(complex128): {"complex128", "*complex128"}, + reflect.TypeOf(make([]complex128, 0)): {"complex128", "[]complex128"}, + reflect.TypeOf(&[]complex128{3}): {"complex128", "*[]complex128"}, + reflect.TypeOf(make(map[complex128]complex128)): {"map[complex128]complex128", "map[complex128]complex128"}, + + make(chan int): {"chan int", "chan int"}, + make(chan struct{}): {"chan struct {}", "chan struct {}"}, + reflect.TypeOf(func() {}): {"func()", "func()"}, + + reflect.TypeOf((*error)(nil)).Elem(): {"error", "error"}, + reflect.TypeOf((*fmt.Stringer)(nil)).Elem(): {"fmt/fmt.Stringer", "fmt.Stringer"}, + + "string": {"string", "string"}, + new(string): {"string", "*string"}, + reflect.TypeOf(make([]string, 0)): {"string", "[]string"}, + reflect.TypeOf(&[]string{"string"}): {"string", "*[]string"}, + reflect.TypeOf(make(map[string]string)): {"map[string]string", "map[string]string"}, + + pkg1.SamePkg{}: {"github.com/go-spring/spring-core/util/testdata/pkg/bar/pkg.SamePkg", "pkg.SamePkg"}, + new(pkg1.SamePkg): {"github.com/go-spring/spring-core/util/testdata/pkg/bar/pkg.SamePkg", "*pkg.SamePkg"}, + reflect.TypeOf(make([]pkg1.SamePkg, 0)): {"github.com/go-spring/spring-core/util/testdata/pkg/bar/pkg.SamePkg", "[]pkg.SamePkg"}, + reflect.TypeOf(&[]pkg1.SamePkg{}): {"github.com/go-spring/spring-core/util/testdata/pkg/bar/pkg.SamePkg", "*[]pkg.SamePkg"}, + reflect.TypeOf(make(map[int]pkg1.SamePkg)): {"map[int]pkg.SamePkg", "map[int]pkg.SamePkg"}, + + pkg2.SamePkg{}: {"github.com/go-spring/spring-core/util/testdata/pkg/foo/pkg.SamePkg", "pkg.SamePkg"}, + new(pkg2.SamePkg): {"github.com/go-spring/spring-core/util/testdata/pkg/foo/pkg.SamePkg", "*pkg.SamePkg"}, + reflect.TypeOf(make([]pkg2.SamePkg, 0)): {"github.com/go-spring/spring-core/util/testdata/pkg/foo/pkg.SamePkg", "[]pkg.SamePkg"}, + reflect.TypeOf(&[]pkg2.SamePkg{}): {"github.com/go-spring/spring-core/util/testdata/pkg/foo/pkg.SamePkg", "*[]pkg.SamePkg"}, + reflect.TypeOf(make(map[int]pkg2.SamePkg)): {"map[int]pkg.SamePkg", "map[int]pkg.SamePkg"}, + + SamePkg{}: {"github.com/go-spring/spring-core/util/util_test.SamePkg", "util_test.SamePkg"}, + new(SamePkg): {"github.com/go-spring/spring-core/util/util_test.SamePkg", "*util_test.SamePkg"}, + reflect.TypeOf(make([]SamePkg, 0)): {"github.com/go-spring/spring-core/util/util_test.SamePkg", "[]util_test.SamePkg"}, + reflect.TypeOf(&[]SamePkg{}): {"github.com/go-spring/spring-core/util/util_test.SamePkg", "*[]util_test.SamePkg"}, + reflect.TypeOf(make(map[int]SamePkg)): {"map[int]util_test.SamePkg", "map[int]util_test.SamePkg"}, + } + + for i, v := range data { + typeName := util.TypeName(i) + assert.Equal(t, typeName, v.typeName) + switch a := i.(type) { + case reflect.Type: + assert.Equal(t, a.String(), v.baseName) + case reflect.Value: + assert.Equal(t, a.Type().String(), v.baseName) + default: + assert.Equal(t, reflect.TypeOf(a).String(), v.baseName) + } + } +} + +func TestIsValueType(t *testing.T) { + + data := []struct { + i interface{} + v bool + }{ + {true, true}, // Bool + {int(1), true}, // Int + {int8(1), true}, // Int8 + {int16(1), true}, // Int16 + {int32(1), true}, // Int32 + {int64(1), true}, // Int64 + {uint(1), true}, // Uint + {uint8(1), true}, // Uint8 + {uint16(1), true}, // Uint16 + {uint32(1), true}, // Uint32 + {uint64(1), true}, // Uint64 + {uintptr(0), false}, // Uintptr + {float32(1), true}, // Float32 + {float64(1), true}, // Float64 + {complex64(1), true}, // Complex64 + {complex128(1), true}, // Complex128 + {[1]int{0}, true}, // Array + {make(chan struct{}), false}, // Chan + {func() {}, false}, // Func + {reflect.TypeOf((*error)(nil)).Elem(), false}, // Interface + {make(map[int]int), true}, // Map + {make(map[string]*int), false}, // + {new(int), false}, // Ptr + {new(struct{}), false}, // + {[]int{0}, true}, // Slice + {[]*int{}, false}, // + {"this is a string", true}, // String + {struct{}{}, true}, // Struct + {unsafe.Pointer(new(int)), false}, // UnsafePointer + } + + for _, d := range data { + var typ reflect.Type + switch i := d.i.(type) { + case reflect.Type: + typ = i + default: + typ = reflect.TypeOf(i) + } + if r := util.IsValueType(typ); d.v != r { + t.Errorf("%v expect %v but %v", typ, d.v, r) + } + } +} + +func TestIsBeanType(t *testing.T) { + + data := []struct { + i interface{} + v bool + }{ + {true, false}, // Bool + {int(1), false}, // Int + {int8(1), false}, // Int8 + {int16(1), false}, // Int16 + {int32(1), false}, // Int32 + {int64(1), false}, // Int64 + {uint(1), false}, // Uint + {uint8(1), false}, // Uint8 + {uint16(1), false}, // Uint16 + {uint32(1), false}, // Uint32 + {uint64(1), false}, // Uint64 + {uintptr(0), false}, // Uintptr + {float32(1), false}, // Float32 + {float64(1), false}, // Float64 + {complex64(1), false}, // Complex64 + {complex128(1), false}, // Complex128 + {[1]int{0}, false}, // Array + {make(chan struct{}), true}, // Chan + {func() {}, true}, // Func + {reflect.TypeOf((*error)(nil)).Elem(), true}, // Interface + {make(map[int]int), false}, // Map + {make(map[string]*int), false}, // + {new(int), false}, // + {new(struct{}), true}, // + {[]int{0}, false}, // Slice + {[]*int{}, false}, // + {"this is a string", false}, // String + {struct{}{}, false}, // Struct + {unsafe.Pointer(new(int)), false}, // UnsafePointer + } + + for _, d := range data { + var typ reflect.Type + switch i := d.i.(type) { + case reflect.Type: + typ = i + default: + typ = reflect.TypeOf(i) + } + if r := util.IsBeanType(typ); d.v != r { + t.Errorf("%v expect %v but %v", typ, d.v, r) + } + } +} + +func TestIsConverter(t *testing.T) { + assert.False(t, util.IsConverter(reflect.TypeOf(3))) + assert.False(t, util.IsConverter(reflect.TypeOf(func() {}))) + assert.False(t, util.IsConverter(reflect.TypeOf(func(key string) {}))) + assert.False(t, util.IsConverter(reflect.TypeOf(func(key string) string { return "" }))) + assert.True(t, util.IsConverter(reflect.TypeOf(func(key string) (string, error) { return "", nil }))) +} + +func TestIsErrorType(t *testing.T) { + err := fmt.Errorf("error") + assert.True(t, util.IsErrorType(reflect.TypeOf(err))) + err = os.ErrClosed + assert.True(t, util.IsErrorType(reflect.TypeOf(err))) +} + +func TestIsContextType(t *testing.T) { + ctx := context.TODO() + assert.True(t, util.IsContextType(reflect.TypeOf(ctx))) + ctx = context.WithValue(context.TODO(), "a", "3") + assert.True(t, util.IsContextType(reflect.TypeOf(ctx))) +} + +func TestReturnNothing(t *testing.T) { + assert.True(t, util.ReturnNothing(reflect.TypeOf(func() {}))) + assert.True(t, util.ReturnNothing(reflect.TypeOf(func(key string) {}))) + assert.False(t, util.ReturnNothing(reflect.TypeOf(func() string { return "" }))) +} + +func TestReturnOnlyError(t *testing.T) { + assert.True(t, util.ReturnOnlyError(reflect.TypeOf(func() error { return nil }))) + assert.True(t, util.ReturnOnlyError(reflect.TypeOf(func(string) error { return nil }))) + assert.False(t, util.ReturnOnlyError(reflect.TypeOf(func() (string, error) { return "", nil }))) +} + +func TestIsStructPtr(t *testing.T) { + assert.False(t, util.IsStructPtr(reflect.TypeOf(3))) + assert.False(t, util.IsStructPtr(reflect.TypeOf(func() {}))) + assert.False(t, util.IsStructPtr(reflect.TypeOf(struct{}{}))) + assert.False(t, util.IsStructPtr(reflect.TypeOf(struct{ a string }{}))) + assert.True(t, util.IsStructPtr(reflect.TypeOf(&struct{ a string }{}))) +} + +func TestIsConstructor(t *testing.T) { + assert.False(t, util.IsConstructor(reflect.TypeOf(func() {}))) + assert.True(t, util.IsConstructor(reflect.TypeOf(func() string { return "" }))) + assert.True(t, util.IsConstructor(reflect.TypeOf(func() *string { return nil }))) + assert.True(t, util.IsConstructor(reflect.TypeOf(func() *testdata.Receiver { return nil }))) + assert.True(t, util.IsConstructor(reflect.TypeOf(func() (*testdata.Receiver, error) { return nil, nil }))) + assert.False(t, util.IsConstructor(reflect.TypeOf(func() (bool, *testdata.Receiver, error) { return false, nil, nil }))) +} + +func TestHasReceiver(t *testing.T) { + assert.False(t, util.HasReceiver(reflect.TypeOf(func() {}), reflect.ValueOf(new(testdata.Receiver)))) + assert.True(t, util.HasReceiver(reflect.TypeOf(func(*testdata.Receiver) {}), reflect.ValueOf(new(testdata.Receiver)))) + assert.True(t, util.HasReceiver(reflect.TypeOf(func(*testdata.Receiver, int) {}), reflect.ValueOf(new(testdata.Receiver)))) + assert.True(t, util.HasReceiver(reflect.TypeOf(func(fmt.Stringer, int) {}), reflect.ValueOf(new(testdata.Receiver)))) + assert.False(t, util.HasReceiver(reflect.TypeOf(func(error, int) {}), reflect.ValueOf(new(testdata.Receiver)))) +} + +func TestIsBeanReceiver(t *testing.T) { + assert.False(t, util.IsBeanReceiver(reflect.TypeOf("abc"))) + assert.False(t, util.IsBeanReceiver(reflect.TypeOf(new(string)))) + assert.True(t, util.IsBeanReceiver(reflect.TypeOf(errors.New("abc")))) + assert.False(t, util.IsBeanReceiver(reflect.TypeOf([]string{}))) + assert.False(t, util.IsBeanReceiver(reflect.TypeOf([]*string{}))) + assert.True(t, util.IsBeanReceiver(reflect.TypeOf([]fmt.Stringer{}))) + assert.False(t, util.IsBeanReceiver(reflect.TypeOf(map[string]string{}))) + assert.False(t, util.IsBeanReceiver(reflect.TypeOf(map[string]*string{}))) + assert.True(t, util.IsBeanReceiver(reflect.TypeOf(map[string]fmt.Stringer{}))) +} diff --git a/util/value.go b/util/value.go index 458b39a7..dc0ffe6a 100644 --- a/util/value.go +++ b/util/value.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,60 +18,49 @@ package util import ( "reflect" + "runtime" + "strings" + "unsafe" ) -// errorType the reflection type of error. -var errorType = reflect.TypeOf((*error)(nil)).Elem() +const ( + flagStickyRO = 1 << 5 + flagEmbedRO = 1 << 6 + flagRO = flagStickyRO | flagEmbedRO +) -// IsFuncType returns whether `t` is func type. -func IsFuncType(t reflect.Type) bool { - return t.Kind() == reflect.Func +// PatchValue makes an unexported field can be assignable. +func PatchValue(v reflect.Value) reflect.Value { + rv := reflect.ValueOf(&v) + flag := rv.Elem().FieldByName("flag") + ptrFlag := (*uintptr)(unsafe.Pointer(flag.UnsafeAddr())) + *ptrFlag = *ptrFlag &^ flagRO + return v } -// IsErrorType returns whether `t` is error type. -func IsErrorType(t reflect.Type) bool { - return t == errorType || t.Implements(errorType) +// Indirect returns its element type when t is a pointer type. +func Indirect(t reflect.Type) reflect.Type { + if t.Kind() != reflect.Ptr { + return t + } + return t.Elem() } -// IsConverter returns whether `t` is a converter type. -func IsConverter(t reflect.Type) bool { - return IsFuncType(t) && - t.NumIn() == 1 && - t.In(0).Kind() == reflect.String && - t.NumOut() == 2 && - (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) -} +// FileLine returns a function's name, file name and line number. +func FileLine(fn interface{}) (file string, line int, fnName string) { -// IsValueType returns whether the input type is the primitive value type and their -// composite type including array, slice, map and struct, such as []int, [3]string, -// []string, map[int]int, map[string]string, etc. -func IsValueType(t reflect.Type) bool { - fn := func(t reflect.Type) bool { - return IsPrimitiveValueType(t) || t.Kind() == reflect.Struct - } - switch t.Kind() { - case reflect.Map, reflect.Slice, reflect.Array: - return fn(t.Elem()) - default: - return fn(t) - } -} + fnPtr := reflect.ValueOf(fn).Pointer() + fnInfo := runtime.FuncForPC(fnPtr) + file, line = fnInfo.FileLine(fnPtr) -// IsPrimitiveValueType returns whether `t` is the primitive value type which only is -// int, unit, float, bool, string and complex. -func IsPrimitiveValueType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Bool: - return true - default: - return false + s := fnInfo.Name() + if ss := strings.Split(s, "/"); len(ss) > 0 { + s = ss[len(ss)-1] + i := strings.Index(s, ".") + s = s[i+1:] } + + // method values are printed as "T.m-fm" + s = strings.TrimRight(s, "-fm") + return file, line, s } diff --git a/util/value_test.go b/util/value_test.go new file mode 100644 index 00000000..1ff1ea31 --- /dev/null +++ b/util/value_test.go @@ -0,0 +1,196 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "reflect" + "testing" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" + "github.com/go-spring/spring-core/util/testdata" +) + +func TestPatchValue(t *testing.T) { + var r struct{ v int } + v := reflect.ValueOf(&r) + v = v.Elem().Field(0) + assert.Panic(t, func() { + v.SetInt(4) + }, "using value obtained using unexported field") + v = util.PatchValue(v) + v.SetInt(4) +} + +func TestIndirect(t *testing.T) { + var r struct{ v int } + typ := reflect.TypeOf(r) + assert.Equal(t, util.Indirect(typ), reflect.TypeOf(r)) + typ = reflect.TypeOf(&r) + assert.Equal(t, util.Indirect(typ), reflect.TypeOf(r)) +} + +func fnNoArgs() {} + +func fnWithArgs(i int) {} + +type receiver struct{} + +func (r receiver) fnNoArgs() {} + +func (r receiver) fnWithArgs(i int) {} + +func (r *receiver) ptrFnNoArgs() {} + +func (r *receiver) ptrFnWithArgs(i int) {} + +func TestFileLine(t *testing.T) { + offset := 62 + testcases := []struct { + fn interface{} + file string + line int + fnName string + }{ + { + fn: fnNoArgs, + file: "spring-core/util/value_test.go", + line: offset - 15, + fnName: "fnNoArgs", + }, + { + fnWithArgs, + "spring-core/util/value_test.go", + offset - 13, + "fnWithArgs", + }, + { + receiver{}.fnNoArgs, + "spring-core/util/value_test.go", + offset - 9, + "receiver.fnNoArgs", + }, + { + receiver.fnNoArgs, + "spring-core/util/value_test.go", + offset - 9, + "receiver.fnNoArgs", + }, + { + receiver{}.fnWithArgs, + "spring-core/util/value_test.go", + offset - 7, + "receiver.fnWithArgs", + }, + { + receiver.fnWithArgs, + "spring-core/util/value_test.go", + offset - 7, + "receiver.fnWithArgs", + }, + { + (&receiver{}).ptrFnNoArgs, + "spring-core/util/value_test.go", + offset - 5, + "(*receiver).ptrFnNoArgs", + }, + { + (*receiver).ptrFnNoArgs, + "spring-core/util/value_test.go", + offset - 5, + "(*receiver).ptrFnNoArgs", + }, + { + (&receiver{}).ptrFnWithArgs, + "spring-core/util/value_test.go", + offset - 3, + "(*receiver).ptrFnWithArgs", + }, + { + (*receiver).ptrFnWithArgs, + "spring-core/util/value_test.go", + offset - 3, + "(*receiver).ptrFnWithArgs", + }, + { + testdata.FnNoArgs, + "spring-core/util/testdata/pkg.go", + 19, + "FnNoArgs", + }, + { + testdata.FnWithArgs, + "spring-core/util/testdata/pkg.go", + 21, + "FnWithArgs", + }, + { + testdata.Receiver{}.FnNoArgs, + "spring-core/util/testdata/pkg.go", + 25, + "Receiver.FnNoArgs", + }, + { + testdata.Receiver{}.FnWithArgs, + "spring-core/util/testdata/pkg.go", + 27, + "Receiver.FnWithArgs", + }, + { + (&testdata.Receiver{}).PtrFnNoArgs, + "spring-core/util/testdata/pkg.go", + 29, + "(*Receiver).PtrFnNoArgs", + }, + { + (&testdata.Receiver{}).PtrFnWithArgs, + "spring-core/util/testdata/pkg.go", + 31, + "(*Receiver).PtrFnWithArgs", + }, + { + testdata.Receiver.FnNoArgs, + "spring-core/util/testdata/pkg.go", + 25, + "Receiver.FnNoArgs", + }, + { + testdata.Receiver.FnWithArgs, + "spring-core/util/testdata/pkg.go", + 27, + "Receiver.FnWithArgs", + }, + { + (*testdata.Receiver).PtrFnNoArgs, + "spring-core/util/testdata/pkg.go", + 29, + "(*Receiver).PtrFnNoArgs", + }, + { + (*testdata.Receiver).PtrFnWithArgs, + "spring-core/util/testdata/pkg.go", + 31, + "(*Receiver).PtrFnWithArgs", + }, + } + for _, c := range testcases { + file, line, fnName := util.FileLine(c.fn) + assert.String(t, file).HasSuffix(c.file) + assert.Equal(t, line, c.line) + assert.Equal(t, fnName, c.fnName) + } +} From a32c16908c5480b68e702b23cfbbed998f775967 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 18:15:41 +0800 Subject: [PATCH 38/75] code refactor --- conf/bind.go | 60 ++++++++++++------------- gs/internal/gs_arg/arg.go | 22 ++++----- gs/internal/gs_core/core_test.go | 8 ++-- util/assert/assert.go | 2 +- util/assert/assert_test.go | 2 +- util/assert/string.go | 2 +- util/assert/string_test.go | 2 +- util/error.go | 2 +- util/error_test.go | 12 ++--- util/{macros => macro}/fileline.go | 6 +-- util/{macros => macro}/fileline_test.go | 60 ++++++++++++------------- util/panic.go | 2 +- util/panic_test.go | 2 +- util/testdata/pkg.go | 2 +- util/testdata/pkg/bar/pkg.go | 4 +- util/testdata/pkg/foo/pkg.go | 4 +- util/type_mock.go | 14 ++++-- util/type_mock_test.go | 2 +- util/type_test.go | 2 +- util/value.go | 2 +- util/value_test.go | 2 +- 21 files changed, 111 insertions(+), 103 deletions(-) rename util/{macros => macro}/fileline.go (90%) rename util/{macros => macro}/fileline_test.go (63%) diff --git a/conf/bind.go b/conf/bind.go index ca79b91b..110eaaf8 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -24,7 +24,7 @@ import ( "strings" "github.com/go-spring/spring-core/util" - "github.com/go-spring/spring-core/util/macros" + "github.com/go-spring/spring-core/util/macro" ) var ( @@ -122,7 +122,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind if !util.IsValueType(t) { err := errors.New("target should be value type") - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } defer func() { @@ -144,21 +144,21 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind return bindSlice(p, v, t, param, filter) case reflect.Array: err := errors.New("use slice instead of array") - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) default: // for linter } fn := converters[t] if fn == nil && v.Kind() == reflect.Struct { if err := bindStruct(p, v, t, param, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } return nil } val, err := resolve(p, param) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } if fn != nil { @@ -166,7 +166,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind out := fnValue.Call([]reflect.Value{reflect.ValueOf(val)}) if !out[1].IsNil() { err = out[1].Interface().(error) - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } v.Set(out[0]) return nil @@ -179,28 +179,28 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind v.SetUint(u) return nil } - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 if i, err = strconv.ParseInt(val, 0, 0); err == nil { v.SetInt(i) return nil } - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) case reflect.Float32, reflect.Float64: var f float64 if f, err = strconv.ParseFloat(val, 64); err == nil { v.SetFloat(f) return nil } - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) case reflect.Bool: var b bool if b, err = strconv.ParseBool(val); err == nil { v.SetBool(b) return nil } - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) case reflect.String: v.SetString(val) return nil @@ -208,7 +208,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind } err = fmt.Errorf("unsupported bind type %q", t.String()) - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } // bindSlice binds properties to a slice value. @@ -217,7 +217,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind et := t.Elem() p, err := getSlice(p, et, param) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } slice := reflect.MakeSlice(t, 0, 0) @@ -238,7 +238,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind break } if err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } slice = reflect.Append(slice, e) } @@ -259,13 +259,13 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP strVal = p.Get(param.Key) } else { if !param.Tag.HasDef { - return nil, fmt.Errorf("%s: property %q %w", macros.FileLine(), param.Key, ErrNotExist) + return nil, fmt.Errorf("%s: property %q %w", macro.FileLine(), param.Key, ErrNotExist) } if param.Tag.Def == "" { return nil, nil } if !util.IsPrimitiveValueType(et) && converters[et] == nil { - return nil, fmt.Errorf("%s: can't find converter for %s", macros.FileLine(), et.String()) + return nil, fmt.Errorf("%s: can't find converter for %s", macro.FileLine(), et.String()) } strVal = param.Tag.Def } @@ -286,10 +286,10 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP } } else if fn, ok := splitters[s]; ok && fn != nil { if arrVal, err = fn(strVal); err != nil { - return nil, fmt.Errorf("%s: split error: %w, value: %q", macros.FileLine(), err, strVal) + return nil, fmt.Errorf("%s: split error: %w, value: %q", macro.FileLine(), err, strVal) } } else { - return nil, fmt.Errorf("%s: unknown splitter %q", macros.FileLine(), s) + return nil, fmt.Errorf("%s: unknown splitter %q", macro.FileLine(), s) } r := New() @@ -305,7 +305,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("map can't have a non-empty default value") - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } et := t.Elem() @@ -314,7 +314,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa keys, err := p.SubKeys(param.Key) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } for _, key := range keys { @@ -329,7 +329,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa } err = BindValue(p, e, et, subParam, filter) if err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } ret.SetMapIndex(reflect.ValueOf(key), e) } @@ -341,7 +341,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("struct can't have a non-empty default value") - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } for i := 0; i < t.NumField(); i++ { @@ -359,7 +359,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin if tag, ok := ft.Tag.Lookup("value"); ok { if err := subParam.BindTag(tag, ft.Tag); err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } if filter != nil { ret, err := filter.Do(fv.Addr().Interface(), subParam) @@ -371,7 +371,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin } } if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } continue } @@ -382,7 +382,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin continue } if err := bindStruct(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } continue } @@ -396,7 +396,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin subParam.Key = strings.ToLower(subParam.Key) subParam.Key = strings.ReplaceAll(subParam.Key, "_", ".") if err := BindValue(p, fv, ft.Type, subParam, filter); err != nil { - return fmt.Errorf("%s: bind %s error, %w", macros.FileLine(), param.Path, err) + return fmt.Errorf("%s: bind %s error, %w", macro.FileLine(), param.Path, err) } } } @@ -412,13 +412,13 @@ func resolve(p ReadOnlyProperties, param BindParam) (string, error) { } if p.Has(param.Key) { err := fmt.Errorf("property %q isn't simple value", param.Key) - return "", fmt.Errorf("%s: resolve property %q error, %w", macros.FileLine(), param.Key, err) + return "", fmt.Errorf("%s: resolve property %q error, %w", macro.FileLine(), param.Key, err) } if param.Tag.HasDef { return resolveString(p, param.Tag.Def) } err := fmt.Errorf("property %q %w", param.Key, ErrNotExist) - return "", fmt.Errorf("%s: resolve property %q error, %w", macros.FileLine(), param.Key, err) + return "", fmt.Errorf("%s: resolve property %q error, %w", macro.FileLine(), param.Key, err) } // resolveString returns property references processed string. @@ -456,7 +456,7 @@ func resolveString(p ReadOnlyProperties, s string) (string, error) { if end < 0 || count > 0 { err := ErrInvalidSyntax - return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macro.FileLine(), s, err) } var param BindParam @@ -464,12 +464,12 @@ func resolveString(p ReadOnlyProperties, s string) (string, error) { s1, err := resolve(p, param) if err != nil { - return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macro.FileLine(), s, err) } s2, err := resolveString(p, s[end+1:]) if err != nil { - return "", fmt.Errorf("%s: resolve string %q error, %w", macros.FileLine(), s, err) + return "", fmt.Errorf("%s: resolve string %q error, %w", macro.FileLine(), s, err) } return s[:start] + s1 + s2, nil diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 51b5a186..2cf276d2 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -27,7 +27,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/util" - "github.com/go-spring/spring-core/util/macros" + "github.com/go-spring/spring-core/util/macro" ) // IndexArg is an Arg that has an index. @@ -107,7 +107,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(macros.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(macro.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else { fnArgs[arg.n] = arg.arg } @@ -117,7 +117,7 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(macros.FileLine(), "function has no args but given %d", len(args)) + return nil, util.Errorf(macro.FileLine(), "function has no args but given %d", len(args)) } } } @@ -128,25 +128,25 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { fnArgs = append(fnArgs, arg) case IndexArg: if !shouldIndex { - return nil, util.Errorf(macros.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(macro.FileLine(), "the Args must have or have no index") } if arg.n < 0 || arg.n >= fixedArgCount { - return nil, util.Errorf(macros.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) + return nil, util.Errorf(macro.FileLine(), "arg index %d exceeds max index %d", arg.n, fixedArgCount) } else if fnArgs[arg.n] != nil { - return nil, util.Errorf(macros.FileLine(), "found same index %d", arg.n) + return nil, util.Errorf(macro.FileLine(), "found same index %d", arg.n) } else { fnArgs[arg.n] = arg.arg } default: if shouldIndex { - return nil, util.Errorf(macros.FileLine(), "the Args must have or have no index") + return nil, util.Errorf(macro.FileLine(), "the Args must have or have no index") } if i < fixedArgCount { fnArgs[i] = arg } else if fnType.IsVariadic() { fnArgs = append(fnArgs, arg) } else { - return nil, util.Errorf(macros.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) + return nil, util.Errorf(macro.FileLine(), "the count %d of Args exceeds max index %d", len(args), fixedArgCount) } } } @@ -180,7 +180,7 @@ func (r *argList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, erro // option arg may not return a value when the condition is not met. v, err := r.getArg(ctx, arg, t, fileLine) if err != nil { - return nil, util.Wrapf(err, macros.FileLine(), "returns error when getting %d arg", idx) + return nil, util.Wrapf(err, macro.FileLine(), "returns error when getting %d arg", idx) } if v.IsValid() { result = append(result, v) @@ -210,7 +210,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine switch g := arg.(type) { case *Callable: if results, err := g.Call(ctx); err != nil { - return reflect.Value{}, util.Wrapf(err, macros.FileLine(), "") + return reflect.Value{}, util.Wrapf(err, macro.FileLine(), "") } else if len(results) < 1 { return reflect.Value{}, errors.New("") } else { @@ -252,7 +252,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return v, nil } - return reflect.Value{}, util.Errorf(macros.FileLine(), "error type %s", t.String()) + return reflect.Value{}, util.Errorf(macro.FileLine(), "error type %s", t.String()) } // OptionArg Option 函数的参数绑定。 diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 6d269437..bb82ceca 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -38,7 +38,7 @@ import ( pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/assert" - "github.com/go-spring/spring-core/util/macros" + "github.com/go-spring/spring-core/util/macro" "github.com/spf13/cast" ) @@ -833,7 +833,7 @@ func NewManager() Manager { } func NewManagerRetError() (Manager, error) { - return localManager{}, util.Error(macros.FileLine(), "error") + return localManager{}, util.Error(macro.FileLine(), "error") } func NewManagerRetErrorNil() (Manager, error) { @@ -965,7 +965,7 @@ func (d *callDestroy) InitWithError() error { d.inited = true return nil } - return util.Error(macros.FileLine(), "error") + return util.Error(macro.FileLine(), "error") } func (d *callDestroy) DestroyWithError() error { @@ -973,7 +973,7 @@ func (d *callDestroy) DestroyWithError() error { d.destroyed = true return nil } - return util.Error(macros.FileLine(), "error") + return util.Error(macro.FileLine(), "error") } type nestedCallDestroy struct { diff --git a/util/assert/assert.go b/util/assert/assert.go index d7f266fa..04ef1161 100644 --- a/util/assert/assert.go +++ b/util/assert/assert.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/assert/assert_test.go b/util/assert/assert_test.go index fdadb4d4..10d12bce 100644 --- a/util/assert/assert_test.go +++ b/util/assert/assert_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/assert/string.go b/util/assert/string.go index 8304468d..595aab51 100644 --- a/util/assert/string.go +++ b/util/assert/string.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/assert/string_test.go b/util/assert/string_test.go index 7473a661..8ae1ffee 100644 --- a/util/assert/string_test.go +++ b/util/assert/string_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/error.go b/util/error.go index a0e424e6..f1daaa92 100644 --- a/util/error.go +++ b/util/error.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/error_test.go b/util/error_test.go index d356478d..08e6a367 100644 --- a/util/error_test.go +++ b/util/error_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,20 +21,20 @@ import ( "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/assert" - "github.com/go-spring/spring-core/util/macros" + "github.com/go-spring/spring-core/util/macro" ) func TestError(t *testing.T) { - e0 := util.Error(macros.FileLine(), "error") + e0 := util.Error(macro.FileLine(), "error") assert.Error(t, e0, ".*/error_test.go:29 error") - e1 := util.Errorf(macros.FileLine(), "error: %d", 0) + e1 := util.Errorf(macro.FileLine(), "error: %d", 0) assert.Error(t, e1, ".*/error_test.go:32 error: 0") - e2 := util.Wrap(e0, macros.FileLine(), "error") + e2 := util.Wrap(e0, macro.FileLine(), "error") assert.Error(t, e2, ".*/error_test.go:35 error; .*/error_test.go:29 error") - e3 := util.Wrapf(e1, macros.FileLine(), "error: %d", 1) + e3 := util.Wrapf(e1, macro.FileLine(), "error: %d", 1) assert.Error(t, e3, ".*/error_test.go:38 error: 1; .*/error_test.go:32 error: 0") } diff --git a/util/macros/fileline.go b/util/macro/fileline.go similarity index 90% rename from util/macros/fileline.go rename to util/macro/fileline.go index 48334ab3..f611234b 100644 --- a/util/macros/fileline.go +++ b/util/macro/fileline.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package macros +package macro import ( "fmt" @@ -50,7 +50,7 @@ func Line() int { } // FileLine returns the file name and line of the call point. -// In reality macros.FileLine costs less time than debug.Stack. +// In reality macro.FileLine costs less time than debug.Stack. func FileLine() string { file, line := fileLine() return fmt.Sprintf("%s:%d", file, line) diff --git a/util/macros/fileline_test.go b/util/macro/fileline_test.go similarity index 63% rename from util/macros/fileline_test.go rename to util/macro/fileline_test.go index 0db0fc52..b48ff5ed 100644 --- a/util/macros/fileline_test.go +++ b/util/macro/fileline_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package macros_test +package macro_test import ( "fmt" @@ -23,79 +23,79 @@ import ( "time" "github.com/go-spring/spring-core/util/assert" - "github.com/go-spring/spring-core/util/macros" + "github.com/go-spring/spring-core/util/macro" ) func f1(t *testing.T) ([]string, time.Duration) { ret, cost := f2(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 32) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 32) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f2(t *testing.T) ([]string, time.Duration) { ret, cost := f3(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 42) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 42) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f3(t *testing.T) ([]string, time.Duration) { ret, cost := f4(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 52) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 52) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f4(t *testing.T) ([]string, time.Duration) { ret, cost := f5(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 62) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 62) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f5(t *testing.T) ([]string, time.Duration) { ret, cost := f6(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 72) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 72) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f6(t *testing.T) ([]string, time.Duration) { ret, cost := f7(t) - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 82) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 82) start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost += time.Since(start) return append(ret, fileLine), cost } func f7(t *testing.T) ([]string, time.Duration) { - assert.String(t, macros.File()).HasSuffix("code/fileline_test.go") - assert.Equal(t, macros.Line(), 91) + assert.String(t, macro.File()).HasSuffix("macro/fileline_test.go") + assert.Equal(t, macro.Line(), 91) { start := time.Now() _ = debug.Stack() fmt.Println("\t", "debug.Stack cost", time.Since(start)) } start := time.Now() - fileLine := macros.FileLine() + fileLine := macro.FileLine() cost := time.Since(start) return []string{fileLine}, cost } @@ -105,21 +105,21 @@ func TestFileLine(t *testing.T) { fmt.Printf("loop %d\n", i) ret, cost := f1(t) fmt.Println("\t", ret) - fmt.Println("\t", "all macros.FileLine cost", cost) + fmt.Println("\t", "all macro.FileLine cost", cost) } // loop 0 // debug.Stack cost 37.794µs - // all macros.FileLine cost 14.638µs + // all macro.FileLine cost 14.638µs // loop 1 // debug.Stack cost 11.699µs - // all macros.FileLine cost 6.398µs + // all macro.FileLine cost 6.398µs // loop 2 // debug.Stack cost 20.62µs - // all macros.FileLine cost 4.185µs + // all macro.FileLine cost 4.185µs // loop 3 // debug.Stack cost 11.736µs - // all macros.FileLine cost 4.274µs + // all macro.FileLine cost 4.274µs // loop 4 // debug.Stack cost 19.821µs - // all macros.FileLine cost 4.061µs + // all macro.FileLine cost 4.061µs } diff --git a/util/panic.go b/util/panic.go index 2f209f06..61e39ee1 100644 --- a/util/panic.go +++ b/util/panic.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/panic_test.go b/util/panic_test.go index 3b468092..59ead4f2 100644 --- a/util/panic_test.go +++ b/util/panic_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/testdata/pkg.go b/util/testdata/pkg.go index 78356224..31d64a86 100644 --- a/util/testdata/pkg.go +++ b/util/testdata/pkg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/testdata/pkg/bar/pkg.go b/util/testdata/pkg/bar/pkg.go index f807a4d7..3097d24f 100644 --- a/util/testdata/pkg/bar/pkg.go +++ b/util/testdata/pkg/bar/pkg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,5 +24,5 @@ import ( type SamePkg struct{} func (p *SamePkg) Package() { - fmt.Println("github.com/go-spring/spring-base/util/testdata/pkg/bar/pkg.SamePkg") + fmt.Println("github.com/go-spring/spring-core/util/testdata/pkg/bar/pkg.SamePkg") } diff --git a/util/testdata/pkg/foo/pkg.go b/util/testdata/pkg/foo/pkg.go index 4e22cc94..7023e620 100644 --- a/util/testdata/pkg/foo/pkg.go +++ b/util/testdata/pkg/foo/pkg.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,5 +24,5 @@ import ( type SamePkg struct{} func (p *SamePkg) Package() { - fmt.Println("github.com/go-spring/spring-base/util/testdata/pkg/foo/pkg.SamePkg") + fmt.Println("github.com/go-spring/spring-core/util/testdata/pkg/foo/pkg.SamePkg") } diff --git a/util/type_mock.go b/util/type_mock.go index 8daf8992..02bb5dc0 100644 --- a/util/type_mock.go +++ b/util/type_mock.go @@ -1,5 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: type.go +// +// Generated by this command: +// +// mockgen -build_flags="-mod=mod" -package=util -source=type.go -destination=type_mock.go +// // Package util is a generated GoMock package. package util @@ -7,13 +12,14 @@ package util import ( reflect "reflect" - "go.uber.org/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockBeanSelector is a mock of BeanSelector interface. type MockBeanSelector struct { ctrl *gomock.Controller recorder *MockBeanSelectorMockRecorder + isgomock struct{} } // MockBeanSelectorMockRecorder is the mock recorder for MockBeanSelector. @@ -37,6 +43,7 @@ func (m *MockBeanSelector) EXPECT() *MockBeanSelectorMockRecorder { type MockBeanDefinition struct { ctrl *gomock.Controller recorder *MockBeanDefinitionMockRecorder + isgomock struct{} } // MockBeanDefinitionMockRecorder is the mock recorder for MockBeanDefinition. @@ -99,10 +106,10 @@ func (mr *MockBeanDefinitionMockRecorder) ID() *gomock.Call { } // Interface mocks base method. -func (m *MockBeanDefinition) Interface() interface{} { +func (m *MockBeanDefinition) Interface() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Interface") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } @@ -172,6 +179,7 @@ func (mr *MockBeanDefinitionMockRecorder) Wired() *gomock.Call { type MockConverter struct { ctrl *gomock.Controller recorder *MockConverterMockRecorder + isgomock struct{} } // MockConverterMockRecorder is the mock recorder for MockConverter. diff --git a/util/type_mock_test.go b/util/type_mock_test.go index ad4ae055..ea2e999e 100644 --- a/util/type_mock_test.go +++ b/util/type_mock_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/type_test.go b/util/type_test.go index c46867aa..4b111ee8 100644 --- a/util/type_test.go +++ b/util/type_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/value.go b/util/value.go index dc0ffe6a..9e14138a 100644 --- a/util/value.go +++ b/util/value.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/util/value_test.go b/util/value_test.go index 1ff1ea31..56906d5c 100644 --- a/util/value_test.go +++ b/util/value_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 2fc1cc5398bfe572078ff179148b233311757901 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 18:23:32 +0800 Subject: [PATCH 39/75] code refactor --- gs/internal/gs/bean.go | 9 ++- gs/internal/gs/util.go | 121 ------------------------------- gs/internal/gs_arg/arg.go | 6 +- gs/internal/gs_core/bean.go | 8 +- gs/internal/gs_core/bean_test.go | 3 +- gs/internal/gs_core/core.go | 5 +- gs/internal/gs_core/wire.go | 8 +- 7 files changed, 21 insertions(+), 139 deletions(-) delete mode 100644 gs/internal/gs/util.go diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 80842095..134462e8 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -7,6 +7,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" + "github.com/go-spring/spring-core/util" ) var refreshableType = reflect.TypeFor[dync.Refreshable]() @@ -232,13 +233,13 @@ func (d *BeanDefinition) Primary() *BeanDefinition { // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 // 的要求:只能有一个入参并且必须是 bean 的类型,没有返回值或者只返回 error 类型值。 func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { - if !IsFuncType(fnType) { + if !util.IsFuncType(fnType) { return false } - if fnType.NumIn() != 1 || !HasReceiver(fnType, beanValue) { + if fnType.NumIn() != 1 || !util.HasReceiver(fnType, beanValue) { return false } - return ReturnNothing(fnType) || ReturnOnlyError(fnType) + return util.ReturnNothing(fnType) || util.ReturnOnlyError(fnType) } // Init 设置 bean 的初始化函数。 @@ -318,7 +319,7 @@ func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bo t: t, v: v, name: name, - typeName: TypeName(t), + typeName: util.TypeName(t), r: &BeanRegistration{ f: f, status: Default, diff --git a/gs/internal/gs/util.go b/gs/internal/gs/util.go deleted file mode 100644 index e774e6fd..00000000 --- a/gs/internal/gs/util.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2012-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gs - -import ( - "reflect" - "strings" -) - -// errorType the reflection type of error. -var errorType = reflect.TypeOf((*error)(nil)).Elem() - -// TypeName returns a fully qualified name consisting of package path and type name. -func TypeName(i interface{}) string { - - var typ reflect.Type - switch o := i.(type) { - case reflect.Type: - typ = o - case reflect.Value: - typ = o.Type() - default: - typ = reflect.TypeOf(o) - } - - for { - if k := typ.Kind(); k == reflect.Ptr || k == reflect.Slice { - typ = typ.Elem() - } else { - break - } - } - - if pkgPath := typ.PkgPath(); pkgPath != "" { - pkgPath = strings.TrimSuffix(pkgPath, "_test") - return pkgPath + "/" + typ.String() - } - return typ.String() // the path of built-in type is empty -} - -// IsFuncType returns whether `t` is func type. -func IsFuncType(t reflect.Type) bool { - return t.Kind() == reflect.Func -} - -// IsErrorType returns whether `t` is error type. -func IsErrorType(t reflect.Type) bool { - return t == errorType || t.Implements(errorType) -} - -// ReturnNothing returns whether the function has no return value. -func ReturnNothing(t reflect.Type) bool { - return t.NumOut() == 0 -} - -// ReturnOnlyError returns whether the function returns only error value. -func ReturnOnlyError(t reflect.Type) bool { - return t.NumOut() == 1 && IsErrorType(t.Out(0)) -} - -// IsStructPtr returns whether it is the pointer type of structure. -func IsStructPtr(t reflect.Type) bool { - return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct -} - -// IsConstructor returns whether `t` is a constructor type. What is a constructor? -// It should be a function first, has any number of inputs and supports the option -// pattern input, has one or two outputs and the second output should be an error. -func IsConstructor(t reflect.Type) bool { - returnError := t.NumOut() == 2 && IsErrorType(t.Out(1)) - return IsFuncType(t) && (t.NumOut() == 1 || returnError) -} - -// HasReceiver returns whether the function has a receiver. -func HasReceiver(t reflect.Type, receiver reflect.Value) bool { - if t.NumIn() < 1 { - return false - } - t0 := t.In(0) - if t0.Kind() != reflect.Interface { - return t0 == receiver.Type() - } - return receiver.Type().Implements(t0) -} - -// IsBeanType returns whether `t` is a bean type. -func IsBeanType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface: - return true - case reflect.Ptr: - return t.Elem().Kind() == reflect.Struct - default: - return false - } -} - -// IsBeanReceiver returns whether the `t` is a bean receiver, a bean receiver can -// be a bean, a map or slice whose elements are beans. -func IsBeanReceiver(t reflect.Type) bool { - switch t.Kind() { - case reflect.Map, reflect.Slice, reflect.Array: - return IsBeanType(t.Elem()) - default: - return IsBeanType(t) - } -} diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 2cf276d2..e5ccb00b 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -228,7 +228,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine case string: tag = g default: - tag = gs.TypeName(g) + ":" + tag = util.TypeName(g) + ":" } // binds properties value by the "value" tag. @@ -244,7 +244,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine } // wires dependent beans by the "autowire" tag. - if gs.IsBeanReceiver(t) { + if util.IsBeanReceiver(t) { v := reflect.New(t).Elem() if err = ctx.Wire(v, tag); err != nil { return reflect.Value{}, err @@ -383,7 +383,7 @@ func (r *Callable) Call(ctx gs.ArgContext) ([]reflect.Value, error) { } o := out[n-1] - if gs.IsErrorType(o.Type()) { + if util.IsErrorType(o.Type()) { if i := o.Interface(); i != nil { return out[:n-1], i.(error) } diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index ddfb9049..5fb80fcf 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -45,7 +45,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { } t := v.Type() - if !gs.IsBeanType(t) { + if !util.IsBeanType(t) { panic(errors.New("bean must be ref type")) } @@ -60,7 +60,7 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { // 以 reflect.ValueOf(fn) 形式注册的函数被视为函数对象 bean 。 if !fromValue && t.Kind() == reflect.Func { - if !gs.IsConstructor(t) { + if !util.IsConstructor(t) { t1 := "func(...)bean" t2 := "func(...)(bean, error)" panic(fmt.Errorf("constructor should be %s or %s", t1, t2)) @@ -74,12 +74,12 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { out0 := t.Out(0) v = reflect.New(out0) - if gs.IsBeanType(out0) { + if util.IsBeanType(out0) { v = v.Elem() } t = v.Type() - if !gs.IsBeanType(t) { + if !util.IsBeanType(t) { panic(errors.New("bean must be ref type")) } diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index 74aeb61d..cb1463eb 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -27,6 +27,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" + "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/assert" ) @@ -99,7 +100,7 @@ func TestIsFuncBeanType(t *testing.T) { } for k, v := range data { - ok := gs.IsConstructor(k) + ok := util.IsConstructor(k) assert.Equal(t, ok, v) } } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 78d45d54..6eed421d 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -33,6 +33,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/util" ) type BeanInit interface { @@ -334,7 +335,7 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti return nil, err } v := reflect.New(out0) - if gs.IsBeanType(out0) { + if util.IsBeanType(out0) { v = v.Elem() } name := bd.GetName() + "_" + m.Name @@ -522,7 +523,7 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} // Invoke 调用函数,函数的参数会自动注入,函数的返回值也会自动注入。 func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { - if !gs.IsFuncType(reflect.TypeOf(fn)) { + if !util.IsFuncType(reflect.TypeOf(fn)) { return nil, errors.New("fn should be func type") } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 15ad28a4..7e380959 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -219,7 +219,7 @@ func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { case *gs.BeanDefinition: return parseWireTag(s.ID()), nil default: - return parseWireTag(gs.TypeName(s) + ":"), nil + return parseWireTag(util.TypeName(s) + ":"), nil } } @@ -283,7 +283,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } et := t.Elem() - if !gs.IsBeanReceiver(et) { + if !util.IsBeanReceiver(et) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -400,7 +400,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } t := v.Type() - if !gs.IsBeanReceiver(t) { + if !util.IsBeanReceiver(t) { return fmt.Errorf("%s is not valid receiver type", t.String()) } @@ -635,7 +635,7 @@ func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (refl } // 构造函数的返回值为值类型时 b.Type() 返回其指针类型。 - if val := out[0]; gs.IsBeanType(val.Type()) { + if val := out[0]; util.IsBeanType(val.Type()) { // 如果实现接口的是值类型,那么需要转换成指针类型然后再赋值给接口。 if !val.IsNil() && val.Kind() == reflect.Interface && util.IsValueType(val.Elem().Type()) { v := reflect.New(val.Elem().Type()) From 39dc81bf8fddaa0e3572d0c65a0542378ca163db Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 18:34:44 +0800 Subject: [PATCH 40/75] code refactor --- util/assert/assert.go | 120 +++++++++++++++++++++++++++++++++++++ util/assert/assert_test.go | 90 ++++++++++++++++++++++++++++ util/error.go | 20 +++---- util/error_test.go | 26 +++++--- util/flat_test.go | 84 ++++++++++++++++++++++++++ 5 files changed, 320 insertions(+), 20 deletions(-) create mode 100644 util/flat_test.go diff --git a/util/assert/assert.go b/util/assert/assert.go index 04ef1161..16e5b9ce 100644 --- a/util/assert/assert.go +++ b/util/assert/assert.go @@ -229,3 +229,123 @@ func Implements(t T, got interface{}, expect interface{}, msg ...string) { fail(t, str, msg...) } } + +// InSlice assertion failed when got is not in expect array & slice. +func InSlice(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + v := reflect.ValueOf(expect) + if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { + str := fmt.Sprintf("unsupported expect value (%T) %v", expect, expect) + fail(t, str, msg...) + return + } + + for i := 0; i < v.Len(); i++ { + if reflect.DeepEqual(got, v.Index(i).Interface()) { + return + } + } + + str := fmt.Sprintf("got (%T) %v is not in (%T) %v", got, got, expect, expect) + fail(t, str, msg...) +} + +// NotInSlice assertion failed when got is in expect array & slice. +func NotInSlice(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + v := reflect.ValueOf(expect) + if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { + str := fmt.Sprintf("unsupported expect value (%T) %v", expect, expect) + fail(t, str, msg...) + return + } + + e := reflect.TypeOf(got) + if e != v.Type().Elem() { + str := fmt.Sprintf("got type (%s) doesn't match expect type (%s)", e, v.Type()) + fail(t, str, msg...) + return + } + + for i := 0; i < v.Len(); i++ { + if reflect.DeepEqual(got, v.Index(i).Interface()) { + str := fmt.Sprintf("got (%T) %v is in (%T) %v", got, got, expect, expect) + fail(t, str, msg...) + return + } + } +} + +// SubInSlice assertion failed when got is not sub in expect array & slice. +func SubInSlice(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + v1 := reflect.ValueOf(got) + if v1.Kind() != reflect.Array && v1.Kind() != reflect.Slice { + str := fmt.Sprintf("unsupported got value (%T) %v", got, got) + fail(t, str, msg...) + return + } + + v2 := reflect.ValueOf(expect) + if v2.Kind() != reflect.Array && v2.Kind() != reflect.Slice { + str := fmt.Sprintf("unsupported expect value (%T) %v", expect, expect) + fail(t, str, msg...) + return + } + + for i := 0; i < v1.Len(); i++ { + for j := 0; j < v2.Len(); j++ { + if reflect.DeepEqual(v1.Index(i).Interface(), v2.Index(j).Interface()) { + return + } + } + } + + str := fmt.Sprintf("got (%T) %v is not sub in (%T) %v", got, got, expect, expect) + fail(t, str, msg...) +} + +// InMapKeys assertion failed when got is not in keys of expect map. +func InMapKeys(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + switch v := reflect.ValueOf(expect); v.Kind() { + case reflect.Map: + for _, key := range v.MapKeys() { + if reflect.DeepEqual(got, key.Interface()) { + return + } + } + default: + str := fmt.Sprintf("unsupported expect value (%T) %v", expect, expect) + fail(t, str, msg...) + return + } + + str := fmt.Sprintf("got (%T) %v is not in keys of (%T) %v", got, got, expect, expect) + fail(t, str, msg...) +} + +// InMapValues assertion failed when got is not in values of expect map. +func InMapValues(t T, got interface{}, expect interface{}, msg ...string) { + t.Helper() + + switch v := reflect.ValueOf(expect); v.Kind() { + case reflect.Map: + for _, key := range v.MapKeys() { + if reflect.DeepEqual(got, v.MapIndex(key).Interface()) { + return + } + } + default: + str := fmt.Sprintf("unsupported expect value (%T) %v", expect, expect) + fail(t, str, msg...) + return + } + + str := fmt.Sprintf("got (%T) %v is not in values of (%T) %v", got, got, expect, expect) + fail(t, str, msg...) +} diff --git a/util/assert/assert_test.go b/util/assert/assert_test.go index 10d12bce..059414bc 100644 --- a/util/assert/assert_test.go +++ b/util/assert/assert_test.go @@ -319,3 +319,93 @@ func TestImplements(t *testing.T) { assert.Implements(g, new(int), (*io.Reader)(nil)) }) } + +func TestInSlice(t *testing.T) { + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported expect value (string) 1"}) + assert.InSlice(g, 1, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 1 is not in ([]string) [1]"}) + assert.InSlice(g, 1, []string{"1"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int64) 1 is not in ([]int64) [3 2]"}) + assert.InSlice(g, int64(1), []int64{3, 2}) + }) + runCase(t, func(g *assert.MockT) { + assert.InSlice(g, int64(1), []int64{3, 2, 1}) + assert.InSlice(g, "1", []string{"3", "2", "1"}) + }) +} + +func TestNotInSlice(t *testing.T) { + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported expect value (string) 1"}) + assert.NotInSlice(g, 1, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got type (int) doesn't match expect type ([]string)"}) + assert.NotInSlice(g, 1, []string{"1"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (string) 1 is in ([]string) [3 2 1]"}) + assert.NotInSlice(g, "1", []string{"3", "2", "1"}) + }) + runCase(t, func(g *assert.MockT) { + assert.NotInSlice(g, int64(1), []int64{3, 2}) + }) +} + +func TestSubInSlice(t *testing.T) { + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported got value (int) 1"}) + assert.SubInSlice(g, 1, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported expect value (string) 1"}) + assert.SubInSlice(g, []int{1}, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got ([]int) [1] is not sub in ([]string) [1]"}) + assert.SubInSlice(g, []int{1}, []string{"1"}) + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got ([]int) [1] is not sub in ([]int) [3 2]"}) + assert.SubInSlice(g, []int{1}, []int{3, 2}) + }) + runCase(t, func(g *assert.MockT) { + assert.SubInSlice(g, []int{1}, []int{3, 2, 1}) + assert.SubInSlice(g, []string{"1"}, []string{"3", "2", "1"}) + }) +} + +func TestInMapKeys(t *testing.T) { + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported expect value (string) 1"}) + assert.InMapKeys(g, 1, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 1 is not in keys of (map[string]string) map[1:1]"}) + assert.InMapKeys(g, 1, map[string]string{"1": "1"}) + }) + runCase(t, func(g *assert.MockT) { + assert.InMapKeys(g, int64(1), map[int64]int64{3: 1, 2: 2, 1: 3}) + assert.InMapKeys(g, "1", map[string]string{"3": "1", "2": "2", "1": "3"}) + }) +} + +func TestInMapValues(t *testing.T) { + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"unsupported expect value (string) 1"}) + assert.InMapValues(g, 1, "1") + }) + runCase(t, func(g *assert.MockT) { + g.EXPECT().Error([]interface{}{"got (int) 1 is not in values of (map[string]string) map[1:1]"}) + assert.InMapValues(g, 1, map[string]string{"1": "1"}) + }) + runCase(t, func(g *assert.MockT) { + assert.InMapValues(g, int64(1), map[int64]int64{3: 1, 2: 2, 1: 3}) + assert.InMapValues(g, "1", map[string]string{"3": "1", "2": "2", "1": "3"}) + }) +} diff --git a/util/error.go b/util/error.go index f1daaa92..62b89e26 100644 --- a/util/error.go +++ b/util/error.go @@ -27,39 +27,33 @@ var ForbiddenMethod = errors.New("forbidden method") // UnimplementedMethod throws this error when calling an unimplemented method. var UnimplementedMethod = errors.New("unimplemented method") -var WrapFormat = func(err error, fileline string, format string, a ...interface{}) error { +var WrapFormat = func(err error, fileline string, msg string) error { if err == nil { - if format != "" { - return fmt.Errorf(fileline+" "+format, a...) - } - return errors.New(fileline + " " + fmt.Sprint(a...)) + return fmt.Errorf("%s %s", fileline, msg) } - if format == "" { - return fmt.Errorf("%s %s; %w", fileline, fmt.Sprint(a...), err) - } - return fmt.Errorf("%s %s; %w", fileline, fmt.Sprintf(format, a...), err) + return fmt.Errorf("%s %s; %w", fileline, msg, err) } // Error returns an error with the file and line. // The file and line may be calculated at the compile time in the future. func Error(fileline string, text string) error { - return WrapFormat(nil, fileline, "", text) + return WrapFormat(nil, fileline, text) } // Errorf returns an error with the file and line. // The file and line may be calculated at the compile time in the future. func Errorf(fileline string, format string, a ...interface{}) error { - return WrapFormat(nil, fileline, format, a...) + return WrapFormat(nil, fileline, fmt.Sprintf(format, a...)) } // Wrap returns an error with the file and line. // The file and line may be calculated at the compile time in the future. func Wrap(err error, fileline string, text string) error { - return WrapFormat(err, fileline, "", text) + return WrapFormat(err, fileline, text) } // Wrapf returns an error with the file and line. // The file and line may be calculated at the compile time in the future. func Wrapf(err error, fileline string, format string, a ...interface{}) error { - return WrapFormat(err, fileline, format, a...) + return WrapFormat(err, fileline, fmt.Sprintf(format, a...)) } diff --git a/util/error_test.go b/util/error_test.go index 08e6a367..11bc0061 100644 --- a/util/error_test.go +++ b/util/error_test.go @@ -26,15 +26,27 @@ import ( func TestError(t *testing.T) { - e0 := util.Error(macro.FileLine(), "error") - assert.Error(t, e0, ".*/error_test.go:29 error") + e0 := util.Error(macro.FileLine(), "error: 0") + t.Log(e0) + assert.Error(t, e0, ".*/error_test.go:29 error: 0") - e1 := util.Errorf(macro.FileLine(), "error: %d", 0) - assert.Error(t, e1, ".*/error_test.go:32 error: 0") + e1 := util.Errorf(macro.FileLine(), "error: %d", 1) + t.Log(e1) + assert.Error(t, e1, ".*/error_test.go:33 error: 1") - e2 := util.Wrap(e0, macro.FileLine(), "error") - assert.Error(t, e2, ".*/error_test.go:35 error; .*/error_test.go:29 error") + e2 := util.Wrap(e0, macro.FileLine(), "error: 0") + t.Log(e2) + assert.Error(t, e2, ".*/error_test.go:37 error: 0; .*/error_test.go:29 error: 0") e3 := util.Wrapf(e1, macro.FileLine(), "error: %d", 1) - assert.Error(t, e3, ".*/error_test.go:38 error: 1; .*/error_test.go:32 error: 0") + t.Log(e3) + assert.Error(t, e3, ".*/error_test.go:41 error: 1; .*/error_test.go:33 error: 1") + + e4 := util.Wrap(e2, macro.FileLine(), "error: 0") + t.Log(e4) + assert.Error(t, e4, ".*/error_test.go:45 error: 0; .*/error_test.go:37 error: 0; .*/error_test.go:29 error: 0") + + e5 := util.Wrapf(e3, macro.FileLine(), "error: %d", 1) + t.Log(e5) + assert.Error(t, e5, ".*/error_test.go:49 error: 1; .*/error_test.go:41 error: 1; .*/error_test.go:33 error: 1") } diff --git a/util/flat_test.go b/util/flat_test.go new file mode 100644 index 00000000..89117638 --- /dev/null +++ b/util/flat_test.go @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package util_test + +import ( + "testing" + + "github.com/go-spring/spring-core/util" + "github.com/go-spring/spring-core/util/assert" +) + +func TestFlatten(t *testing.T) { + m := util.FlattenMap(map[string]interface{}{ + "int": 123, + "str": "abc", + "arr": []interface{}{ + "abc", + "def", + map[string]interface{}{ + "a": "123", + "b": "456", + }, + ([]interface{})(nil), + (map[string]string)(nil), + []interface{}{}, + map[string]string{}, + }, + "map": map[string]interface{}{ + "a": "123", + "b": "456", + "arr": []string{ + "abc", + "def", + }, + "nil_arr": nil, + "nil_map": nil, + "empty_arr": []interface{}{}, + "empty_map": map[string]string{}, + }, + "nil_arr": nil, + "nil_map": nil, + "empty_arr": []interface{}{}, + "empty_map": map[string]string{}, + }) + expect := map[string]string{ + "int": "123", + "str": "abc", + "nil_arr": "", + "nil_map": "", + "empty_arr": "", + "empty_map": "", + "map.a": "123", + "map.b": "456", + "map.arr[0]": "abc", + "map.arr[1]": "def", + "map.nil_arr": "", + "map.nil_map": "", + "map.empty_arr": "", + "map.empty_map": "", + "arr[0]": "abc", + "arr[1]": "def", + "arr[2].a": "123", + "arr[2].b": "456", + "arr[3]": "", + "arr[4]": "", + "arr[5]": "", + "arr[6]": "", + } + assert.Equal(t, m, expect) +} From b0450f2612617e3ef50b7dc0d6321b0759a322e6 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 5 Jan 2025 19:53:14 +0800 Subject: [PATCH 41/75] use syslog --- gs/internal/gs/bean.go | 22 ++++++++++++++ gs/internal/gs_arg/arg.go | 35 ++++++++++++----------- gs/internal/gs_core/core.go | 54 ++++++++++++++++++----------------- gs/internal/gs_core/wire.go | 11 +++---- gs/syslog/syslog.go | 2 +- gs/testdata/config/logger.xml | 14 --------- util/panic.go | 48 ------------------------------- util/panic_test.go | 43 ---------------------------- 8 files changed, 75 insertions(+), 154 deletions(-) delete mode 100644 gs/testdata/config/logger.xml delete mode 100644 util/panic.go delete mode 100644 util/panic_test.go diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 134462e8..4ffae188 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -24,6 +24,28 @@ const ( Wired // 注入完成 ) +// GetStatusString 获取 bean 状态的字符串表示。 +func GetStatusString(status BeanStatus) string { + switch status { + case Deleted: + return "Deleted" + case Default: + return "Default" + case Resolving: + return "Resolving" + case Resolved: + return "Resolved" + case Creating: + return "Creating" + case Created: + return "Created" + case Wired: + return "Wired" + default: + return "" + } +} + // BeanRegistration bean 注册数据。 type BeanRegistration struct { f Callable // 构造函数 diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index e5ccb00b..9178ad9c 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -26,6 +26,7 @@ import ( "runtime" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/macro" ) @@ -197,15 +198,15 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine tag string ) - // description := fmt.Sprintf("arg:\"%v\" %s", arg, fileLine) - // r.logger.Tracef("get value %s", description) - // defer func() { - // if err == nil { - // r.logger.Tracef("get value success %s", description) - // } else { - // r.logger.Tracef("get value error %s %s", err.Error(), description) - // } - // }() + description := fmt.Sprintf("arg:\"%v\" %s", arg, fileLine) + syslog.Debug("get value %s", description) + defer func() { + if err == nil { + syslog.Debug("get value success %s", description) + } else { + syslog.Debug("get value error %s %s", err.Error(), description) + } + }() switch g := arg.(type) { case *Callable: @@ -298,14 +299,14 @@ func (arg *OptionArg) call(ctx gs.ArgContext) (reflect.Value, error) { err error ) - // arg.logger.Tracef("call option func %s", arg.r.fileLine) - // defer func() { - // if err == nil { - // arg.logger.Tracef("call option func success %s", arg.r.fileLine) - // } else { - // arg.logger.Tracef("call option func error %s %s", err.Error(), arg.r.fileLine) - // } - // }() + syslog.Debug("call option func %s", arg.r.fileLine) + defer func() { + if err == nil { + syslog.Debug("call option func success %s", arg.r.fileLine) + } else { + syslog.Debug("call option func error %s %s", err.Error(), arg.r.fileLine) + } + }() if arg.c != nil { ok, err = ctx.Matches(arg.c) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 6eed421d..f37fd722 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -27,12 +27,14 @@ import ( "sort" "strings" "sync" + "time" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" ) @@ -148,7 +150,7 @@ func (c *Container) Refresh() (err error) { } c.state = RefreshInit - // start := time.Now() + start := time.Now() // 处理 group 逻辑 for _, fn := range c.groupFuncs { @@ -204,12 +206,12 @@ func (c *Container) Refresh() (err error) { stack := newWiringStack() - // defer func() { - // if err != nil || len(stack.beans) > 0 { - // err = fmt.Errorf("%s ↩\n%s", err, stack.path()) - // c.logger.Error(err) - // } - // }() + defer func() { + if err != nil || len(stack.beans) > 0 { + err = fmt.Errorf("%s ↩\n%s", err, stack.path()) + syslog.Error("%s", err.Error()) + } + }() // 按照 bean id 升序注入,保证注入过程始终一致。 { @@ -246,9 +248,9 @@ func (c *Container) Refresh() (err error) { c.destroyers = stack.sortDestroyers() c.state = Refreshed - // cost := time.Now().Sub(start) - // c.logger.Infof("refresh %d beans cost %v", len(beansById), cost) - // c.logger.Info("Container refreshed successfully") + cost := time.Now().Sub(start) + syslog.Info("refresh %d beans cost %v", len(beansById), cost) + syslog.Info("refreshed successfully") return nil } @@ -349,11 +351,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } func (c *Container) registerBean(b *gs.BeanDefinition) { - // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), b.Type(), b.FileLine()) + syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), b.Type(), b.FileLine()) c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) for _, t := range b.GetExports() { - // c.logger.Debugf("register %s name:%q type:%q %s", b.getClass(), b.BeanName(), t, b.FileLine()) + syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), t, b.FileLine()) c.beansByType[t] = append(c.beansByType[t], b) } } @@ -484,7 +486,7 @@ func (c *Container) Get(i interface{}, selectors ...gs.BeanSelector) error { defer func() { if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) + syslog.Info("wiring path %s", stack.path()) } }() @@ -506,11 +508,11 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} stack := newWiringStack() - // defer func() { - // if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) - // } - // }() + defer func() { + if len(stack.beans) > 0 { + syslog.Info("wiring path %s", stack.path()) + } + }() b := NewBean(objOrCtor, ctorArgs...) err := c.wireBean(b, stack) @@ -529,11 +531,11 @@ func (c *Container) Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error stack := newWiringStack() - // defer func() { - // if len(stack.beans) > 0 { - // c.logger.Infof("wiring path %s", stack.path()) - // } - // }() + defer func() { + if len(stack.beans) > 0 { + syslog.Info("wiring path %s", stack.path()) + } + }() r, err := gs_arg.Bind(fn, args, 1) if err != nil { @@ -560,7 +562,7 @@ func (c *Container) Go(fn func(ctx context.Context)) { defer c.wg.Done() defer func() { if r := recover(); r != nil { - // c.logger.Panic(r) + syslog.Error("%v", r) } }() fn(c.ctx) @@ -574,11 +576,11 @@ func (c *Container) Close() { c.cancel() c.wg.Wait() - // c.logger.Info("goroutines exited") + syslog.Info("goroutines exited") for _, f := range c.destroyers { f() } - // c.logger.Info("Container closed") + syslog.Info("container closed") } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 7e380959..8f2dc2d5 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -12,6 +12,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" ) @@ -78,16 +79,16 @@ func newWiringStack() *wiringStack { // pushBack 添加一个即将注入的 bean 。 func (s *wiringStack) pushBack(b *gs.BeanDefinition) { - // s.logger.Tracef("push %s %s", b, getStatusString(b.status)) + syslog.Debug("push %s %s", b, gs.GetStatusString(b.GetStatus())) s.beans = append(s.beans, b) } // popBack 删除一个已经注入的 bean 。 func (s *wiringStack) popBack() { n := len(s.beans) - // b := s.beans[n-1] + b := s.beans[n-1] s.beans = s.beans[:n-1] - // s.logger.Tracef("pop %s %s", b, getStatusString(b.status)) + syslog.Debug("pop %s %s", b, gs.GetStatusString(b.GetStatus())) } // path 返回 bean 的注入路径。 @@ -119,7 +120,7 @@ func (s *wiringStack) sortDestroyers() []func() { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) if len(out) > 0 && !out[0].IsNil() { - // s.logger.Error(out[0].Interface().(error)) + syslog.Error(out[0].Interface().(error).Error()) } } } @@ -437,7 +438,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } if !found { foundBeans = append(foundBeans, b) - // c.logger.Warnf("you should call Export() on %s", b) + syslog.Warn("you should call Export() on %s", b) } } } diff --git a/gs/syslog/syslog.go b/gs/syslog/syslog.go index 252bb182..50e01234 100644 --- a/gs/syslog/syslog.go +++ b/gs/syslog/syslog.go @@ -23,7 +23,7 @@ import ( ) func init() { - slog.Default().Enabled(context.Background(), slog.LevelInfo) + slog.SetLogLoggerLevel(slog.LevelInfo) slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) } diff --git a/gs/testdata/config/logger.xml b/gs/testdata/config/logger.xml deleted file mode 100644 index 12e80c0d..00000000 --- a/gs/testdata/config/logger.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/util/panic.go b/util/panic.go deleted file mode 100644 index 61e39ee1..00000000 --- a/util/panic.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import ( - "fmt" -) - -// PanicCond panic condition. -type PanicCond struct { - fn func() interface{} -} - -// When throws a panic when `isPanic` is true. -func (p *PanicCond) When(isPanic bool) { - if isPanic { - panic(p.fn()) - } -} - -// NewPanicCond returns a panic condition. -func NewPanicCond(fn func() interface{}) *PanicCond { - return &PanicCond{fn} -} - -// Panic returns a panic condition that throws an error. -func Panic(err interface{}) *PanicCond { - return NewPanicCond(func() interface{} { return err }) -} - -// Panicf returns a panic condition that throws a formatted error. -func Panicf(format string, a ...interface{}) *PanicCond { - return NewPanicCond(func() interface{} { return fmt.Errorf(format, a...) }) -} diff --git a/util/panic_test.go b/util/panic_test.go deleted file mode 100644 index 59ead4f2..00000000 --- a/util/panic_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util_test - -import ( - "errors" - "testing" - - "github.com/go-spring/spring-core/util" - "github.com/go-spring/spring-core/util/assert" -) - -func TestPanicCond(t *testing.T) { - - util.Panic("this is an error").When(false) - assert.Panic(t, func() { - util.Panic("this is an error").When(true) - }, "this is an error") - - util.Panic(errors.New("this is an error")).When(false) - assert.Panic(t, func() { - util.Panic(errors.New("this is an error")).When(true) - }, "this is an error") - - util.Panicf("this is an %s", "error").When(false) - assert.Panic(t, func() { - util.Panicf("this is an %s", "error").When(true) - }, "this is an error") -} From 1dd70714c2bca61b714b7ebb316ab19a764097ac Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 6 Jan 2025 13:45:49 +0800 Subject: [PATCH 42/75] code refactor --- gs/internal/gs_arg/arg.go | 10 +++--- gs/internal/gs_cond/cond.go | 72 ++++++++++++++++++++++--------------- gs/internal/gs_conf/args.go | 3 +- gs/internal/gs_core/core.go | 2 -- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 9178ad9c..21c914f1 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -16,7 +16,7 @@ //go:generate mockgen -build_flags="-mod=mod" -package=arg -source=arg.go -destination=arg_mock.go -// Package arg 用于实现函数参数绑定。 +// Package gs_arg 用于实现函数参数绑定。 package gs_arg import ( @@ -101,8 +101,10 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { }() fnArgs := make([]gs.Arg, fixedArgCount) - if len(args) > 0 { + if args[0] == nil { + return nil, util.Errorf(macro.FileLine(), "the first arg must not be nil") + } switch arg := args[0].(type) { case *OptionArg: fnArgs = append(fnArgs, arg) @@ -202,9 +204,9 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine syslog.Debug("get value %s", description) defer func() { if err == nil { - syslog.Debug("get value success %s", description) + syslog.Debug("get value %s success", description) } else { - syslog.Debug("get value error %s %s", err.Error(), description) + syslog.Debug("get value %s error:%s", err.Error(), description) } }() diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index 68fc0327..5a86c133 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -165,43 +165,57 @@ func Group(op Operator, cond ...gs.Condition) gs.Condition { return &group{op: op, cond: cond} } -func (g *group) Matches(ctx gs.CondContext) (bool, error) { +func (g *group) matchesOr(ctx gs.CondContext) (bool, error) { + for _, c := range g.cond { + if ok, err := c.Matches(ctx); err != nil { + return false, err + } else if ok { + return true, nil + } + } + return false, nil +} - if len(g.cond) == 0 { - return false, errors.New("no condition in group") +func (g *group) matchesAnd(ctx gs.CondContext) (bool, error) { + for _, c := range g.cond { + if ok, err := c.Matches(ctx); err != nil { + return false, err + } else if !ok { + return false, nil + } + } + return true, nil +} + +func (g *group) matchesNone(ctx gs.CondContext) (bool, error) { + for _, c := range g.cond { + if ok, err := c.Matches(ctx); err != nil { + return false, err + } else if ok { + return false, nil + } } + return true, nil +} +// Matches evaluates the group of conditions based on the specified operator. +// - If the operator is Or, it returns true if at least one condition is satisfied. +// - If the operator is And, it returns true if all conditions are satisfied. +// - If the operator is None, it returns true if none of the conditions are satisfied. +func (g *group) Matches(ctx gs.CondContext) (bool, error) { + if len(g.cond) == 0 { + return false, errors.New("no conditions in group") + } switch g.op { case Or: - for _, c := range g.cond { - if ok, err := c.Matches(ctx); err != nil { - return false, err - } else if ok { - return true, nil - } - } - return false, nil + return g.matchesOr(ctx) case And: - for _, c := range g.cond { - if ok, err := c.Matches(ctx); err != nil { - return false, err - } else if !ok { - return false, nil - } - } - return true, nil + return g.matchesAnd(ctx) case None: - for _, c := range g.cond { - if ok, err := c.Matches(ctx); err != nil { - return false, err - } else if ok { - return false, nil - } - } - return true, nil + return g.matchesNone(ctx) + default: + return false, fmt.Errorf("error condition operator %d", g.op) } - - return false, fmt.Errorf("error condition operator %d", g.op) } // node is a Condition implemented by link of Condition(s). diff --git a/gs/internal/gs_conf/args.go b/gs/internal/gs_conf/args.go index 2e3d7596..7999df4d 100644 --- a/gs/internal/gs_conf/args.go +++ b/gs/internal/gs_conf/args.go @@ -47,8 +47,7 @@ func (c *CommandArgs) CopyTo(out *conf.Properties) error { cmdArgs := os.Args[1:] n := len(cmdArgs) for i := 0; i < n; i++ { - s := cmdArgs[i] - if s == option { + if cmdArgs[i] == option { if i >= n-1 { return fmt.Errorf("cmd option %s needs arg", option) } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index f37fd722..55c3c19a 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -14,8 +14,6 @@ * limitations under the License. */ -// Package gs_ctx 实现了 go-spring 的核心骨架,包含 IoC 容器、基于 IoC 容器的 App -// 以及全局 App 对象封装三个部分,可以应用于多种使用场景。 package gs_core import ( From d699915fd9b60c611bcea41cd9f784e5d0a74ef6 Mon Sep 17 00:00:00 2001 From: lianghuan Date: Mon, 6 Jan 2025 19:34:55 +0800 Subject: [PATCH 43/75] export cond pkg --- gs/gs.go | 78 ++++++++++++++++++++++++++++++++ gs/internal/gs_cond/cond.go | 33 ++++++++------ gs/internal/gs_cond/cond_test.go | 8 ++-- 3 files changed, 102 insertions(+), 17 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 8a18b62d..b1fc1936 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -23,6 +23,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" + "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_core" "github.com/go-spring/spring-core/gs/sysconf" ) @@ -42,6 +43,83 @@ type ( GroupFunc = gs.GroupFunc ) +/************************************ cond ***********************************/ + +type ( + FuncCond = gs_cond.FuncCond + Conditional = gs_cond.Conditional + PropertyOption = gs_cond.PropertyOption +) + +// OK returns a Condition that always returns true. +func OK() gs.Condition { + return gs_cond.OK() +} + +// Not returns a Condition that returns true when the given Condition returns false. +func Not(c gs.Condition) gs.Condition { + return gs_cond.Not(c) +} + +// Or returns a Condition that returns true when any of the given Conditions returns true. +func Or(cond ...gs.Condition) gs.Condition { + return gs_cond.Or(cond...) +} + +// And returns a Condition that returns true when all the given Conditions return true. +func And(cond ...gs.Condition) gs.Condition { + return gs_cond.And(cond...) +} + +// None returns a Condition that returns true when none of the given Conditions returns true. +func None(cond ...gs.Condition) gs.Condition { + return gs_cond.None(cond...) +} + +func On(cond gs.Condition) *Conditional { + return gs_cond.On(cond) +} + +func MatchIfMissing() PropertyOption { + return gs_cond.MatchIfMissing() +} + +func HavingValue(havingValue string) PropertyOption { + return gs_cond.HavingValue(havingValue) +} + +func OnProperty(name string, options ...PropertyOption) *Conditional { + return gs_cond.OnProperty(name, options...) +} + +func OnMissingProperty(name string) *Conditional { + return gs_cond.OnMissingProperty(name) +} + +func OnBean(selector gs.BeanSelector) *Conditional { + return gs_cond.OnBean(selector) +} + +func OnMissingBean(selector gs.BeanSelector) *Conditional { + return gs_cond.OnMissingBean(selector) +} + +func OnSingleBean(selector gs.BeanSelector) *Conditional { + return gs_cond.OnSingleBean(selector) +} + +func OnExpression(expression string) *Conditional { + return gs_cond.OnExpression(expression) +} + +func OnMatches(fn func(ctx gs.CondContext) (bool, error)) *Conditional { + return gs_cond.OnMatches(fn) +} + +func OnProfile(profile string) *Conditional { + return gs_cond.OnProfile(profile) +} + /************************************ boot ***********************************/ var boot *gs_app.Boot diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index 5a86c133..cde1f5d5 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -149,9 +149,9 @@ func (c *onExpression) Matches(ctx gs.CondContext) (bool, error) { type Operator int const ( - Or = Operator(1) // at least one of the conditions must be met. - And = Operator(2) // all conditions must be met. - None = Operator(3) // all conditions must be not met. + opOr = Operator(iota) // at least one of the conditions must be met. + opAnd // all conditions must be met. + opNone // all conditions must be not met. ) // group is a Condition implemented by operation of Condition(s). @@ -160,9 +160,16 @@ type group struct { cond []gs.Condition } -// Group returns a Condition implemented by operation of Condition(s). -func Group(op Operator, cond ...gs.Condition) gs.Condition { - return &group{op: op, cond: cond} +func Or(cond ...gs.Condition) gs.Condition { + return &group{op: opOr, cond: cond} +} + +func And(cond ...gs.Condition) gs.Condition { + return &group{op: opAnd, cond: cond} +} + +func None(cond ...gs.Condition) gs.Condition { + return &group{op: opNone, cond: cond} } func (g *group) matchesOr(ctx gs.CondContext) (bool, error) { @@ -207,11 +214,11 @@ func (g *group) Matches(ctx gs.CondContext) (bool, error) { return false, errors.New("no conditions in group") } switch g.op { - case Or: + case opOr: return g.matchesOr(ctx) - case And: + case opAnd: return g.matchesAnd(ctx) - case None: + case opNone: return g.matchesNone(ctx) default: return false, fmt.Errorf("error condition operator %d", g.op) @@ -243,13 +250,13 @@ func (n *node) Matches(ctx gs.CondContext) (bool, error) { } switch n.op { - case Or: + case opOr: if ok { return ok, nil } else { return n.next.Matches(ctx) } - case And: + case opAnd: if ok { return n.next.Matches(ctx) } else { @@ -279,7 +286,7 @@ func (c *Conditional) Matches(ctx gs.CondContext) (bool, error) { // Or sets a Or operator. func (c *Conditional) Or() *Conditional { n := &node{} - c.curr.op = Or + c.curr.op = opOr c.curr.next = n c.curr = n return c @@ -288,7 +295,7 @@ func (c *Conditional) Or() *Conditional { // And sets a And operator. func (c *Conditional) And() *Conditional { n := &node{} - c.curr.op = And + c.curr.op = opAnd c.curr.next = n c.curr = n return c diff --git a/gs/internal/gs_cond/cond_test.go b/gs/internal/gs_cond/cond_test.go index e524d082..9cca0f96 100644 --- a/gs/internal/gs_cond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -436,7 +436,7 @@ func TestGroup(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ok, err := gs_cond.Group(gs_cond.And, gs_cond.OK()).Matches(ctx) + ok, err := gs_cond.And(gs_cond.OK()).Matches(ctx) assert.Nil(t, err) assert.True(t, ok) }) @@ -444,7 +444,7 @@ func TestGroup(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ok, err := gs_cond.Group(gs_cond.And, gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) + ok, err := gs_cond.And(gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) assert.Nil(t, err) assert.False(t, ok) }) @@ -452,7 +452,7 @@ func TestGroup(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ok, err := gs_cond.Group(gs_cond.Or, gs_cond.OK()).Matches(ctx) + ok, err := gs_cond.Or(gs_cond.OK()).Matches(ctx) assert.Nil(t, err) assert.True(t, ok) }) @@ -460,7 +460,7 @@ func TestGroup(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ok, err := gs_cond.Group(gs_cond.Or, gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) + ok, err := gs_cond.Or(gs_cond.OK(), gs_cond.Not(gs_cond.OK())).Matches(ctx) assert.Nil(t, err) assert.True(t, ok) }) From bf7b03abeaf027841082049a2721cfb340e7eb26 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 7 Jan 2025 08:36:11 +0800 Subject: [PATCH 44/75] export arg pkg --- gs/gs.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index b1fc1936..c8817e6f 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -23,6 +23,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" + "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_core" "github.com/go-spring/spring-core/gs/sysconf" @@ -43,10 +44,61 @@ type ( GroupFunc = gs.GroupFunc ) +/************************************ arg ***********************************/ + +// IndexArg returns an IndexArg. +func IndexArg(n int, arg gs.Arg) gs_arg.IndexArg { + return gs_arg.Index(n, arg) +} + +// R0 returns an IndexArg with index 0. +func R0(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R0(arg) } + +// R1 returns an IndexArg with index 1. +func R1(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R1(arg) } + +// R2 returns an IndexArg with index 2. +func R2(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R2(arg) } + +// R3 returns an IndexArg with index 3. +func R3(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R3(arg) } + +// R4 returns an IndexArg with index 4. +func R4(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R4(arg) } + +// R5 returns an IndexArg with index 5. +func R5(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R5(arg) } + +// R6 returns an IndexArg with index 6. +func R6(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R6(arg) } + +// NilArg return a ValueArg with a value of nil. +func NilArg() gs_arg.ValueArg { + return gs_arg.Nil() +} + +// ValueArg return a ValueArg with a value of v. +func ValueArg(v interface{}) gs_arg.ValueArg { + return gs_arg.Value(v) +} + +// OptionArg 返回 Option 函数的参数绑定。 +func OptionArg(fn interface{}, args ...gs.Arg) *gs_arg.OptionArg { + return gs_arg.Option(fn, args...) +} + +// MustBindArg 为 Option 方法绑定运行时参数。 +func MustBindArg(fn interface{}, args ...gs.Arg) *gs_arg.Callable { + return gs_arg.Provide(fn, args...) +} + +func BindArg(fn interface{}, args []gs.Arg, skip int) (*gs_arg.Callable, error) { + return gs_arg.Bind(fn, args, skip) +} + /************************************ cond ***********************************/ type ( - FuncCond = gs_cond.FuncCond Conditional = gs_cond.Conditional PropertyOption = gs_cond.PropertyOption ) @@ -76,10 +128,6 @@ func None(cond ...gs.Condition) gs.Condition { return gs_cond.None(cond...) } -func On(cond gs.Condition) *Conditional { - return gs_cond.On(cond) -} - func MatchIfMissing() PropertyOption { return gs_cond.MatchIfMissing() } From 7a27a967e2c3834bd8f03d8cebce99fcdd3a2cbf Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 7 Jan 2025 13:44:04 +0800 Subject: [PATCH 45/75] code refactor --- gs/gs.go | 74 ++++++++++++++++---------------- gs/internal/gs_app/app.go | 5 ++- gs/internal/gs_arg/arg.go | 18 ++++---- gs/internal/gs_core/core_test.go | 2 +- 4 files changed, 52 insertions(+), 47 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index c8817e6f..dbaf9501 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -21,10 +21,12 @@ import ( "reflect" "strings" + "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" "github.com/go-spring/spring-core/gs/sysconf" ) @@ -35,42 +37,42 @@ const ( ) type ( - BeanSelector = gs.BeanSelector - Condition = gs.Condition - CondContext = gs.CondContext - Arg = gs.Arg - Context = gs.Context - ContextAware = gs.ContextAware - GroupFunc = gs.GroupFunc + Arg = gs.Arg + BeanDefinition = gs.BeanDefinition + BeanSelector = gs.BeanSelector + CondContext = gs.CondContext + Condition = gs.Condition + Context = gs.Context + ContextAware = gs.ContextAware ) /************************************ arg ***********************************/ // IndexArg returns an IndexArg. -func IndexArg(n int, arg gs.Arg) gs_arg.IndexArg { +func IndexArg(n int, arg Arg) gs_arg.IndexArg { return gs_arg.Index(n, arg) } // R0 returns an IndexArg with index 0. -func R0(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R0(arg) } +func R0(arg Arg) gs_arg.IndexArg { return gs_arg.R0(arg) } // R1 returns an IndexArg with index 1. -func R1(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R1(arg) } +func R1(arg Arg) gs_arg.IndexArg { return gs_arg.R1(arg) } // R2 returns an IndexArg with index 2. -func R2(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R2(arg) } +func R2(arg Arg) gs_arg.IndexArg { return gs_arg.R2(arg) } // R3 returns an IndexArg with index 3. -func R3(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R3(arg) } +func R3(arg Arg) gs_arg.IndexArg { return gs_arg.R3(arg) } // R4 returns an IndexArg with index 4. -func R4(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R4(arg) } +func R4(arg Arg) gs_arg.IndexArg { return gs_arg.R4(arg) } // R5 returns an IndexArg with index 5. -func R5(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R5(arg) } +func R5(arg Arg) gs_arg.IndexArg { return gs_arg.R5(arg) } // R6 returns an IndexArg with index 6. -func R6(arg gs.Arg) gs_arg.IndexArg { return gs_arg.R6(arg) } +func R6(arg Arg) gs_arg.IndexArg { return gs_arg.R6(arg) } // NilArg return a ValueArg with a value of nil. func NilArg() gs_arg.ValueArg { @@ -83,16 +85,16 @@ func ValueArg(v interface{}) gs_arg.ValueArg { } // OptionArg 返回 Option 函数的参数绑定。 -func OptionArg(fn interface{}, args ...gs.Arg) *gs_arg.OptionArg { +func OptionArg(fn interface{}, args ...Arg) *gs_arg.OptionArg { return gs_arg.Option(fn, args...) } // MustBindArg 为 Option 方法绑定运行时参数。 -func MustBindArg(fn interface{}, args ...gs.Arg) *gs_arg.Callable { - return gs_arg.Provide(fn, args...) +func MustBindArg(fn interface{}, args ...Arg) *gs_arg.Callable { + return gs_arg.MustBind(fn, args...) } -func BindArg(fn interface{}, args []gs.Arg, skip int) (*gs_arg.Callable, error) { +func BindArg(fn interface{}, args []Arg, skip int) (*gs_arg.Callable, error) { return gs_arg.Bind(fn, args, skip) } @@ -104,27 +106,27 @@ type ( ) // OK returns a Condition that always returns true. -func OK() gs.Condition { +func OK() Condition { return gs_cond.OK() } // Not returns a Condition that returns true when the given Condition returns false. -func Not(c gs.Condition) gs.Condition { +func Not(c Condition) Condition { return gs_cond.Not(c) } // Or returns a Condition that returns true when any of the given Conditions returns true. -func Or(cond ...gs.Condition) gs.Condition { +func Or(cond ...Condition) Condition { return gs_cond.Or(cond...) } // And returns a Condition that returns true when all the given Conditions return true. -func And(cond ...gs.Condition) gs.Condition { +func And(cond ...Condition) Condition { return gs_cond.And(cond...) } // None returns a Condition that returns true when none of the given Conditions returns true. -func None(cond ...gs.Condition) gs.Condition { +func None(cond ...Condition) Condition { return gs_cond.None(cond...) } @@ -144,15 +146,15 @@ func OnMissingProperty(name string) *Conditional { return gs_cond.OnMissingProperty(name) } -func OnBean(selector gs.BeanSelector) *Conditional { +func OnBean(selector BeanSelector) *Conditional { return gs_cond.OnBean(selector) } -func OnMissingBean(selector gs.BeanSelector) *Conditional { +func OnMissingBean(selector BeanSelector) *Conditional { return gs_cond.OnMissingBean(selector) } -func OnSingleBean(selector gs.BeanSelector) *Conditional { +func OnSingleBean(selector BeanSelector) *Conditional { return gs_cond.OnSingleBean(selector) } @@ -160,7 +162,7 @@ func OnExpression(expression string) *Conditional { return gs_cond.OnExpression(expression) } -func OnMatches(fn func(ctx gs.CondContext) (bool, error)) *Conditional { +func OnMatches(fn func(ctx CondContext) (bool, error)) *Conditional { return gs_cond.OnMatches(fn) } @@ -192,14 +194,14 @@ func Boot() *gs_app.Boot { /*********************************** app *************************************/ +type AppConfigAware struct { + AppConfig *gs_conf.AppConfig +} + var app = gs_app.NewApp() // Start 启动程序。 func Start() error { - printBanner() - if err := bootRun(); err != nil { - return err - } return app.Start() } @@ -223,21 +225,21 @@ func ShutDown(msg ...string) { } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *gs.BeanDefinition { +func Object(i interface{}) *BeanDefinition { return app.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { +func Provide(ctor interface{}, args ...Arg) *BeanDefinition { return app.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 -func Accept(b *gs.BeanDefinition) *gs.BeanDefinition { +func Accept(b *BeanDefinition) *BeanDefinition { return app.Accept(b) } -func Group(fn GroupFunc) { +func Group(fn func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error)) { app.Group(fn) } diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 19fbe10a..b89559d7 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -51,11 +51,14 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { - return &App{ + app := &App{ c: gs_core.New(), p: gs_conf.NewAppConfig(), exitChan: make(chan struct{}), } + app.Object(app) + app.Object(app.p) + return app } func (app *App) Config() *gs_conf.AppConfig { diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 21c914f1..19c8fe80 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -264,15 +264,6 @@ type OptionArg struct { c gs.Condition } -// Provide 为 Option 方法绑定运行时参数。 -func Provide(fn interface{}, args ...gs.Arg) *Callable { - r, err := Bind(fn, args, 1) - if err != nil { - panic(err) - } - return r -} - // Option 返回 Option 函数的参数绑定。 func Option(fn interface{}, args ...gs.Arg) *OptionArg { @@ -335,6 +326,15 @@ type Callable struct { fileLine string } +// MustBind 为 Option 方法绑定运行时参数。 +func MustBind(fn interface{}, args ...gs.Arg) *Callable { + r, err := Bind(fn, args, 1) + if err != nil { + panic(err) + } + return r +} + // Bind returns a Callable that wrappers a function and its binding arguments. // The argument skip is the number of frames to skip over. func Bind(fn interface{}, args []gs.Arg, skip int) (*Callable, error) { diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index bb82ceca..c44cdc34 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -1366,7 +1366,7 @@ func TestOptionConstructorArg(t *testing.T) { c.Provide(NewClassRoom, gs_arg.Option(withStudents), gs_arg.Option(withClassName, "${class_name:=二年级03班}", "${class_floor:=3}"), - gs_arg.Option(withBuilder, gs_arg.Provide(func(param string) *ClassBuilder { + gs_arg.Option(withBuilder, gs_arg.MustBind(func(param string) *ClassBuilder { return &ClassBuilder{param: param} }, gs_arg.Value("1"))), ) From 3125d2e0a3ea8ad33cdcd8f84b2bf64d0adc1718 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 7 Jan 2025 19:22:26 +0800 Subject: [PATCH 46/75] code refactor --- gs/gs.go | 4 ++-- gs/internal/gs/bean.go | 4 ++-- gs/internal/gs/gs.go | 12 +++--------- gs/internal/gs_app/app.go | 11 +++++++---- gs/internal/gs_app/boot.go | 3 ++- gs/internal/gs_core/core.go | 17 ++++++++++++----- gs/internal/gs_core/core_test.go | 4 ++-- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index dbaf9501..0b2752e4 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -43,7 +43,7 @@ type ( CondContext = gs.CondContext Condition = gs.Condition Context = gs.Context - ContextAware = gs.ContextAware + ContextAware = gs_core.ContextAware ) /************************************ arg ***********************************/ @@ -195,7 +195,7 @@ func Boot() *gs_app.Boot { /*********************************** app *************************************/ type AppConfigAware struct { - AppConfig *gs_conf.AppConfig + AppConfig *gs_conf.AppConfig `autowire:""` } var app = gs_app.NewApp() diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 4ffae188..823abd20 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -330,8 +330,8 @@ func (d *BeanDefinition) EnableRefresh(tag string) { } } -// Simplify 精简内存 -func (d *BeanDefinition) Simplify() { +// SimplifyMemory 精简内存 +func (d *BeanDefinition) SimplifyMemory() { d.r = nil } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 9eacbeea..b45c774d 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -50,16 +50,14 @@ type Callable interface { Call(ctx ArgContext) ([]reflect.Value, error) } -type GroupFunc func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error) - type Container interface { Object(i interface{}) *BeanDefinition Provide(ctor interface{}, args ...Arg) *BeanDefinition Accept(b *BeanDefinition) *BeanDefinition - Group(fn GroupFunc) + Group(fn func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error)) RefreshProperties(p conf.ReadOnlyProperties) error Refresh() error - Simplify() + SimplifyMemory() Close() } @@ -73,6 +71,7 @@ type Container interface { // App 对象,从而实现使用方式的统一。 type Context interface { Context() context.Context + RefreshProperties(p conf.ReadOnlyProperties) error Keys() []string Has(key string) bool Prop(key string, opts ...conf.GetOption) string @@ -83,8 +82,3 @@ type Context interface { Invoke(fn interface{}, args ...Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) } - -// ContextAware injects the Context into a struct as the field GSContext. -type ContextAware struct { - GSContext Context `autowire:""` -} diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index b89559d7..10afba90 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -24,6 +24,7 @@ import ( "reflect" "syscall" + "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" @@ -80,7 +81,7 @@ func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.c.Accept(b) } -func (app *App) Group(fn gs.GroupFunc) { +func (app *App) Group(fn func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error)) { app.c.Group(fn) } @@ -116,17 +117,19 @@ func (app *App) Start() error { return err } + ctx := app.c.(gs.Context) + // 执行命令行启动器 for _, r := range app.Runners { - r.Run(app.c.(gs.Context)) + r.Run(ctx) } // 通知应用启动事件 for _, svr := range app.Servers { - svr.OnAppStart(app.c.(gs.Context)) + svr.OnAppStart(ctx) } - app.c.Simplify() + app.c.SimplifyMemory() return nil } diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 0d1b1582..f0c91671 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -19,6 +19,7 @@ package gs_app import ( "reflect" + "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" @@ -63,7 +64,7 @@ func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanDefinition { return b.c.Accept(bd) } -func (b *Boot) Group(fn gs.GroupFunc) { +func (b *Boot) Group(fn func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error)) { b.c.Group(fn) } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 55c3c19a..53f446a9 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -55,6 +55,13 @@ const ( var BeanDefinitionPtrType = reflect.TypeOf((*gs.BeanDefinition)(nil)) +type GroupFunc = func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error) + +// ContextAware injects the Context into a struct as the field GSContext. +type ContextAware struct { + GSContext gs.Context `autowire:""` +} + // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -66,7 +73,7 @@ type Container struct { beans []*gs.BeanDefinition beansByName map[string][]*gs.BeanDefinition beansByType map[reflect.Type][]*gs.BeanDefinition - groupFuncs []gs.GroupFunc + groupFuncs []GroupFunc p *dync.Properties ctx context.Context cancel context.CancelFunc @@ -108,7 +115,7 @@ func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return b } -func (c *Container) Group(fn gs.GroupFunc) { +func (c *Container) Group(fn GroupFunc) { c.groupFuncs = append(c.groupFuncs, fn) } @@ -252,10 +259,10 @@ func (c *Container) Refresh() (err error) { return nil } -// Simplify 清理运行时不需要的空间。 -func (c *Container) Simplify() { +// SimplifyMemory 清理运行时不需要的空间。 +func (c *Container) SimplifyMemory() { for _, bd := range c.beans { - bd.Simplify() + bd.SimplifyMemory() } c.beans = nil c.groupFuncs = nil diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index c44cdc34..1f023208 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -2881,7 +2881,7 @@ func TestDestroyDependence(t *testing.T) { } type ContextAware struct { - gs.ContextAware + gs_core.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -3044,7 +3044,7 @@ func TestConfiguration(t *testing.T) { c := gs_core.New() c.Object(&ConfigurationBean{"123"}).Configuration(nil, []string{"NewBean"}).Name("123") c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") - ctx := &gs.ContextAware{} + ctx := &gs_core.ContextAware{} c.Object(ctx) if err := c.Refresh(); err != nil { t.Fatal(err) From 485357f468efcef765d4433f12b1087ac144581b Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 8 Jan 2025 08:30:26 +0800 Subject: [PATCH 47/75] move dync into gs internal --- conf/bind.go | 14 +++++----- conf/conf.go | 6 ++--- gs/gs.go | 3 +-- gs/internal/gs/bean.go | 3 +-- gs/internal/gs/gs.go | 22 ++++++++++++--- gs/internal/gs_app/app.go | 3 +-- gs/internal/gs_app/boot.go | 3 +-- gs/internal/gs_conf/conf.go | 31 ++++------------------ gs/internal/gs_core/core.go | 10 +++---- gs/internal/gs_core/core_test.go | 18 ++++++------- gs/internal/gs_core/wire.go | 3 +-- {dync => gs/internal/gs_dync}/dync.go | 24 +++++++---------- {dync => gs/internal/gs_dync}/dync_test.go | 22 +++++++-------- 13 files changed, 74 insertions(+), 88 deletions(-) rename {dync => gs/internal/gs_dync}/dync.go (89%) rename {dync => gs/internal/gs_dync}/dync_test.go (90%) diff --git a/conf/bind.go b/conf/bind.go index 110eaaf8..fbb37529 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -118,7 +118,7 @@ type Filter interface { } // BindValue binds properties to a value. -func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { +func BindValue(p readOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) (RetErr error) { if !util.IsValueType(t) { err := errors.New("target should be value type") @@ -212,7 +212,7 @@ func BindValue(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind } // bindSlice binds properties to a slice value. -func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindSlice(p readOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { et := t.Elem() p, err := getSlice(p, et, param) @@ -245,7 +245,7 @@ func bindSlice(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bind return nil } -func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyProperties, error) { +func getSlice(p readOnlyProperties, et reflect.Type, param BindParam) (readOnlyProperties, error) { // properties that defined as list. if p.Has(param.Key + "[0]") { @@ -301,7 +301,7 @@ func getSlice(p ReadOnlyProperties, et reflect.Type, param BindParam) (ReadOnlyP } // bindMap binds properties to a map value. -func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindMap(p readOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("map can't have a non-empty default value") @@ -337,7 +337,7 @@ func bindMap(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindPa } // bindStruct binds properties to a struct value. -func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { +func bindStruct(p readOnlyProperties, v reflect.Value, t reflect.Type, param BindParam, filter Filter) error { if param.Tag.HasDef && param.Tag.Def != "" { err := errors.New("struct can't have a non-empty default value") @@ -404,7 +404,7 @@ func bindStruct(p ReadOnlyProperties, v reflect.Value, t reflect.Type, param Bin } // resolve returns property references processed property value. -func resolve(p ReadOnlyProperties, param BindParam) (string, error) { +func resolve(p readOnlyProperties, param BindParam) (string, error) { const defVal = "@@def@@" val := p.Get(param.Key, Def(defVal)) if val != defVal { @@ -422,7 +422,7 @@ func resolve(p ReadOnlyProperties, param BindParam) (string, error) { } // resolveString returns property references processed string. -func resolveString(p ReadOnlyProperties, s string) (string, error) { +func resolveString(p readOnlyProperties, s string) (string, error) { var ( length = len(s) diff --git a/conf/conf.go b/conf/conf.go index 84c7372e..13de6bba 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -85,8 +85,8 @@ func RegisterConverter[T any](fn Converter[T]) { converters[t.Out(0)] = fn } -// ReadOnlyProperties is the interface for read-only properties. -type ReadOnlyProperties interface { +// readOnlyProperties is the interface for read-only properties. +type readOnlyProperties interface { // Data returns key-value pairs of the properties. Data() map[string]string @@ -113,7 +113,7 @@ type ReadOnlyProperties interface { CopyTo(out *Properties) error } -var _ ReadOnlyProperties = (*Properties)(nil) +var _ readOnlyProperties = (*Properties)(nil) // Properties stores the data with map[string]string and the keys are case-sensitive, // you can get one of them by its key, or bind some of them to a value. diff --git a/gs/gs.go b/gs/gs.go index 0b2752e4..ab73f2df 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -21,7 +21,6 @@ import ( "reflect" "strings" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" "github.com/go-spring/spring-core/gs/internal/gs_arg" @@ -239,7 +238,7 @@ func Accept(b *BeanDefinition) *BeanDefinition { return app.Accept(b) } -func Group(fn func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error)) { +func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { app.Group(fn) } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 823abd20..dee2eda3 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -6,11 +6,10 @@ import ( "reflect" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/util" ) -var refreshableType = reflect.TypeFor[dync.Refreshable]() +var refreshableType = reflect.TypeFor[Refreshable]() type BeanStatus int8 diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index b45c774d..ea25d7dc 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -50,12 +50,28 @@ type Callable interface { Call(ctx ArgContext) ([]reflect.Value, error) } +type Properties interface { + Data() map[string]string + Keys() []string + Has(key string) bool + SubKeys(key string) ([]string, error) + Get(key string, opts ...conf.GetOption) string + Resolve(s string) (string, error) + Bind(i interface{}, args ...conf.BindArg) error + CopyTo(out *conf.Properties) error +} + +// Refreshable 可动态刷新的对象 +type Refreshable interface { + OnRefresh(prop Properties, param conf.BindParam) error +} + type Container interface { Object(i interface{}) *BeanDefinition Provide(ctor interface{}, args ...Arg) *BeanDefinition Accept(b *BeanDefinition) *BeanDefinition - Group(fn func(p conf.ReadOnlyProperties) ([]*BeanDefinition, error)) - RefreshProperties(p conf.ReadOnlyProperties) error + Group(fn func(p Properties) ([]*BeanDefinition, error)) + RefreshProperties(p Properties) error Refresh() error SimplifyMemory() Close() @@ -71,7 +87,7 @@ type Container interface { // App 对象,从而实现使用方式的统一。 type Context interface { Context() context.Context - RefreshProperties(p conf.ReadOnlyProperties) error + RefreshProperties(p Properties) error Keys() []string Has(key string) bool Prop(key string, opts ...conf.GetOption) string diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 10afba90..baf95b69 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -24,7 +24,6 @@ import ( "reflect" "syscall" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" @@ -81,7 +80,7 @@ func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { return app.c.Accept(b) } -func (app *App) Group(fn func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error)) { +func (app *App) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { app.c.Group(fn) } diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index f0c91671..0ed6923f 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -19,7 +19,6 @@ package gs_app import ( "reflect" - "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" @@ -64,7 +63,7 @@ func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanDefinition { return b.c.Accept(bd) } -func (b *Boot) Group(fn func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error)) { +func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { b.c.Group(fn) } diff --git a/gs/internal/gs_conf/conf.go b/gs/internal/gs_conf/conf.go index fa681c72..4a51141d 100644 --- a/gs/internal/gs_conf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -23,31 +23,11 @@ import ( "sync/atomic" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/sysconf" ) -// AtomicProperties is a thread-safe version of Properties. -type AtomicProperties struct { - v atomic.Pointer[conf.Properties] -} - -// NewAtomicProperties creates a new atomic properties. -func NewAtomicProperties() *AtomicProperties { - return new(AtomicProperties) -} - -// Load loads as read-only properties. -func (p *AtomicProperties) Load() conf.ReadOnlyProperties { - if s := p.v.Load(); s != nil { - return s - } - return nil -} - -// Store stores a new properties. -func (p *AtomicProperties) Store(v *conf.Properties) { - p.v.Store(v) -} +type AtomicProperties = atomic.Pointer[conf.Properties] /******************************** AppConfig **********************************/ @@ -55,7 +35,7 @@ func (p *AtomicProperties) Store(v *conf.Properties) { type AppConfig struct { LocalFile *PropertySources RemoteFile *PropertySources - RemoteProp *AtomicProperties + RemoteProp AtomicProperties Environment *Environment CommandArgs *CommandArgs } @@ -64,7 +44,6 @@ func NewAppConfig() *AppConfig { return &AppConfig{ LocalFile: NewPropertySources(ConfigTypeLocal, "application"), RemoteFile: NewPropertySources(ConfigTypeRemote, "application"), - RemoteProp: NewAtomicProperties(), Environment: NewEnvironment(), CommandArgs: NewCommandArgs(), } @@ -84,7 +63,7 @@ func merge(out *conf.Properties, sources ...interface { } // Refresh merges all layers into a properties as read-only. -func (c *AppConfig) Refresh() (conf.ReadOnlyProperties, error) { +func (c *AppConfig) Refresh() (gs.Properties, error) { p := sysconf.Clone() err := merge(p, c.Environment, c.CommandArgs) @@ -141,7 +120,7 @@ func NewBootConfig() *BootConfig { } // Refresh merges all layers into a properties as read-only. -func (c *BootConfig) Refresh() (conf.ReadOnlyProperties, error) { +func (c *BootConfig) Refresh() (gs.Properties, error) { p := sysconf.Clone() err := merge(p, c.Environment, c.CommandArgs) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 53f446a9..cd231154 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -28,10 +28,10 @@ import ( "time" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" + "github.com/go-spring/spring-core/gs/internal/gs_dync" "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" ) @@ -55,7 +55,7 @@ const ( var BeanDefinitionPtrType = reflect.TypeOf((*gs.BeanDefinition)(nil)) -type GroupFunc = func(p conf.ReadOnlyProperties) ([]*gs.BeanDefinition, error) +type GroupFunc = func(p gs.Properties) ([]*gs.BeanDefinition, error) // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { @@ -74,7 +74,7 @@ type Container struct { beansByName map[string][]*gs.BeanDefinition beansByType map[reflect.Type][]*gs.BeanDefinition groupFuncs []GroupFunc - p *dync.Properties + p *gs_dync.Properties ctx context.Context cancel context.CancelFunc wg sync.WaitGroup @@ -89,7 +89,7 @@ func New() gs.Container { c := &Container{ ctx: ctx, cancel: cancel, - p: dync.New(), + p: gs_dync.New(), beansByName: make(map[string][]*gs.BeanDefinition), beansByType: make(map[reflect.Type][]*gs.BeanDefinition), } @@ -144,7 +144,7 @@ func (c *Container) Bind(i interface{}, opts ...conf.BindArg) error { return c.p.Data().Bind(i, opts...) } -func (c *Container) RefreshProperties(p conf.ReadOnlyProperties) error { +func (c *Container) RefreshProperties(p gs.Properties) error { return c.p.Refresh(p) } diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 1f023208..18245053 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -29,11 +29,11 @@ import ( "time" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_core" + "github.com/go-spring/spring-core/gs/internal/gs_dync" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" "github.com/go-spring/spring-core/util" @@ -2903,15 +2903,15 @@ func TestContextAware(t *testing.T) { } type DynamicConfig struct { - Int dync.Value[int64] `value:"${int:=3}" expr:"$<6"` - Float dync.Value[float64] `value:"${float:=1.2}"` - Map dync.Value[map[string]string] `value:"${map:=}"` - Slice dync.Value[[]string] `value:"${slice:=}"` + Int gs_dync.Value[int64] `value:"${int:=3}" expr:"$<6"` + Float gs_dync.Value[float64] `value:"${float:=1.2}"` + Map gs_dync.Value[map[string]string] `value:"${map:=}"` + Slice gs_dync.Value[[]string] `value:"${slice:=}"` } -var _ dync.Refreshable = (*DynamicConfig)(nil) +var _ gs.Refreshable = (*DynamicConfig)(nil) -func (d *DynamicConfig) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (d *DynamicConfig) OnRefresh(prop gs.Properties, param conf.BindParam) error { fmt.Println("DynamicConfig.OnRefresh") return nil } @@ -2920,9 +2920,9 @@ type DynamicConfigWrapper struct { Wrapper DynamicConfig `value:"${wrapper}"` // struct 自身的 refresh 会抑制 fields 的 refresh } -var _ dync.Refreshable = (*DynamicConfigWrapper)(nil) +var _ gs.Refreshable = (*DynamicConfigWrapper)(nil) -func (d *DynamicConfigWrapper) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (d *DynamicConfigWrapper) OnRefresh(prop gs.Properties, param conf.BindParam) error { fmt.Println("DynamicConfig.OnRefresh") return nil } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 8f2dc2d5..9ab4e316 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" @@ -593,7 +592,7 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 if b.IsRefreshEnable() { - i := b.Interface().(dync.Refreshable) + i := b.Interface().(gs.Refreshable) refreshParam := b.GetRefreshParam() watch := c.state == Refreshing if err = c.p.RefreshBean(i, refreshParam, watch); err != nil { diff --git a/dync/dync.go b/gs/internal/gs_dync/dync.go similarity index 89% rename from dync/dync.go rename to gs/internal/gs_dync/dync.go index 7594d64e..cf1bf942 100644 --- a/dync/dync.go +++ b/gs/internal/gs_dync/dync.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package dync +package gs_dync import ( "encoding/json" @@ -26,13 +26,9 @@ import ( "sync/atomic" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" ) -// Refreshable 可动态刷新的对象 -type Refreshable interface { - OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error -} - // Value 可动态刷新的对象 type Value[T interface{}] struct { v atomic.Value @@ -44,7 +40,7 @@ func (r *Value[T]) Value() T { } // OnRefresh 实现 Refreshable 接口 -func (r *Value[T]) OnRefresh(prop conf.ReadOnlyProperties, param conf.BindParam) error { +func (r *Value[T]) OnRefresh(prop gs.Properties, param conf.BindParam) error { var o T v := reflect.ValueOf(&o).Elem() err := conf.BindValue(prop, v, v.Type(), param, nil) @@ -62,13 +58,13 @@ func (r *Value[T]) MarshalJSON() ([]byte, error) { // refreshObject 绑定的可刷新对象 type refreshObject struct { - target Refreshable + target gs.Refreshable param conf.BindParam } // Properties 动态属性 type Properties struct { - prop conf.ReadOnlyProperties + prop gs.Properties lock sync.RWMutex objects []*refreshObject } @@ -81,7 +77,7 @@ func New() *Properties { } // Data 获取属性列表 -func (p *Properties) Data() conf.ReadOnlyProperties { +func (p *Properties) Data() gs.Properties { p.lock.RLock() defer p.lock.RUnlock() return p.prop @@ -95,7 +91,7 @@ func (p *Properties) ObjectsCount() int { } // Refresh 更新属性列表以及绑定的可刷新对象 -func (p *Properties) Refresh(prop conf.ReadOnlyProperties) (err error) { +func (p *Properties) Refresh(prop gs.Properties) (err error) { p.lock.Lock() defer p.lock.Unlock() @@ -214,13 +210,13 @@ func (p *Properties) safeRefreshObject(f *refreshObject) (err error) { } // RefreshBean 刷新一个 bean 对象,根据 watch 的值决定是否添加监听 -func (p *Properties) RefreshBean(v Refreshable, param conf.BindParam, watch bool) error { +func (p *Properties) RefreshBean(v gs.Refreshable, param conf.BindParam, watch bool) error { p.lock.Lock() defer p.lock.Unlock() return p.doRefresh(v, param, watch) } -func (p *Properties) doRefresh(v Refreshable, param conf.BindParam, watch bool) error { +func (p *Properties) doRefresh(v gs.Refreshable, param conf.BindParam, watch bool) error { if watch { p.objects = append(p.objects, &refreshObject{ target: v, @@ -236,7 +232,7 @@ type filter struct { } func (f *filter) Do(i interface{}, param conf.BindParam) (bool, error) { - v, ok := i.(Refreshable) + v, ok := i.(gs.Refreshable) if !ok { return false, nil } diff --git a/dync/dync_test.go b/gs/internal/gs_dync/dync_test.go similarity index 90% rename from dync/dync_test.go rename to gs/internal/gs_dync/dync_test.go index 73c4bdb8..6dc2cd1f 100644 --- a/dync/dync_test.go +++ b/gs/internal/gs_dync/dync_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package dync_test +package gs_dync_test import ( "encoding/json" @@ -22,7 +22,7 @@ import ( "testing" "github.com/go-spring/spring-core/conf" - "github.com/go-spring/spring-core/dync" + "github.com/go-spring/spring-core/gs/internal/gs_dync" "github.com/go-spring/spring-core/util/assert" ) @@ -34,15 +34,15 @@ type StructValue struct { } type Config struct { - Int dync.Value[int64] `value:"${int:=3}" expr:"$<6"` - Float dync.Value[float64] `value:"${float:=1.2}"` - Map dync.Value[map[string]string] `value:"${map:=}"` - Slice dync.Value[[]string] `value:"${slice:=}"` - Object dync.Value[StructValue] `value:"${object:=}"` + Int gs_dync.Value[int64] `value:"${int:=3}" expr:"$<6"` + Float gs_dync.Value[float64] `value:"${float:=1.2}"` + Map gs_dync.Value[map[string]string] `value:"${map:=}"` + Slice gs_dync.Value[[]string] `value:"${slice:=}"` + Object gs_dync.Value[StructValue] `value:"${object:=}"` } -func newTest() (*dync.Properties, *Config, error) { - mgr := dync.New() +func newTest() (*gs_dync.Properties, *Config, error) { + mgr := gs_dync.New() cfg := new(Config) err := mgr.RefreshField(reflect.ValueOf(cfg), conf.BindParam{}, true) if err != nil { @@ -92,7 +92,7 @@ func TestDynamic(t *testing.T) { t.Run("init validate error", func(t *testing.T) { - mgr := dync.New() + mgr := gs_dync.New() cfg := new(Config) // cfg.Int.OnValidate(func(v int64) error { // if v < 3 { @@ -130,7 +130,7 @@ func TestDynamic(t *testing.T) { t.Run("success", func(t *testing.T) { - mgr := dync.New() + mgr := gs_dync.New() cfg := new(Config) // cfg.Int.OnValidate(func(v int64) error { // if v < 3 { From e25f4554cce3789328ae75ed18497564e1439988 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 8 Jan 2025 10:32:50 +0800 Subject: [PATCH 48/75] code refactor --- gs/internal/gs_conf/conf.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gs/internal/gs_conf/conf.go b/gs/internal/gs_conf/conf.go index 4a51141d..38de02cc 100644 --- a/gs/internal/gs_conf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -20,22 +20,19 @@ import ( "fmt" "os" "strings" - "sync/atomic" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/sysconf" ) -type AtomicProperties = atomic.Pointer[conf.Properties] - /******************************** AppConfig **********************************/ // AppConfig is a layered app configuration. type AppConfig struct { LocalFile *PropertySources RemoteFile *PropertySources - RemoteProp AtomicProperties + RemoteProp gs.Properties Environment *Environment CommandArgs *CommandArgs } @@ -90,7 +87,7 @@ func (c *AppConfig) Refresh() (gs.Properties, error) { for _, file := range remoteFiles { sources = append(sources, file) } - sources = append(sources, c.RemoteProp.Load()) + sources = append(sources, c.RemoteProp) sources = append(sources, c.Environment) sources = append(sources, c.CommandArgs) From bcdc3fb6670fb578a9c116b067b3b50147802cd9 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 8 Jan 2025 10:55:36 +0800 Subject: [PATCH 49/75] use go1.24 --- go.mod | 2 +- gs/gs.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d66dca95..3c912cfe 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-spring/spring-core -go 1.22 +go 1.24 require ( github.com/expr-lang/expr v1.16.9 diff --git a/gs/gs.go b/gs/gs.go index ab73f2df..26fdb3eb 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -27,6 +27,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" + "github.com/go-spring/spring-core/gs/internal/gs_dync" "github.com/go-spring/spring-core/gs/sysconf" ) @@ -43,6 +44,7 @@ type ( Condition = gs.Condition Context = gs.Context ContextAware = gs_core.ContextAware + Dync[T any] = gs_dync.Value[T] ) /************************************ arg ***********************************/ From 3a0db73b41b2d4f8be1979ef233560b63bfcae5c Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 8 Jan 2025 13:40:37 +0800 Subject: [PATCH 50/75] code refactor --- gs/gs.go | 33 ++++++++++------- gs/internal/gs/bean.go | 11 +++--- gs/internal/gs/gs.go | 63 ++++++++++++++++++++++++++++++-- gs/internal/gs_app/app.go | 16 +++++--- gs/internal/gs_app/boot.go | 16 +++++--- gs/internal/gs_core/core.go | 15 +++++--- gs/internal/gs_core/core_test.go | 26 ++++++------- 7 files changed, 127 insertions(+), 53 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 26fdb3eb..fded6767 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -37,14 +37,15 @@ const ( ) type ( - Arg = gs.Arg - BeanDefinition = gs.BeanDefinition - BeanSelector = gs.BeanSelector - CondContext = gs.CondContext - Condition = gs.Condition - Context = gs.Context - ContextAware = gs_core.ContextAware - Dync[T any] = gs_dync.Value[T] + Arg = gs.Arg + BeanDefinition = gs.BeanDefinition + BeanRegistration = gs.BeanRegistration + BeanSelector = gs.BeanSelector + CondContext = gs.CondContext + Condition = gs.Condition + Context = gs.Context + ContextAware = gs_core.ContextAware + Dync[T any] = gs_dync.Value[T] ) /************************************ arg ***********************************/ @@ -226,18 +227,22 @@ func ShutDown(msg ...string) { } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *BeanDefinition { - return app.Accept(gs_core.NewBean(reflect.ValueOf(i))) +func Object(i interface{}) *BeanRegistration { + b := gs_core.NewBean(reflect.ValueOf(i)) + app.Accept(b) + return &BeanRegistration{B: b} } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...Arg) *BeanDefinition { - return app.Accept(gs_core.NewBean(ctor, args...)) +func Provide(ctor interface{}, args ...Arg) *BeanRegistration { + b := gs_core.NewBean(ctor, args...) + app.Accept(b) + return &BeanRegistration{B: b} } // Accept 参考 Container.Accept 的解释。 -func Accept(b *BeanDefinition) *BeanDefinition { - return app.Accept(b) +func Accept(b *BeanDefinition) { + app.Accept(b) } func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index dee2eda3..bbc98a9f 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -45,8 +45,8 @@ func GetStatusString(status BeanStatus) string { } } -// BeanRegistration bean 注册数据。 -type BeanRegistration struct { +// beanRegistration bean 注册数据。 +type beanRegistration struct { f Callable // 构造函数 method bool // 是否为成员方法 cond Condition // 判断条件 @@ -68,7 +68,7 @@ type BeanRegistration struct { // BeanDefinition bean 元数据。 type BeanDefinition struct { - r *BeanRegistration + r *beanRegistration v reflect.Value // 值 t reflect.Type // 类型 @@ -318,7 +318,7 @@ func (d *BeanDefinition) Configuration(includes []string, excludes []string) *Be } // EnableRefresh 设置 bean 为可刷新的。 -func (d *BeanDefinition) EnableRefresh(tag string) { +func (d *BeanDefinition) EnableRefresh(tag string) *BeanDefinition { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } @@ -327,6 +327,7 @@ func (d *BeanDefinition) EnableRefresh(tag string) { if err != nil { panic(err) } + return d } // SimplifyMemory 精简内存 @@ -341,7 +342,7 @@ func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bo v: v, name: name, typeName: util.TypeName(t), - r: &BeanRegistration{ + r: &beanRegistration{ f: f, status: Default, method: method, diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index ea25d7dc..bdd4f648 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -66,10 +66,67 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } +type BeanRegistration struct { + B *BeanDefinition +} + +func (d *BeanRegistration) Name(name string) *BeanRegistration { + d.B.Name(name) + return d +} + +// On 设置 bean 的 Condition。 +func (d *BeanRegistration) On(cond Condition) *BeanRegistration { + d.B.On(cond) + return d +} + +// DependsOn 设置 bean 的间接依赖项。 +func (d *BeanRegistration) DependsOn(selectors ...BeanSelector) *BeanRegistration { + d.B.DependsOn(selectors...) + return d +} + +// Primary 设置 bean 为主版本。 +func (d *BeanRegistration) Primary() *BeanRegistration { + d.B.Primary() + return d +} + +// Init 设置 bean 的初始化函数。 +func (d *BeanRegistration) Init(fn interface{}) *BeanRegistration { + d.B.Init(fn) + return d +} + +// Destroy 设置 bean 的销毁函数。 +func (d *BeanRegistration) Destroy(fn interface{}) *BeanRegistration { + d.B.Destroy(fn) + return d +} + +// Export 设置 bean 的导出接口。 +func (d *BeanRegistration) Export(exports ...interface{}) *BeanRegistration { + d.B.Export(exports...) + return d +} + +// Configuration 设置 bean 为配置类。 +func (d *BeanRegistration) Configuration(includes []string, excludes []string) *BeanRegistration { + d.B.Configuration(includes, excludes) + return d +} + +// EnableRefresh 设置 bean 为可刷新的。 +func (d *BeanRegistration) EnableRefresh(tag string) *BeanRegistration { + d.B.EnableRefresh(tag) + return d +} + type Container interface { - Object(i interface{}) *BeanDefinition - Provide(ctor interface{}, args ...Arg) *BeanDefinition - Accept(b *BeanDefinition) *BeanDefinition + Object(i interface{}) *BeanRegistration + Provide(ctor interface{}, args ...Arg) *BeanRegistration + Accept(b *BeanDefinition) Group(fn func(p Properties) ([]*BeanDefinition, error)) RefreshProperties(p Properties) error Refresh() error diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index baf95b69..aef2c8e7 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -66,18 +66,22 @@ func (app *App) Config() *gs_conf.AppConfig { } // Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *gs.BeanDefinition { - return app.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) +func (app *App) Object(i interface{}) *gs.BeanRegistration { + b := gs_core.NewBean(reflect.ValueOf(i)) + app.c.Accept(b) + return &gs.BeanRegistration{B: b} } // Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return app.c.Accept(gs_core.NewBean(ctor, args...)) +func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { + b := gs_core.NewBean(ctor, args...) + app.c.Accept(b) + return &gs.BeanRegistration{B: b} } // Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { - return app.c.Accept(b) +func (app *App) Accept(b *gs.BeanDefinition) { + app.c.Accept(b) } func (app *App) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 0ed6923f..6558b1ea 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -49,18 +49,22 @@ func (b *Boot) Config() *gs_conf.BootConfig { } // Object 参考 Container.Object 的解释。 -func (b *Boot) Object(i interface{}) *gs.BeanDefinition { - return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) +func (b *Boot) Object(i interface{}) *gs.BeanRegistration { + bd := gs_core.NewBean(reflect.ValueOf(i)) + b.c.Accept(bd) + return &gs.BeanRegistration{B: bd} } // Provide 参考 Container.Provide 的解释。 -func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return b.c.Accept(gs_core.NewBean(ctor, args...)) +func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { + bd := gs_core.NewBean(ctor, args...) + b.c.Accept(bd) + return &gs.BeanRegistration{B: bd} } // Accept 参考 Container.Accept 的解释。 -func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanDefinition { - return b.c.Accept(bd) +func (b *Boot) Accept(bd *gs.BeanDefinition) { + b.c.Accept(bd) } func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index cd231154..b66dcea2 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -98,21 +98,24 @@ func New() gs.Container { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Object(i interface{}) *gs.BeanDefinition { - return c.Accept(NewBean(reflect.ValueOf(i))) +func (c *Container) Object(i interface{}) *gs.BeanRegistration { + b := NewBean(reflect.ValueOf(i)) + c.Accept(b) + return &gs.BeanRegistration{B: b} } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { - return c.Accept(NewBean(ctor, args...)) +func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { + b := NewBean(ctor, args...) + c.Accept(b) + return &gs.BeanRegistration{B: b} } -func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanDefinition { +func (c *Container) Accept(b *gs.BeanDefinition) { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } c.beans = append(c.beans, b) - return b } func (c *Container) Group(fn GroupFunc) { diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 18245053..ff407dcc 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -889,7 +889,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanName(), "NewManager") + assert.Equal(t, bd.B.BeanName(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1186,7 +1186,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.B.BeanName(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1436,7 +1436,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent) + bd := c.Provide((*Server).Consumer, parent.B) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1454,7 +1454,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanName(), "Consumer") + assert.Equal(t, bd.B.BeanName(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1477,7 +1477,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanName(), "Consumer") + assert.Equal(t, bd.B.BeanName(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1486,7 +1486,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent, "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.B, "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1512,7 +1512,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.B.ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1566,7 +1566,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.B.ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2022,7 +2022,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface().(*callDestroy) + d := bd.B.Interface().(*callDestroy) assert.True(t, d.destroyed) }) @@ -2039,7 +2039,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface() + d := bd.B.Interface() assert.False(t, d.(*callDestroy).destroyed) } @@ -2059,7 +2059,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface() + d := bd.B.Interface() assert.True(t, d.(*callDestroy).destroyed) } }) @@ -2467,7 +2467,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.B.ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -2898,7 +2898,7 @@ func TestContextAware(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() assert.Nil(t, err) - a := b.Interface().(*ContextAware) + a := b.B.Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } From 1f44a0e819fba10777532746aff89e7ddc4c8c3c Mon Sep 17 00:00:00 2001 From: lvan100 Date: Wed, 8 Jan 2025 20:18:39 +0800 Subject: [PATCH 51/75] code refactor --- gs/internal/gs/bean.go | 26 +++++++++++++------------- gs/internal/gs_core/core.go | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index bbc98a9f..d096b195 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -53,10 +53,8 @@ type beanRegistration struct { init interface{} // 初始化函数 destroy interface{} // 销毁函数 depends []BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 file string // 注册点所在文件 line int // 注册点所在行数 - status BeanStatus // 状态 configuration bool // 是否扫描成员方法 includeMethod []string // 包含哪些成员方法 @@ -73,9 +71,11 @@ type BeanDefinition struct { v reflect.Value // 值 t reflect.Type // 类型 - name string // 名称 - typeName string // 原始类型的全限定名 - primary bool // 是否为主版本 + name string // 名称 + typeName string // 原始类型的全限定名 + primary bool // 是否为主版本 + exports []reflect.Type // 导出的接口 + status BeanStatus // 状态 } func (d *BeanDefinition) GetName() string { @@ -87,11 +87,11 @@ func (d *BeanDefinition) GetTypeName() string { } func (d *BeanDefinition) GetStatus() BeanStatus { - return d.r.status + return d.status } func (d *BeanDefinition) SetStatus(status BeanStatus) { - d.r.status = status + d.status = status } func (d *BeanDefinition) IsPrimary() bool { @@ -119,7 +119,7 @@ func (d *BeanDefinition) GetDepends() []BeanSelector { } func (d *BeanDefinition) GetExports() []reflect.Type { - return d.r.exports + return d.exports } func (d *BeanDefinition) IsConfiguration() bool { @@ -178,12 +178,12 @@ func (d *BeanDefinition) TypeName() string { // Created 返回是否已创建。 func (d *BeanDefinition) Created() bool { - return d.r.status >= Created + return d.status >= Created } // Wired 返回 bean 是否已经注入。 func (d *BeanDefinition) Wired() bool { - return d.r.status == Wired + return d.status == Wired } func (d *BeanDefinition) File() string { @@ -295,7 +295,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { panic(errors.New("only interface type can be exported")) } exported := false - for _, export := range d.r.exports { + for _, export := range d.exports { if t == export { exported = true break @@ -304,7 +304,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { if exported { continue } - d.r.exports = append(d.r.exports, t) + d.exports = append(d.exports, t) } return d } @@ -342,9 +342,9 @@ func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bo v: v, name: name, typeName: util.TypeName(t), + status: Default, r: &beanRegistration{ f: f, - status: Default, method: method, file: file, line: line, diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index b66dcea2..27f998cb 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -437,7 +437,7 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) var t reflect.Type switch st := selector.(type) { - case string, gs.BeanDefinition, *gs.BeanDefinition: + case string, *gs.BeanDefinition: tag, err := c.toWireTag(selector) if err != nil { return nil, err From d9eceaddbb42487e4d9ca81648d960e47a7c229a Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 08:11:02 +0800 Subject: [PATCH 52/75] code refactor --- gs/gs.go | 20 +++++++++---- gs/internal/gs/bean.go | 4 +++ gs/internal/gs/gs.go | 33 ++++++++++++++------- gs/internal/gs_app/app.go | 51 +++++++------------------------- gs/internal/gs_app/boot.go | 4 +-- gs/internal/gs_core/core.go | 4 +-- gs/internal/gs_core/core_test.go | 26 ++++++++-------- 7 files changed, 68 insertions(+), 74 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index fded6767..65c8c87b 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -226,27 +226,35 @@ func ShutDown(msg ...string) { app.ShutDown(msg...) } +func AppConfig() *gs_conf.AppConfig { + return app.P +} + +func RefreshProperties(p gs.Properties) error { + return app.C.RefreshProperties(p) +} + // Object 参考 Container.Object 的解释。 func Object(i interface{}) *BeanRegistration { b := gs_core.NewBean(reflect.ValueOf(i)) - app.Accept(b) - return &BeanRegistration{B: b} + app.C.Accept(b) + return b.BeanRegistration() } // Provide 参考 Container.Provide 的解释。 func Provide(ctor interface{}, args ...Arg) *BeanRegistration { b := gs_core.NewBean(ctor, args...) - app.Accept(b) - return &BeanRegistration{B: b} + app.C.Accept(b) + return b.BeanRegistration() } // Accept 参考 Container.Accept 的解释。 func Accept(b *BeanDefinition) { - app.Accept(b) + app.C.Accept(b) } func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { - app.Group(fn) + app.C.Group(fn) } /********************************** banner ***********************************/ diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index d096b195..b242923e 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -78,6 +78,10 @@ type BeanDefinition struct { status BeanStatus // 状态 } +func (d *BeanDefinition) BeanRegistration() *BeanRegistration { + return &BeanRegistration{d} +} + func (d *BeanDefinition) GetName() string { return d.name } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index bdd4f648..7d9ea868 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -67,59 +67,71 @@ type Refreshable interface { } type BeanRegistration struct { - B *BeanDefinition + b *BeanDefinition } func (d *BeanRegistration) Name(name string) *BeanRegistration { - d.B.Name(name) + d.b.Name(name) return d } +func (d *BeanRegistration) BeanName() string { + return d.b.BeanName() +} + +func (d *BeanRegistration) ID() string { + return d.b.ID() +} + +func (d *BeanRegistration) Interface() interface{} { + return d.b.Interface() +} + // On 设置 bean 的 Condition。 func (d *BeanRegistration) On(cond Condition) *BeanRegistration { - d.B.On(cond) + d.b.On(cond) return d } // DependsOn 设置 bean 的间接依赖项。 func (d *BeanRegistration) DependsOn(selectors ...BeanSelector) *BeanRegistration { - d.B.DependsOn(selectors...) + d.b.DependsOn(selectors...) return d } // Primary 设置 bean 为主版本。 func (d *BeanRegistration) Primary() *BeanRegistration { - d.B.Primary() + d.b.Primary() return d } // Init 设置 bean 的初始化函数。 func (d *BeanRegistration) Init(fn interface{}) *BeanRegistration { - d.B.Init(fn) + d.b.Init(fn) return d } // Destroy 设置 bean 的销毁函数。 func (d *BeanRegistration) Destroy(fn interface{}) *BeanRegistration { - d.B.Destroy(fn) + d.b.Destroy(fn) return d } // Export 设置 bean 的导出接口。 func (d *BeanRegistration) Export(exports ...interface{}) *BeanRegistration { - d.B.Export(exports...) + d.b.Export(exports...) return d } // Configuration 设置 bean 为配置类。 func (d *BeanRegistration) Configuration(includes []string, excludes []string) *BeanRegistration { - d.B.Configuration(includes, excludes) + d.b.Configuration(includes, excludes) return d } // EnableRefresh 设置 bean 为可刷新的。 func (d *BeanRegistration) EnableRefresh(tag string) *BeanRegistration { - d.B.EnableRefresh(tag) + d.b.EnableRefresh(tag) return d } @@ -144,7 +156,6 @@ type Container interface { // App 对象,从而实现使用方式的统一。 type Context interface { Context() context.Context - RefreshProperties(p Properties) error Keys() []string Has(key string) bool Prop(key string, opts ...conf.GetOption) string diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index aef2c8e7..7e83e5be 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -21,7 +21,6 @@ import ( "fmt" "os" "os/signal" - "reflect" "syscall" "github.com/go-spring/spring-core/gs/internal/gs" @@ -40,8 +39,8 @@ type AppServer interface { // App 应用 type App struct { - c gs.Container - p *gs_conf.AppConfig + C gs.Container + P *gs_conf.AppConfig exitChan chan struct{} @@ -52,42 +51,14 @@ type App struct { // NewApp application 的构造函数 func NewApp() *App { app := &App{ - c: gs_core.New(), - p: gs_conf.NewAppConfig(), + C: gs_core.New(), + P: gs_conf.NewAppConfig(), exitChan: make(chan struct{}), } - app.Object(app) - app.Object(app.p) + app.C.Object(app) return app } -func (app *App) Config() *gs_conf.AppConfig { - return app.p -} - -// Object 参考 Container.Object 的解释。 -func (app *App) Object(i interface{}) *gs.BeanRegistration { - b := gs_core.NewBean(reflect.ValueOf(i)) - app.c.Accept(b) - return &gs.BeanRegistration{B: b} -} - -// Provide 参考 Container.Provide 的解释。 -func (app *App) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { - b := gs_core.NewBean(ctor, args...) - app.c.Accept(b) - return &gs.BeanRegistration{B: b} -} - -// Accept 参考 Container.Accept 的解释。 -func (app *App) Accept(b *gs.BeanDefinition) { - app.c.Accept(b) -} - -func (app *App) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { - app.c.Group(fn) -} - func (app *App) Run() error { if err := app.Start(); err != nil { return err @@ -105,22 +76,22 @@ func (app *App) Run() error { func (app *App) Start() error { - p, err := app.p.Refresh() + p, err := app.P.Refresh() if err != nil { return err } - err = app.c.RefreshProperties(p) + err = app.C.RefreshProperties(p) if err != nil { return err } - err = app.c.Refresh() + err = app.C.Refresh() if err != nil { return err } - ctx := app.c.(gs.Context) + ctx := app.C.(gs.Context) // 执行命令行启动器 for _, r := range app.Runners { @@ -132,7 +103,7 @@ func (app *App) Start() error { svr.OnAppStart(ctx) } - app.c.SimplifyMemory() + app.C.SimplifyMemory() return nil } @@ -140,7 +111,7 @@ func (app *App) Stop() { for _, svr := range app.Servers { svr.OnAppStop(context.Background()) } - app.c.Close() + app.C.Close() } // ShutDown 关闭执行器 diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 6558b1ea..41ab71af 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -52,14 +52,14 @@ func (b *Boot) Config() *gs_conf.BootConfig { func (b *Boot) Object(i interface{}) *gs.BeanRegistration { bd := gs_core.NewBean(reflect.ValueOf(i)) b.c.Accept(bd) - return &gs.BeanRegistration{B: bd} + return bd.BeanRegistration() } // Provide 参考 Container.Provide 的解释。 func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { bd := gs_core.NewBean(ctor, args...) b.c.Accept(bd) - return &gs.BeanRegistration{B: bd} + return bd.BeanRegistration() } // Accept 参考 Container.Accept 的解释。 diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 27f998cb..ca050d2c 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -101,14 +101,14 @@ func New() gs.Container { func (c *Container) Object(i interface{}) *gs.BeanRegistration { b := NewBean(reflect.ValueOf(i)) c.Accept(b) - return &gs.BeanRegistration{B: b} + return b.BeanRegistration() } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { b := NewBean(ctor, args...) c.Accept(b) - return &gs.BeanRegistration{B: b} + return b.BeanRegistration() } func (c *Container) Accept(b *gs.BeanDefinition) { diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index ff407dcc..8172325c 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -889,7 +889,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.B.BeanName(), "NewManager") + assert.Equal(t, bd.BeanName(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1186,7 +1186,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.B.BeanName(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1436,7 +1436,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent.B) + bd := c.Provide((*Server).Consumer, parent.ID()) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1454,7 +1454,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.B.BeanName(), "Consumer") + assert.Equal(t, bd.BeanName(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1477,7 +1477,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.B.BeanName(), "Consumer") + assert.Equal(t, bd.BeanName(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1486,7 +1486,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent.B, "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.ID(), "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1512,7 +1512,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.B.ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1566,7 +1566,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.B.ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2022,7 +2022,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.B.Interface().(*callDestroy) + d := bd.Interface().(*callDestroy) assert.True(t, d.destroyed) }) @@ -2039,7 +2039,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.B.Interface() + d := bd.Interface() assert.False(t, d.(*callDestroy).destroyed) } @@ -2059,7 +2059,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.B.Interface() + d := bd.Interface() assert.True(t, d.(*callDestroy).destroyed) } }) @@ -2467,7 +2467,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.B.ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -2898,7 +2898,7 @@ func TestContextAware(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() assert.Nil(t, err) - a := b.B.Interface().(*ContextAware) + a := b.Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } From 05506ac337619da5305eb5ad627968cb0d40aa80 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 08:19:04 +0800 Subject: [PATCH 53/75] code refactor --- gs/gs.go | 12 ++++-------- gs/internal/gs/gs.go | 2 +- gs/internal/gs_app/boot.go | 12 ++++-------- gs/internal/gs_core/core.go | 11 ++++------- 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 65c8c87b..0bebb778 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -236,21 +236,17 @@ func RefreshProperties(p gs.Properties) error { // Object 参考 Container.Object 的解释。 func Object(i interface{}) *BeanRegistration { - b := gs_core.NewBean(reflect.ValueOf(i)) - app.C.Accept(b) - return b.BeanRegistration() + return app.C.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func Provide(ctor interface{}, args ...Arg) *BeanRegistration { - b := gs_core.NewBean(ctor, args...) - app.C.Accept(b) - return b.BeanRegistration() + return app.C.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 -func Accept(b *BeanDefinition) { - app.C.Accept(b) +func Accept(b *BeanDefinition) *BeanRegistration { + return app.C.Accept(b) } func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 7d9ea868..791dbd85 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -138,7 +138,7 @@ func (d *BeanRegistration) EnableRefresh(tag string) *BeanRegistration { type Container interface { Object(i interface{}) *BeanRegistration Provide(ctor interface{}, args ...Arg) *BeanRegistration - Accept(b *BeanDefinition) + Accept(b *BeanDefinition) *BeanRegistration Group(fn func(p Properties) ([]*BeanDefinition, error)) RefreshProperties(p Properties) error Refresh() error diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 41ab71af..ef74ac71 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -50,21 +50,17 @@ func (b *Boot) Config() *gs_conf.BootConfig { // Object 参考 Container.Object 的解释。 func (b *Boot) Object(i interface{}) *gs.BeanRegistration { - bd := gs_core.NewBean(reflect.ValueOf(i)) - b.c.Accept(bd) - return bd.BeanRegistration() + return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { - bd := gs_core.NewBean(ctor, args...) - b.c.Accept(bd) - return bd.BeanRegistration() + return b.c.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 -func (b *Boot) Accept(bd *gs.BeanDefinition) { - b.c.Accept(bd) +func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanRegistration { + return b.c.Accept(bd) } func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index ca050d2c..6c781ebb 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -99,23 +99,20 @@ func New() gs.Container { // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Object(i interface{}) *gs.BeanRegistration { - b := NewBean(reflect.ValueOf(i)) - c.Accept(b) - return b.BeanRegistration() + return c.Accept(NewBean(reflect.ValueOf(i))) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { - b := NewBean(ctor, args...) - c.Accept(b) - return b.BeanRegistration() + return c.Accept(NewBean(ctor, args...)) } -func (c *Container) Accept(b *gs.BeanDefinition) { +func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanRegistration { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } c.beans = append(c.beans, b) + return b.BeanRegistration() } func (c *Container) Group(fn GroupFunc) { From b28a3bda1c34b58cd198006fa89c01daff44e2af Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 10:29:45 +0800 Subject: [PATCH 54/75] expose AppRunner & AppServer & BootRunner --- gs/gs.go | 22 ++++++++++++++++++---- gs/internal/gs/bean.go | 6 ++++++ gs/internal/gs_app/boot.go | 8 +++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 0bebb778..0612bacf 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -230,18 +230,28 @@ func AppConfig() *gs_conf.AppConfig { return app.P } -func RefreshProperties(p gs.Properties) error { - return app.C.RefreshProperties(p) +func AppRunner(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { + b := gs_core.NewBean(objOrCtor, ctorArgs...) + b.Export((*gs_app.AppRunner)(nil)) + return app.C.Accept(b) +} + +func AppServer(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { + b := gs_core.NewBean(objOrCtor, ctorArgs...) + b.Export((*gs_app.AppServer)(nil)) + return app.C.Accept(b) } // Object 参考 Container.Object 的解释。 func Object(i interface{}) *BeanRegistration { - return app.C.Accept(gs_core.NewBean(reflect.ValueOf(i))) + b := gs_core.NewBean(reflect.ValueOf(i)) + return app.C.Accept(b) } // Provide 参考 Container.Provide 的解释。 func Provide(ctor interface{}, args ...Arg) *BeanRegistration { - return app.C.Accept(gs_core.NewBean(ctor, args...)) + b := gs_core.NewBean(ctor, args...) + return app.C.Accept(b) } // Accept 参考 Container.Accept 的解释。 @@ -253,6 +263,10 @@ func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { app.C.Group(fn) } +func RefreshProperties(p gs.Properties) error { + return app.C.RefreshProperties(p) +} + /********************************** banner ***********************************/ var appBanner = ` diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index b242923e..d7ed60d6 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "reflect" + "runtime" "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/util" @@ -237,6 +238,11 @@ func (d *BeanDefinition) Name(name string) *BeanDefinition { return d } +func (d *BeanDefinition) Caller(skip int) *BeanDefinition { + _, d.r.file, d.r.line, _ = runtime.Caller(skip) + return d +} + // On 设置 bean 的 Condition。 func (d *BeanDefinition) On(cond Condition) *BeanDefinition { d.r.cond = cond diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index ef74ac71..f7e47434 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -32,7 +32,7 @@ type Boot struct { c gs.Container p *gs_conf.BootConfig - Runners []AppRunner `autowire:"${spring.boot.runners:=*?}"` + Runners []BootRunner `autowire:"${spring.boot.runners:=*?}"` } func NewBoot() *Boot { @@ -48,6 +48,12 @@ func (b *Boot) Config() *gs_conf.BootConfig { return b.p } +func (b *Boot) Runner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanRegistration { + bd := gs_core.NewBean(objOrCtor, ctorArgs...) + bd.Export((*BootRunner)(nil)) + return b.c.Accept(bd) +} + // Object 参考 Container.Object 的解释。 func (b *Boot) Object(i interface{}) *gs.BeanRegistration { return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) From 7f0c69c8a0480b739a5c985e38a8c9589cc37e77 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 10:34:39 +0800 Subject: [PATCH 55/75] code refactor --- gs/gs.go | 10 ++++------ gs/internal/gs_app/app.go | 8 ++++---- gs/internal/gs_app/boot.go | 8 ++------ 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 0612bacf..6500ca06 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -46,6 +46,8 @@ type ( Context = gs.Context ContextAware = gs_core.ContextAware Dync[T any] = gs_dync.Value[T] + Runner = gs_app.Runner + Server = gs_app.Server ) /************************************ arg ***********************************/ @@ -196,10 +198,6 @@ func Boot() *gs_app.Boot { /*********************************** app *************************************/ -type AppConfigAware struct { - AppConfig *gs_conf.AppConfig `autowire:""` -} - var app = gs_app.NewApp() // Start 启动程序。 @@ -232,13 +230,13 @@ func AppConfig() *gs_conf.AppConfig { func AppRunner(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*gs_app.AppRunner)(nil)) + b.Export((*gs_app.Runner)(nil)) return app.C.Accept(b) } func AppServer(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*gs_app.AppServer)(nil)) + b.Export((*gs_app.Server)(nil)) return app.C.Accept(b) } diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 7e83e5be..67f40f5c 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -28,11 +28,11 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" ) -type AppRunner interface { +type Runner interface { Run(ctx gs.Context) } -type AppServer interface { +type Server interface { OnAppStart(ctx gs.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -44,8 +44,8 @@ type App struct { exitChan chan struct{} - Runners []AppRunner `autowire:"${spring.app.runners:=*?}"` - Servers []AppServer `autowire:"${spring.app.servers:=*?}"` + Runners []Runner `autowire:"${spring.app.runners:=*?}"` + Servers []Server `autowire:"${spring.app.servers:=*?}"` } // NewApp application 的构造函数 diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index f7e47434..4cccb026 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -24,15 +24,11 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" ) -type BootRunner interface { - Run(ctx gs.Context) -} - type Boot struct { c gs.Container p *gs_conf.BootConfig - Runners []BootRunner `autowire:"${spring.boot.runners:=*?}"` + Runners []Runner `autowire:"${spring.boot.runners:=*?}"` } func NewBoot() *Boot { @@ -50,7 +46,7 @@ func (b *Boot) Config() *gs_conf.BootConfig { func (b *Boot) Runner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanRegistration { bd := gs_core.NewBean(objOrCtor, ctorArgs...) - bd.Export((*BootRunner)(nil)) + bd.Export((*Runner)(nil)) return b.c.Accept(bd) } From 4e61d5f5f5b8245920b37c706b3797dc6ab40882 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 13:25:43 +0800 Subject: [PATCH 56/75] code refactor --- gs/internal/gs_core/core.go | 50 +++++++++++++++++++++++++++----- gs/internal/gs_core/core_test.go | 8 ++--- gs/internal/gs_core/wire.go | 34 ++++++++++------------ 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 6c781ebb..5ff275c7 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -62,6 +62,42 @@ type ContextAware struct { GSContext gs.Context `autowire:""` } +type beanWrapper struct { + b *gs.BeanDefinition +} + +func (d beanWrapper) Bean() *gs.BeanDefinition { + return d.b +} + +func (d beanWrapper) GetName() string { + return d.b.GetName() +} + +func (d beanWrapper) IsPrimary() bool { + return d.b.IsPrimary() +} + +func (d beanWrapper) Type() reflect.Type { + return d.b.Type() +} + +func (d beanWrapper) Value() reflect.Value { + return d.b.Value() +} + +func (d beanWrapper) GetStatus() gs.BeanStatus { + return d.b.GetStatus() +} + +func (d beanWrapper) Match(typeName string, beanName string) bool { + return d.b.Match(typeName, beanName) +} + +func (d beanWrapper) String() string { + return d.b.String() +} + // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -71,8 +107,8 @@ type ContextAware struct { // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { beans []*gs.BeanDefinition - beansByName map[string][]*gs.BeanDefinition - beansByType map[reflect.Type][]*gs.BeanDefinition + beansByName map[string][]beanWrapper + beansByType map[reflect.Type][]beanWrapper groupFuncs []GroupFunc p *gs_dync.Properties ctx context.Context @@ -90,8 +126,8 @@ func New() gs.Container { ctx: ctx, cancel: cancel, p: gs_dync.New(), - beansByName: make(map[string][]*gs.BeanDefinition), - beansByType: make(map[reflect.Type][]*gs.BeanDefinition), + beansByName: make(map[string][]beanWrapper), + beansByType: make(map[reflect.Type][]beanWrapper), } c.Object(c).Export((*gs.Context)(nil)) return c @@ -357,11 +393,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti func (c *Container) registerBean(b *gs.BeanDefinition) { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), b.Type(), b.FileLine()) - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) - c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], beanWrapper{b}) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], beanWrapper{b}) for _, t := range b.GetExports() { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), t, b.FileLine()) - c.beansByType[t] = append(c.beansByType[t], b) + c.beansByType[t] = append(c.beansByType[t], beanWrapper{b}) } } diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 8172325c..03dd0adc 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -136,9 +136,9 @@ type TestObject struct { // 指定名称时使用精确匹配模式,不对数组元素进行转换,即便能做到似乎也无意义 InterfaceSliceByName []fmt.Stringer `autowire:"struct_ptr_slice?"` - MapTyType map[string]interface{} `inject:"?"` - MapByName map[string]interface{} `autowire:"map?"` - MapByNam2 map[string]interface{} `autowire:"struct_ptr?"` + MapTyType map[string]fmt.Stringer `inject:"?"` + MapByName map[string]fmt.Stringer `autowire:"map?"` + MapByNam2 map[string]fmt.Stringer `autowire:"struct_ptr?"` } func TestApplicationContext_AutoWireBeans(t *testing.T) { @@ -154,7 +154,7 @@ func TestApplicationContext_AutoWireBeans(t *testing.T) { err := runTest(c, func(p gs.Context) {}) assert.Nil(t, err) - assert.Equal(t, len(obj.MapTyType), 4) + assert.Equal(t, len(obj.MapTyType), 1) assert.Equal(t, len(obj.MapByName), 0) assert.Equal(t, len(obj.MapByNam2), 1) fmt.Printf("%+v\n", obj) diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 9ab4e316..49b0c06a 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -238,14 +238,14 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } } -type byBeanName []*gs.BeanDefinition +type byBeanName []beanWrapper func (b byBeanName) Len() int { return len(b) } func (b byBeanName) Less(i, j int) bool { return b[i].GetName() < b[j].GetName() } func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*gs.BeanDefinition, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []beanWrapper, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -287,15 +287,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []*gs.BeanDefinition - if et.Kind() == reflect.Interface && et.NumMethod() == 0 { - beans = c.beans - } else { - beans = c.beansByType[et] - } + var beans []beanWrapper + beans = c.beansByType[et] { - var arr []*gs.BeanDefinition + var arr []beanWrapper for _, b := range beans { if b.GetStatus() == gs.Deleted { continue @@ -308,9 +304,9 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []*gs.BeanDefinition - afterAny []*gs.BeanDefinition - beforeAny []*gs.BeanDefinition + anyBeans []beanWrapper + afterAny []beanWrapper + beforeAny []beanWrapper ) foundAny := false @@ -339,7 +335,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]*gs.BeanDefinition{}, beans[:index]...) + tmpBeans := append([]beanWrapper{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -348,7 +344,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*gs.BeanDefinition, 0, n) + arr := make([]beanWrapper, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) @@ -368,7 +364,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } for _, b := range beans { - if err := c.wireBean(b, stack); err != nil { + if err := c.wireBean(b.Bean(), stack); err != nil { return err } } @@ -404,7 +400,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []*gs.BeanDefinition + var foundBeans []beanWrapper for _, b := range c.beansByType[t] { if b.GetStatus() == gs.Deleted { continue @@ -450,7 +446,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []*gs.BeanDefinition + var primaryBeans []beanWrapper for _, b := range foundBeans { if b.IsPrimary() { @@ -476,7 +472,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result *gs.BeanDefinition + var result beanWrapper if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -484,7 +480,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 确保找到的 bean 已经完成依赖注入。 - err := c.wireBean(result, stack) + err := c.wireBean(result.Bean(), stack) if err != nil { return err } From 53ab5421252cbd625bcae66ebcbc3a5a9fc0407a Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 13:55:57 +0800 Subject: [PATCH 57/75] code refactor --- gs/internal/gs/bean.go | 140 ++++++++++++++++++++++++------------ gs/internal/gs_core/core.go | 62 +++++----------- gs/internal/gs_core/wire.go | 89 +++++++++++++++++------ 3 files changed, 178 insertions(+), 113 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index d7ed60d6..8c095738 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -48,7 +48,6 @@ func GetStatusString(status BeanStatus) string { // beanRegistration bean 注册数据。 type beanRegistration struct { - f Callable // 构造函数 method bool // 是否为成员方法 cond Condition // 判断条件 init interface{} // 初始化函数 @@ -65,13 +64,10 @@ type beanRegistration struct { refreshParam conf.BindParam } -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - r *beanRegistration - - v reflect.Value // 值 - t reflect.Type // 类型 - +type BeanRuntimeMeta struct { + f Callable // 构造函数 + v reflect.Value // 值 + t reflect.Type // 类型 name string // 名称 typeName string // 原始类型的全限定名 primary bool // 是否为主版本 @@ -79,28 +75,87 @@ type BeanDefinition struct { status BeanStatus // 状态 } +func (d *BeanRuntimeMeta) Callable() Callable { + return d.f +} + +// Interface 返回 bean 的真实值。 +func (d *BeanRuntimeMeta) Interface() interface{} { + return d.v.Interface() +} + +func (d *BeanRuntimeMeta) GetName() string { + return d.name +} + +func (d *BeanRuntimeMeta) IsPrimary() bool { + return d.primary +} + +func (d *BeanRuntimeMeta) Type() reflect.Type { + return d.t +} + +func (d *BeanRuntimeMeta) Value() reflect.Value { + return d.v +} + +func (d *BeanRuntimeMeta) GetStatus() BeanStatus { + return d.status +} + +func (d *BeanRuntimeMeta) Match(typeName string, beanName string) bool { + + typeIsSame := false + if typeName == "" || d.typeName == typeName { + typeIsSame = true + } + + nameIsSame := false + if beanName == "" || d.name == beanName { + nameIsSame = true + } + + return typeIsSame && nameIsSame +} + +func (d *BeanRuntimeMeta) String() string { + return "" + //return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) +} + +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + r *beanRegistration + m *BeanRuntimeMeta +} + func (d *BeanDefinition) BeanRegistration() *BeanRegistration { return &BeanRegistration{d} } +func (d *BeanDefinition) RuntimeMeta() *BeanRuntimeMeta { + return d.m +} + func (d *BeanDefinition) GetName() string { - return d.name + return d.m.name } func (d *BeanDefinition) GetTypeName() string { - return d.typeName + return d.m.typeName } func (d *BeanDefinition) GetStatus() BeanStatus { - return d.status + return d.m.status } func (d *BeanDefinition) SetStatus(status BeanStatus) { - d.status = status + d.m.status = status } func (d *BeanDefinition) IsPrimary() bool { - return d.primary + return d.m.primary } func (d *BeanDefinition) IsMethod() bool { @@ -124,7 +179,7 @@ func (d *BeanDefinition) GetDepends() []BeanSelector { } func (d *BeanDefinition) GetExports() []reflect.Type { - return d.exports + return d.m.exports } func (d *BeanDefinition) IsConfiguration() bool { @@ -148,47 +203,47 @@ func (d *BeanDefinition) GetRefreshParam() conf.BindParam { } func (d *BeanDefinition) Callable() Callable { - return d.r.f + return d.m.f } // Type 返回 bean 的类型。 func (d *BeanDefinition) Type() reflect.Type { - return d.t + return d.m.t } // Value 返回 bean 的值。 func (d *BeanDefinition) Value() reflect.Value { - return d.v + return d.m.v } // Interface 返回 bean 的真实值。 func (d *BeanDefinition) Interface() interface{} { - return d.v.Interface() + return d.m.v.Interface() } // ID 返回 bean 的 ID 。 func (d *BeanDefinition) ID() string { - return d.typeName + ":" + d.name + return d.m.typeName + ":" + d.m.name } // BeanName 返回 bean 的名称。 func (d *BeanDefinition) BeanName() string { - return d.name + return d.m.name } // TypeName 返回 bean 的原始类型的全限定名。 func (d *BeanDefinition) TypeName() string { - return d.typeName + return d.m.typeName } // Created 返回是否已创建。 func (d *BeanDefinition) Created() bool { - return d.status >= Created + return d.m.status >= Created } // Wired 返回 bean 是否已经注入。 func (d *BeanDefinition) Wired() bool { - return d.status == Wired + return d.m.status == Wired } func (d *BeanDefinition) File() string { @@ -206,35 +261,24 @@ func (d *BeanDefinition) FileLine() string { // GetClass 返回 bean 的类型描述。 func (d *BeanDefinition) GetClass() string { - if d.r.f == nil { + if d.m.f == nil { return "object bean" } return "constructor bean" } func (d *BeanDefinition) String() string { - return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) + return d.m.String() } // Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 func (d *BeanDefinition) Match(typeName string, beanName string) bool { - - typeIsSame := false - if typeName == "" || d.typeName == typeName { - typeIsSame = true - } - - nameIsSame := false - if beanName == "" || d.name == beanName { - nameIsSame = true - } - - return typeIsSame && nameIsSame + return d.m.Match(typeName, beanName) } // Name 设置 bean 的名称。 func (d *BeanDefinition) Name(name string) *BeanDefinition { - d.name = name + d.m.name = name return d } @@ -257,7 +301,7 @@ func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition { // Primary 设置 bean 为主版本。 func (d *BeanDefinition) Primary() *BeanDefinition { - d.primary = true + d.m.primary = true return d } @@ -305,7 +349,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { panic(errors.New("only interface type can be exported")) } exported := false - for _, export := range d.exports { + for _, export := range d.m.exports { if t == export { exported = true break @@ -314,7 +358,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { if exported { continue } - d.exports = append(d.exports, t) + d.m.exports = append(d.m.exports, t) } return d } @@ -348,16 +392,18 @@ func (d *BeanDefinition) SimplifyMemory() { // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ - t: t, - v: v, - name: name, - typeName: util.TypeName(t), - status: Default, r: &beanRegistration{ - f: f, method: method, file: file, line: line, }, + m: &BeanRuntimeMeta{ + f: f, + t: t, + v: v, + name: name, + typeName: util.TypeName(t), + status: Default, + }, } } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 5ff275c7..11860ee5 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -62,42 +62,6 @@ type ContextAware struct { GSContext gs.Context `autowire:""` } -type beanWrapper struct { - b *gs.BeanDefinition -} - -func (d beanWrapper) Bean() *gs.BeanDefinition { - return d.b -} - -func (d beanWrapper) GetName() string { - return d.b.GetName() -} - -func (d beanWrapper) IsPrimary() bool { - return d.b.IsPrimary() -} - -func (d beanWrapper) Type() reflect.Type { - return d.b.Type() -} - -func (d beanWrapper) Value() reflect.Value { - return d.b.Value() -} - -func (d beanWrapper) GetStatus() gs.BeanStatus { - return d.b.GetStatus() -} - -func (d beanWrapper) Match(typeName string, beanName string) bool { - return d.b.Match(typeName, beanName) -} - -func (d beanWrapper) String() string { - return d.b.String() -} - // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -107,8 +71,8 @@ func (d beanWrapper) String() string { // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { beans []*gs.BeanDefinition - beansByName map[string][]beanWrapper - beansByType map[reflect.Type][]beanWrapper + beansByName map[string][]*gs.BeanRuntimeMeta + beansByType map[reflect.Type][]*gs.BeanRuntimeMeta groupFuncs []GroupFunc p *gs_dync.Properties ctx context.Context @@ -126,8 +90,8 @@ func New() gs.Container { ctx: ctx, cancel: cancel, p: gs_dync.New(), - beansByName: make(map[string][]beanWrapper), - beansByType: make(map[reflect.Type][]beanWrapper), + beansByName: make(map[string][]*gs.BeanRuntimeMeta), + beansByType: make(map[reflect.Type][]*gs.BeanRuntimeMeta), } c.Object(c).Export((*gs.Context)(nil)) return c @@ -263,7 +227,7 @@ func (c *Container) Refresh() (err error) { sort.Strings(keys) for _, s := range keys { b := beansById[s] - if err = c.wireBean(b, stack); err != nil { + if err = c.wireBeanInRefreshing(b, stack); err != nil { return err } } @@ -393,11 +357,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti func (c *Container) registerBean(b *gs.BeanDefinition) { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), b.Type(), b.FileLine()) - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], beanWrapper{b}) - c.beansByType[b.Type()] = append(c.beansByType[b.Type()], beanWrapper{b}) + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.RuntimeMeta()) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.RuntimeMeta()) for _, t := range b.GetExports() { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), t, b.FileLine()) - c.beansByType[t] = append(c.beansByType[t], beanWrapper{b}) + c.beansByType[t] = append(c.beansByType[t], b.RuntimeMeta()) } } @@ -556,7 +520,15 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} }() b := NewBean(objOrCtor, ctorArgs...) - err := c.wireBean(b, stack) + var err error + switch c.state { + case Refreshing: + err = c.wireBeanInRefreshing(b, stack) + case Refreshed: + err = c.wireBeanAfterRefreshed(b.RuntimeMeta(), stack) + default: + err = errors.New("state is error for wiring") + } if err != nil { return nil, err } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 49b0c06a..181dc57d 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -238,14 +238,14 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } } -type byBeanName []beanWrapper +type byBeanName []*gs.BeanRuntimeMeta func (b byBeanName) Len() int { return len(b) } func (b byBeanName) Less(i, j int) bool { return b[i].GetName() < b[j].GetName() } func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []beanWrapper, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []*gs.BeanRuntimeMeta, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -287,11 +287,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []beanWrapper + var beans []*gs.BeanRuntimeMeta beans = c.beansByType[et] { - var arr []beanWrapper + var arr []*gs.BeanRuntimeMeta for _, b := range beans { if b.GetStatus() == gs.Deleted { continue @@ -304,9 +304,9 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []beanWrapper - afterAny []beanWrapper - beforeAny []beanWrapper + anyBeans []*gs.BeanRuntimeMeta + afterAny []*gs.BeanRuntimeMeta + beforeAny []*gs.BeanRuntimeMeta ) foundAny := false @@ -335,7 +335,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]beanWrapper{}, beans[:index]...) + tmpBeans := append([]*gs.BeanRuntimeMeta{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -344,7 +344,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]beanWrapper, 0, n) + arr := make([]*gs.BeanRuntimeMeta, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) @@ -364,8 +364,17 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } for _, b := range beans { - if err := c.wireBean(b.Bean(), stack); err != nil { - return err + switch c.state { + case Refreshing: + if err := c.wireBeanInRefreshing(b.Bean(), stack); err != nil { + return err + } + case Refreshed: + if err := c.wireBeanAfterRefreshed(b, stack); err != nil { + return err + } + default: + return fmt.Errorf("state is error for wiring") } } @@ -400,7 +409,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []beanWrapper + var foundBeans []*gs.BeanRuntimeMeta for _, b := range c.beansByType[t] { if b.GetStatus() == gs.Deleted { continue @@ -446,7 +455,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []beanWrapper + var primaryBeans []*gs.BeanRuntimeMeta for _, b := range foundBeans { if b.IsPrimary() { @@ -472,7 +481,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result beanWrapper + var result *gs.BeanRuntimeMeta if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -480,9 +489,17 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 确保找到的 bean 已经完成依赖注入。 - err := c.wireBean(result.Bean(), stack) - if err != nil { - return err + switch c.state { + case Refreshing: + if err := c.wireBeanInRefreshing(result.Bean(), stack); err != nil { + return err + } + case Refreshed: + if err := c.wireBeanAfterRefreshed(result, stack); err != nil { + return err + } + default: + return fmt.Errorf("state is error for wiring") } v.Set(result.Value()) @@ -492,7 +509,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { +func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStack) error { if b.GetStatus() == gs.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) @@ -544,7 +561,7 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { return err } for _, d := range beans { - err = c.wireBean(d, stack) + err = c.wireBeanInRefreshing(d, stack) if err != nil { return err } @@ -601,6 +618,29 @@ func (c *Container) wireBean(b *gs.BeanDefinition, stack *wiringStack) error { return nil } +func (c *Container) wireBeanAfterRefreshed(b *gs.BeanRuntimeMeta, stack *wiringStack) error { + + v, err := c.getBeanValue(b, stack) + if err != nil { + return err + } + + t := v.Type() + err = c.wireBeanValue(v, t, stack) + if err != nil { + return err + } + + // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 + if f, ok := b.Interface().(BeanInit); ok { + if err = f.OnInit(c); err != nil { + return err + } + } + + return nil +} + type argContext struct { c *Container stack *wiringStack @@ -618,8 +658,15 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { return a.c.wireByTag(v, tag, a.stack) } +type SimpleBeanDefinition interface { + Callable() gs.Callable + Value() reflect.Value + Type() reflect.Type + String() string +} + // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *Container) getBeanValue(b SimpleBeanDefinition, stack *wiringStack) (reflect.Value, error) { if b.Callable() == nil { return b.Value(), nil @@ -645,7 +692,7 @@ func (c *Container) getBeanValue(b *gs.BeanDefinition, stack *wiringStack) (refl } if b.Value().IsNil() { - return reflect.Value{}, fmt.Errorf("%s:%q return nil", b.GetClass(), b.FileLine()) + return reflect.Value{}, fmt.Errorf("%s return nil", b.String()) // b.GetClass(), b.FileLine()) } v := b.Value() From 66920cbf05ad5b0880b6f35a678ae38df1676092 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 19:49:02 +0800 Subject: [PATCH 58/75] code refactor --- gs/internal/gs/bean.go | 22 +++++++++---------- gs/internal/gs_core/core.go | 44 +++++++++++++++++++++++++++---------- gs/internal/gs_core/wire.go | 34 ++++++++++++++-------------- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 8c095738..10e67c78 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -53,6 +53,7 @@ type beanRegistration struct { init interface{} // 初始化函数 destroy interface{} // 销毁函数 depends []BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 file string // 注册点所在文件 line int // 注册点所在行数 @@ -65,14 +66,13 @@ type beanRegistration struct { } type BeanRuntimeMeta struct { - f Callable // 构造函数 - v reflect.Value // 值 - t reflect.Type // 类型 - name string // 名称 - typeName string // 原始类型的全限定名 - primary bool // 是否为主版本 - exports []reflect.Type // 导出的接口 - status BeanStatus // 状态 + f Callable // 构造函数 + v reflect.Value // 值 + t reflect.Type // 类型 + name string // 名称 + typeName string // 原始类型的全限定名 + primary bool // 是否为主版本 + status BeanStatus // 状态 } func (d *BeanRuntimeMeta) Callable() Callable { @@ -179,7 +179,7 @@ func (d *BeanDefinition) GetDepends() []BeanSelector { } func (d *BeanDefinition) GetExports() []reflect.Type { - return d.m.exports + return d.r.exports } func (d *BeanDefinition) IsConfiguration() bool { @@ -349,7 +349,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { panic(errors.New("only interface type can be exported")) } exported := false - for _, export := range d.m.exports { + for _, export := range d.r.exports { if t == export { exported = true break @@ -358,7 +358,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { if exported { continue } - d.m.exports = append(d.m.exports, t) + d.r.exports = append(d.r.exports, t) } return d } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 11860ee5..0a279c37 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -62,6 +62,18 @@ type ContextAware struct { GSContext gs.Context `autowire:""` } +type SimpleBean interface { + Callable() gs.Callable + GetName() string + GetStatus() gs.BeanStatus + Interface() interface{} + IsPrimary() bool + Match(typeName string, beanName string) bool + String() string + Type() reflect.Type + Value() reflect.Value +} + // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion // of Control Containers and the Dependency Injection pattern >> 一文中 // 提及的依赖注入的概念。但原文的依赖注入仅仅是指对象之间的依赖关系处理,而有些 IoC @@ -71,8 +83,8 @@ type ContextAware struct { // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { beans []*gs.BeanDefinition - beansByName map[string][]*gs.BeanRuntimeMeta - beansByType map[reflect.Type][]*gs.BeanRuntimeMeta + beansByName map[string][]SimpleBean + beansByType map[reflect.Type][]SimpleBean groupFuncs []GroupFunc p *gs_dync.Properties ctx context.Context @@ -90,8 +102,8 @@ func New() gs.Container { ctx: ctx, cancel: cancel, p: gs_dync.New(), - beansByName: make(map[string][]*gs.BeanRuntimeMeta), - beansByType: make(map[reflect.Type][]*gs.BeanRuntimeMeta), + beansByName: make(map[string][]SimpleBean), + beansByType: make(map[reflect.Type][]SimpleBean), } c.Object(c).Export((*gs.Context)(nil)) return c @@ -250,6 +262,19 @@ func (c *Container) Refresh() (err error) { return errors.New("remove the dependency cycle between beans") } + c.beansByName = make(map[string][]SimpleBean) + c.beansByType = make(map[reflect.Type][]SimpleBean) + for _, b := range c.beans { + if b.GetStatus() == gs.Deleted { + continue + } + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.RuntimeMeta()) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.RuntimeMeta()) + for _, t := range b.GetExports() { + c.beansByType[t] = append(c.beansByType[t], b.RuntimeMeta()) + } + } + c.destroyers = stack.sortDestroyers() c.state = Refreshed @@ -357,11 +382,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti func (c *Container) registerBean(b *gs.BeanDefinition) { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), b.Type(), b.FileLine()) - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.RuntimeMeta()) - c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.RuntimeMeta()) + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) for _, t := range b.GetExports() { syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), t, b.FileLine()) - c.beansByType[t] = append(c.beansByType[t], b.RuntimeMeta()) + c.beansByType[t] = append(c.beansByType[t], b) } } @@ -458,11 +483,6 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) if b.Type() == t { return true } - for _, typ := range b.GetExports() { - if typ == t { - return true - } - } return false }) } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 181dc57d..b102357b 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -238,14 +238,14 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } } -type byBeanName []*gs.BeanRuntimeMeta +type byBeanName []SimpleBean func (b byBeanName) Len() int { return len(b) } func (b byBeanName) Less(i, j int) bool { return b[i].GetName() < b[j].GetName() } func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []*gs.BeanRuntimeMeta, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []SimpleBean, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -287,11 +287,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []*gs.BeanRuntimeMeta + var beans []SimpleBean beans = c.beansByType[et] { - var arr []*gs.BeanRuntimeMeta + var arr []SimpleBean for _, b := range beans { if b.GetStatus() == gs.Deleted { continue @@ -304,9 +304,9 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []*gs.BeanRuntimeMeta - afterAny []*gs.BeanRuntimeMeta - beforeAny []*gs.BeanRuntimeMeta + anyBeans []SimpleBean + afterAny []SimpleBean + beforeAny []SimpleBean ) foundAny := false @@ -335,7 +335,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]*gs.BeanRuntimeMeta{}, beans[:index]...) + tmpBeans := append([]SimpleBean{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -344,7 +344,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]*gs.BeanRuntimeMeta, 0, n) + arr := make([]SimpleBean, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) @@ -366,11 +366,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, for _, b := range beans { switch c.state { case Refreshing: - if err := c.wireBeanInRefreshing(b.Bean(), stack); err != nil { + if err := c.wireBeanInRefreshing(b.(*gs.BeanDefinition), stack); err != nil { return err } case Refreshed: - if err := c.wireBeanAfterRefreshed(b, stack); err != nil { + if err := c.wireBeanAfterRefreshed(b.(*gs.BeanRuntimeMeta), stack); err != nil { return err } default: @@ -409,7 +409,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []*gs.BeanRuntimeMeta + var foundBeans []SimpleBean for _, b := range c.beansByType[t] { if b.GetStatus() == gs.Deleted { continue @@ -455,7 +455,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []*gs.BeanRuntimeMeta + var primaryBeans []SimpleBean for _, b := range foundBeans { if b.IsPrimary() { @@ -481,7 +481,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result *gs.BeanRuntimeMeta + var result SimpleBean if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -491,11 +491,11 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 确保找到的 bean 已经完成依赖注入。 switch c.state { case Refreshing: - if err := c.wireBeanInRefreshing(result.Bean(), stack); err != nil { + if err := c.wireBeanInRefreshing(result.(*gs.BeanDefinition), stack); err != nil { return err } case Refreshed: - if err := c.wireBeanAfterRefreshed(result, stack); err != nil { + if err := c.wireBeanAfterRefreshed(result.(*gs.BeanRuntimeMeta), stack); err != nil { return err } default: @@ -618,7 +618,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac return nil } -func (c *Container) wireBeanAfterRefreshed(b *gs.BeanRuntimeMeta, stack *wiringStack) error { +func (c *Container) wireBeanAfterRefreshed(b SimpleBean, stack *wiringStack) error { v, err := c.getBeanValue(b, stack) if err != nil { From 37a1f76f93097c2bc0c2ad9a280b89280034085e Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 20:12:10 +0800 Subject: [PATCH 59/75] code refactor --- gs/internal/gs/bean.go | 129 +++++++++++------------------------- gs/internal/gs_core/core.go | 43 ++++++------ 2 files changed, 61 insertions(+), 111 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 10e67c78..e56c028d 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -126,182 +126,138 @@ func (d *BeanRuntimeMeta) String() string { // BeanDefinition bean 元数据。 type BeanDefinition struct { - r *beanRegistration - m *BeanRuntimeMeta + *beanRegistration + *BeanRuntimeMeta } func (d *BeanDefinition) BeanRegistration() *BeanRegistration { return &BeanRegistration{d} } -func (d *BeanDefinition) RuntimeMeta() *BeanRuntimeMeta { - return d.m -} - -func (d *BeanDefinition) GetName() string { - return d.m.name -} - func (d *BeanDefinition) GetTypeName() string { - return d.m.typeName -} - -func (d *BeanDefinition) GetStatus() BeanStatus { - return d.m.status + return d.typeName } func (d *BeanDefinition) SetStatus(status BeanStatus) { - d.m.status = status -} - -func (d *BeanDefinition) IsPrimary() bool { - return d.m.primary + d.status = status } func (d *BeanDefinition) IsMethod() bool { - return d.r.method + return d.method } func (d *BeanDefinition) GetCond() Condition { - return d.r.cond + return d.cond } func (d *BeanDefinition) GetInit() interface{} { - return d.r.init + return d.init } func (d *BeanDefinition) GetDestroy() interface{} { - return d.r.destroy + return d.destroy } func (d *BeanDefinition) GetDepends() []BeanSelector { - return d.r.depends + return d.depends } func (d *BeanDefinition) GetExports() []reflect.Type { - return d.r.exports + return d.exports } func (d *BeanDefinition) IsConfiguration() bool { - return d.r.configuration + return d.configuration } func (d *BeanDefinition) GetIncludeMethod() []string { - return d.r.includeMethod + return d.includeMethod } func (d *BeanDefinition) GetExcludeMethod() []string { - return d.r.excludeMethod + return d.excludeMethod } func (d *BeanDefinition) IsRefreshEnable() bool { - return d.r.enableRefresh + return d.enableRefresh } func (d *BeanDefinition) GetRefreshParam() conf.BindParam { - return d.r.refreshParam -} - -func (d *BeanDefinition) Callable() Callable { - return d.m.f -} - -// Type 返回 bean 的类型。 -func (d *BeanDefinition) Type() reflect.Type { - return d.m.t -} - -// Value 返回 bean 的值。 -func (d *BeanDefinition) Value() reflect.Value { - return d.m.v -} - -// Interface 返回 bean 的真实值。 -func (d *BeanDefinition) Interface() interface{} { - return d.m.v.Interface() + return d.refreshParam } // ID 返回 bean 的 ID 。 func (d *BeanDefinition) ID() string { - return d.m.typeName + ":" + d.m.name + return d.typeName + ":" + d.name } // BeanName 返回 bean 的名称。 func (d *BeanDefinition) BeanName() string { - return d.m.name + return d.name } // TypeName 返回 bean 的原始类型的全限定名。 func (d *BeanDefinition) TypeName() string { - return d.m.typeName + return d.typeName } // Created 返回是否已创建。 func (d *BeanDefinition) Created() bool { - return d.m.status >= Created + return d.status >= Created } // Wired 返回 bean 是否已经注入。 func (d *BeanDefinition) Wired() bool { - return d.m.status == Wired + return d.status == Wired } func (d *BeanDefinition) File() string { - return d.r.file + return d.file } func (d *BeanDefinition) Line() int { - return d.r.line + return d.line } // FileLine 返回 bean 的注册点。 func (d *BeanDefinition) FileLine() string { - return fmt.Sprintf("%s:%d", d.r.file, d.r.line) + return fmt.Sprintf("%s:%d", d.file, d.line) } // GetClass 返回 bean 的类型描述。 func (d *BeanDefinition) GetClass() string { - if d.m.f == nil { + if d.f == nil { return "object bean" } return "constructor bean" } -func (d *BeanDefinition) String() string { - return d.m.String() -} - -// Match 测试 bean 的类型全限定名和 bean 的名称是否都匹配。 -func (d *BeanDefinition) Match(typeName string, beanName string) bool { - return d.m.Match(typeName, beanName) -} - // Name 设置 bean 的名称。 func (d *BeanDefinition) Name(name string) *BeanDefinition { - d.m.name = name + d.name = name return d } func (d *BeanDefinition) Caller(skip int) *BeanDefinition { - _, d.r.file, d.r.line, _ = runtime.Caller(skip) + _, d.file, d.line, _ = runtime.Caller(skip) return d } // On 设置 bean 的 Condition。 func (d *BeanDefinition) On(cond Condition) *BeanDefinition { - d.r.cond = cond + d.cond = cond return d } // DependsOn 设置 bean 的间接依赖项。 func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition { - d.r.depends = append(d.r.depends, selectors...) + d.depends = append(d.depends, selectors...) return d } // Primary 设置 bean 为主版本。 func (d *BeanDefinition) Primary() *BeanDefinition { - d.m.primary = true + d.primary = true return d } @@ -320,7 +276,7 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { // Init 设置 bean 的初始化函数。 func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.r.init = fn + d.init = fn return d } panic(errors.New("init should be func(bean) or func(bean)error")) @@ -329,7 +285,7 @@ func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { // Destroy 设置 bean 的销毁函数。 func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { - d.r.destroy = fn + d.destroy = fn return d } panic(errors.New("destroy should be func(bean) or func(bean)error")) @@ -349,7 +305,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { panic(errors.New("only interface type can be exported")) } exported := false - for _, export := range d.r.exports { + for _, export := range d.exports { if t == export { exported = true break @@ -358,16 +314,16 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { if exported { continue } - d.r.exports = append(d.r.exports, t) + d.exports = append(d.exports, t) } return d } // Configuration 设置 bean 为配置类。 func (d *BeanDefinition) Configuration(includes []string, excludes []string) *BeanDefinition { - d.r.configuration = true - d.r.includeMethod = includes - d.r.excludeMethod = excludes + d.configuration = true + d.includeMethod = includes + d.excludeMethod = excludes return d } @@ -376,28 +332,23 @@ func (d *BeanDefinition) EnableRefresh(tag string) *BeanDefinition { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } - d.r.enableRefresh = true - err := d.r.refreshParam.BindTag(tag, "") + d.enableRefresh = true + err := d.refreshParam.BindTag(tag, "") if err != nil { panic(err) } return d } -// SimplifyMemory 精简内存 -func (d *BeanDefinition) SimplifyMemory() { - d.r = nil -} - // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ - r: &beanRegistration{ + beanRegistration: &beanRegistration{ method: method, file: file, line: line, }, - m: &BeanRuntimeMeta{ + BeanRuntimeMeta: &BeanRuntimeMeta{ f: f, t: t, v: v, diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 0a279c37..47dc457a 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -178,6 +178,7 @@ func (c *Container) Refresh() (err error) { } c.beans = append(c.beans, beans...) } + c.groupFuncs = nil // 处理 configuration 逻辑 for _, bd := range c.beans { @@ -262,18 +263,27 @@ func (c *Container) Refresh() (err error) { return errors.New("remove the dependency cycle between beans") } - c.beansByName = make(map[string][]SimpleBean) - c.beansByType = make(map[reflect.Type][]SimpleBean) - for _, b := range c.beans { - if b.GetStatus() == gs.Deleted { - continue + if c.ContextAware { // 保留核心数据 + c.beansByName = make(map[string][]SimpleBean) + c.beansByType = make(map[reflect.Type][]SimpleBean) + for _, b := range c.beans { + if b.GetStatus() == gs.Deleted { + continue + } + c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.BeanRuntimeMeta) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.BeanRuntimeMeta) + for _, t := range b.GetExports() { + c.beansByType[t] = append(c.beansByType[t], b.BeanRuntimeMeta) + } } - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.RuntimeMeta()) - c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.RuntimeMeta()) - for _, t := range b.GetExports() { - c.beansByType[t] = append(c.beansByType[t], b.RuntimeMeta()) + } else { // 清空全部数据 + if c.p.ObjectsCount() == 0 { + c.p = nil } + c.beansByName = nil + c.beansByType = nil } + c.beans = nil c.destroyers = stack.sortDestroyers() c.state = Refreshed @@ -286,18 +296,7 @@ func (c *Container) Refresh() (err error) { // SimplifyMemory 清理运行时不需要的空间。 func (c *Container) SimplifyMemory() { - for _, bd := range c.beans { - bd.SimplifyMemory() - } - c.beans = nil - c.groupFuncs = nil - if !c.ContextAware { - if c.p.ObjectsCount() == 0 { - c.p = nil - } - c.beansByName = nil - c.beansByType = nil - } + } func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefinition, error) { @@ -545,7 +544,7 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} case Refreshing: err = c.wireBeanInRefreshing(b, stack) case Refreshed: - err = c.wireBeanAfterRefreshed(b.RuntimeMeta(), stack) + err = c.wireBeanAfterRefreshed(b, stack) default: err = errors.New("state is error for wiring") } From bcd2f977d7774d833793075fb7421b7fa829fd98 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 20:39:31 +0800 Subject: [PATCH 60/75] code refactor --- gs/gs.go | 2 ++ gs/internal/gs/bean.go | 31 +++++++++++++++++++------------ gs/internal/gs/gs.go | 4 ++++ gs/internal/gs_core/core.go | 16 +++++----------- gs/internal/gs_core/wire.go | 19 ++++++------------- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 6500ca06..4bea47ac 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -39,6 +39,8 @@ const ( type ( Arg = gs.Arg BeanDefinition = gs.BeanDefinition + BeanInit = gs.BeanInit + BeanDestroy = gs.BeanDestroy BeanRegistration = gs.BeanRegistration BeanSelector = gs.BeanSelector CondContext = gs.CondContext diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index e56c028d..cc399877 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -46,6 +46,14 @@ func GetStatusString(status BeanStatus) string { } } +type BeanInit interface { + OnInit(ctx Context) error +} + +type BeanDestroy interface { + OnDestroy() +} + // beanRegistration bean 注册数据。 type beanRegistration struct { method bool // 是否为成员方法 @@ -119,9 +127,16 @@ func (d *BeanRuntimeMeta) Match(typeName string, beanName string) bool { return typeIsSame && nameIsSame } +// GetClass 返回 bean 的类型描述。 +func (d *BeanRuntimeMeta) GetClass() string { + if d.f == nil { + return "object bean" + } + return "constructor bean" +} + func (d *BeanRuntimeMeta) String() string { - return "" - //return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) + return fmt.Sprintf("%s name:%q", d.GetClass(), d.name) } // BeanDefinition bean 元数据。 @@ -130,10 +145,6 @@ type BeanDefinition struct { *BeanRuntimeMeta } -func (d *BeanDefinition) BeanRegistration() *BeanRegistration { - return &BeanRegistration{d} -} - func (d *BeanDefinition) GetTypeName() string { return d.typeName } @@ -224,12 +235,8 @@ func (d *BeanDefinition) FileLine() string { return fmt.Sprintf("%s:%d", d.file, d.line) } -// GetClass 返回 bean 的类型描述。 -func (d *BeanDefinition) GetClass() string { - if d.f == nil { - return "object bean" - } - return "constructor bean" +func (d *BeanDefinition) String() string { + return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) } // Name 设置 bean 的名称。 diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 791dbd85..c4cb2cf3 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -70,6 +70,10 @@ type BeanRegistration struct { b *BeanDefinition } +func NewBeanRegistration(d *BeanDefinition) *BeanRegistration { + return &BeanRegistration{d} +} + func (d *BeanRegistration) Name(name string) *BeanRegistration { d.b.Name(name) return d diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 47dc457a..b5a315ae 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -36,14 +36,6 @@ import ( "github.com/go-spring/spring-core/util" ) -type BeanInit interface { - OnInit(ctx gs.Context) error -} - -type BeanDestroy interface { - OnDestroy() -} - type refreshState int const ( @@ -111,12 +103,14 @@ func New() gs.Container { // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Object(i interface{}) *gs.BeanRegistration { - return c.Accept(NewBean(reflect.ValueOf(i))) + b := NewBean(reflect.ValueOf(i)) + return c.Accept(b) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { - return c.Accept(NewBean(ctor, args...)) + b := NewBean(ctor, args...) + return c.Accept(b) } func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanRegistration { @@ -124,7 +118,7 @@ func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanRegistration { panic(errors.New("should call before Refresh")) } c.beans = append(c.beans, b) - return b.BeanRegistration() + return gs.NewBeanRegistration(b) } func (c *Container) Group(fn GroupFunc) { diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index b102357b..d4d3ec80 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -114,7 +114,7 @@ func (s *wiringStack) sortDestroyers() []func() { destroy := func(v reflect.Value, fn interface{}) func() { return func() { if fn == nil { - v.Interface().(BeanDestroy).OnDestroy() + v.Interface().(gs.BeanDestroy).OnDestroy() } else { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) @@ -495,7 +495,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return err } case Refreshed: - if err := c.wireBeanAfterRefreshed(result.(*gs.BeanRuntimeMeta), stack); err != nil { + if err := c.wireBeanAfterRefreshed(result, stack); err != nil { return err } default: @@ -529,7 +529,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac }() // 记录注入路径上的销毁函数及其执行的先后顺序。 - if _, ok := b.Interface().(BeanDestroy); ok || b.GetDestroy() != nil { + if _, ok := b.Interface().(gs.BeanDestroy); ok || b.GetDestroy() != nil { haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { @@ -597,7 +597,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac } // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 - if f, ok := b.Interface().(BeanInit); ok { + if f, ok := b.Interface().(gs.BeanInit); ok { if err = f.OnInit(c); err != nil { return err } @@ -632,7 +632,7 @@ func (c *Container) wireBeanAfterRefreshed(b SimpleBean, stack *wiringStack) err } // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 - if f, ok := b.Interface().(BeanInit); ok { + if f, ok := b.Interface().(gs.BeanInit); ok { if err = f.OnInit(c); err != nil { return err } @@ -658,15 +658,8 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { return a.c.wireByTag(v, tag, a.stack) } -type SimpleBeanDefinition interface { - Callable() gs.Callable - Value() reflect.Value - Type() reflect.Type - String() string -} - // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *Container) getBeanValue(b SimpleBeanDefinition, stack *wiringStack) (reflect.Value, error) { +func (c *Container) getBeanValue(b SimpleBean, stack *wiringStack) (reflect.Value, error) { if b.Callable() == nil { return b.Value(), nil From 251df35e310540aebbac90f38a41584585ecd9d0 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Thu, 9 Jan 2025 20:55:53 +0800 Subject: [PATCH 61/75] force autowire is nullable --- gs/gs.go | 15 ++++++++++++--- gs/internal/gs_core/core.go | 10 ++++------ gs/internal/gs_core/wire.go | 5 +++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 4bea47ac..4c3ceed9 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -317,7 +317,16 @@ func printBanner() { /********************************** utility **********************************/ -func AllowCircularReferences(allow bool) { - err := sysconf.Set("spring.allow-circular-references", allow) - _ = err // ignore error +func AllowCircularReferences(enable bool) { + err := sysconf.Set("spring.allow-circular-references", enable) + if err != nil { + panic(err) + } +} + +func ForceAutowireIsNullable(enable bool) { + err := sysconf.Set("spring.force-autowire-is-nullable", enable) + if err != nil { + panic(err) + } } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index b5a315ae..75d76e30 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -85,6 +85,9 @@ type Container struct { state refreshState destroyers []func() ContextAware bool + + AllowCircularReferences bool `value:"${spring.allow-circular-references:=false}"` + ForceAutowireIsNullable bool `value:"${spring.force-autowire-is-nullable:=false}"` } // New 创建 IoC 容器。 @@ -240,12 +243,7 @@ func (c *Container) Refresh() (err error) { } } - var AllowCircularReferences bool - err = c.Bind(&AllowCircularReferences, conf.Tag("${spring.allow-circular-references:=false}")) - if err != nil { - return err - } - if AllowCircularReferences { + if c.AllowCircularReferences { // 处理被标记为延迟注入的那些 bean 字段 for _, f := range stack.lazyFields { tag := strings.TrimSuffix(f.tag, ",lazy") diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index d4d3ec80..fd691f04 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -224,6 +224,11 @@ func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { } func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, stack *wiringStack) error { + if c.ForceAutowireIsNullable { + for i := 0; i < len(tags); i++ { + tags[i].nullable = true + } + } switch v.Kind() { case reflect.Map, reflect.Slice, reflect.Array: return c.collectBeans(v, tags, nullable, stack) From 87ffd4a4c607e2b137138df89a072260dfa805e4 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 10 Jan 2025 08:37:38 +0800 Subject: [PATCH 62/75] code refactor --- gs/internal/gs/bean.go | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index cc399877..7744c7ab 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -56,6 +56,7 @@ type BeanDestroy interface { // beanRegistration bean 注册数据。 type beanRegistration struct { + f Callable // 构造函数 method bool // 是否为成员方法 cond Condition // 判断条件 init interface{} // 初始化函数 @@ -64,6 +65,7 @@ type beanRegistration struct { exports []reflect.Type // 导出的接口 file string // 注册点所在文件 line int // 注册点所在行数 + status BeanStatus // 状态 configuration bool // 是否扫描成员方法 includeMethod []string // 包含哪些成员方法 @@ -74,17 +76,15 @@ type beanRegistration struct { } type BeanRuntimeMeta struct { - f Callable // 构造函数 v reflect.Value // 值 t reflect.Type // 类型 name string // 名称 typeName string // 原始类型的全限定名 primary bool // 是否为主版本 - status BeanStatus // 状态 } func (d *BeanRuntimeMeta) Callable() Callable { - return d.f + return nil } // Interface 返回 bean 的真实值。 @@ -109,7 +109,7 @@ func (d *BeanRuntimeMeta) Value() reflect.Value { } func (d *BeanRuntimeMeta) GetStatus() BeanStatus { - return d.status + return Wired } func (d *BeanRuntimeMeta) Match(typeName string, beanName string) bool { @@ -127,16 +127,8 @@ func (d *BeanRuntimeMeta) Match(typeName string, beanName string) bool { return typeIsSame && nameIsSame } -// GetClass 返回 bean 的类型描述。 -func (d *BeanRuntimeMeta) GetClass() string { - if d.f == nil { - return "object bean" - } - return "constructor bean" -} - func (d *BeanRuntimeMeta) String() string { - return fmt.Sprintf("%s name:%q", d.GetClass(), d.name) + return d.name } // BeanDefinition bean 元数据。 @@ -145,10 +137,18 @@ type BeanDefinition struct { *BeanRuntimeMeta } +func (d *BeanDefinition) Callable() Callable { + return d.f +} + func (d *BeanDefinition) GetTypeName() string { return d.typeName } +func (d *BeanDefinition) GetStatus() BeanStatus { + return d.status +} + func (d *BeanDefinition) SetStatus(status BeanStatus) { d.status = status } @@ -235,6 +235,14 @@ func (d *BeanDefinition) FileLine() string { return fmt.Sprintf("%s:%d", d.file, d.line) } +// GetClass 返回 bean 的类型描述。 +func (d *BeanDefinition) GetClass() string { + if d.f == nil { + return "object bean" + } + return "constructor bean" +} + func (d *BeanDefinition) String() string { return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) } @@ -351,17 +359,17 @@ func (d *BeanDefinition) EnableRefresh(tag string) *BeanDefinition { func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ beanRegistration: &beanRegistration{ + f: f, method: method, file: file, line: line, + status: Default, }, BeanRuntimeMeta: &BeanRuntimeMeta{ - f: f, t: t, v: v, name: name, typeName: util.TypeName(t), - status: Default, }, } } From 5ec4e7c80bb8192d77f9bafb11b65ac08355cad4 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 10 Jan 2025 11:37:48 +0800 Subject: [PATCH 63/75] code refactor --- gs/gs.go | 42 +++++++------ gs/internal/gs/bean.go | 20 +++--- gs/internal/gs/gs.go | 101 +++++++++++++++++-------------- gs/internal/gs_app/boot.go | 10 +-- gs/internal/gs_core/bean.go | 5 +- gs/internal/gs_core/bean_test.go | 20 +++--- gs/internal/gs_core/core.go | 37 +++++------ gs/internal/gs_core/core_test.go | 28 ++++----- 8 files changed, 139 insertions(+), 124 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 4c3ceed9..069c3ef9 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -37,19 +37,21 @@ const ( ) type ( - Arg = gs.Arg - BeanDefinition = gs.BeanDefinition - BeanInit = gs.BeanInit - BeanDestroy = gs.BeanDestroy - BeanRegistration = gs.BeanRegistration - BeanSelector = gs.BeanSelector - CondContext = gs.CondContext - Condition = gs.Condition - Context = gs.Context - ContextAware = gs_core.ContextAware - Dync[T any] = gs_dync.Value[T] - Runner = gs_app.Runner - Server = gs_app.Server + Arg = gs.Arg + BeanDefinition = gs.BeanDefinition + BeanInit = gs.BeanInit + BeanDestroy = gs.BeanDestroy + RegisteredBean = gs.RegisteredBean + ToBeRegisteredBean = gs.ToBeRegisteredBean + BeanSelector = gs.BeanSelector + CondContext = gs.CondContext + Condition = gs.Condition + Properties = gs.Properties + Context = gs.Context + ContextAware = gs_core.ContextAware + Dync[T any] = gs_dync.Value[T] + Runner = gs_app.Runner + Server = gs_app.Server ) /************************************ arg ***********************************/ @@ -230,40 +232,40 @@ func AppConfig() *gs_conf.AppConfig { return app.P } -func AppRunner(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { +func AppRunner(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { b := gs_core.NewBean(objOrCtor, ctorArgs...) b.Export((*gs_app.Runner)(nil)) return app.C.Accept(b) } -func AppServer(objOrCtor interface{}, ctorArgs ...gs.Arg) *BeanRegistration { +func AppServer(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { b := gs_core.NewBean(objOrCtor, ctorArgs...) b.Export((*gs_app.Server)(nil)) return app.C.Accept(b) } // Object 参考 Container.Object 的解释。 -func Object(i interface{}) *BeanRegistration { +func Object(i interface{}) *RegisteredBean { b := gs_core.NewBean(reflect.ValueOf(i)) return app.C.Accept(b) } // Provide 参考 Container.Provide 的解释。 -func Provide(ctor interface{}, args ...Arg) *BeanRegistration { +func Provide(ctor interface{}, args ...Arg) *RegisteredBean { b := gs_core.NewBean(ctor, args...) return app.C.Accept(b) } // Accept 参考 Container.Accept 的解释。 -func Accept(b *BeanDefinition) *BeanRegistration { +func Accept(b *ToBeRegisteredBean) *RegisteredBean { return app.C.Accept(b) } -func Group(fn func(p gs.Properties) ([]*BeanDefinition, error)) { +func Group(fn func(p Properties) ([]*ToBeRegisteredBean, error)) { app.C.Group(fn) } -func RefreshProperties(p gs.Properties) error { +func RefreshProperties(p Properties) error { return app.C.RefreshProperties(p) } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 7744c7ab..f745aff2 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -248,30 +248,30 @@ func (d *BeanDefinition) String() string { } // Name 设置 bean 的名称。 -func (d *BeanDefinition) Name(name string) *BeanDefinition { +func (d *BeanDefinition) setName(name string) *BeanDefinition { d.name = name return d } -func (d *BeanDefinition) Caller(skip int) *BeanDefinition { +func (d *BeanDefinition) setCaller(skip int) *BeanDefinition { _, d.file, d.line, _ = runtime.Caller(skip) return d } // On 设置 bean 的 Condition。 -func (d *BeanDefinition) On(cond Condition) *BeanDefinition { +func (d *BeanDefinition) setOn(cond Condition) *BeanDefinition { d.cond = cond return d } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) DependsOn(selectors ...BeanSelector) *BeanDefinition { +func (d *BeanDefinition) setDependsOn(selectors ...BeanSelector) *BeanDefinition { d.depends = append(d.depends, selectors...) return d } // Primary 设置 bean 为主版本。 -func (d *BeanDefinition) Primary() *BeanDefinition { +func (d *BeanDefinition) setPrimary() *BeanDefinition { d.primary = true return d } @@ -289,7 +289,7 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { } // Init 设置 bean 的初始化函数。 -func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { +func (d *BeanDefinition) setInit(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.init = fn return d @@ -298,7 +298,7 @@ func (d *BeanDefinition) Init(fn interface{}) *BeanDefinition { } // Destroy 设置 bean 的销毁函数。 -func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { +func (d *BeanDefinition) setDestroy(fn interface{}) *BeanDefinition { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.destroy = fn return d @@ -307,7 +307,7 @@ func (d *BeanDefinition) Destroy(fn interface{}) *BeanDefinition { } // Export 设置 bean 的导出接口。 -func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { +func (d *BeanDefinition) setExport(exports ...interface{}) *BeanDefinition { for _, o := range exports { t, ok := o.(reflect.Type) if !ok { @@ -335,7 +335,7 @@ func (d *BeanDefinition) Export(exports ...interface{}) *BeanDefinition { } // Configuration 设置 bean 为配置类。 -func (d *BeanDefinition) Configuration(includes []string, excludes []string) *BeanDefinition { +func (d *BeanDefinition) setConfiguration(includes []string, excludes []string) *BeanDefinition { d.configuration = true d.includeMethod = includes d.excludeMethod = excludes @@ -343,7 +343,7 @@ func (d *BeanDefinition) Configuration(includes []string, excludes []string) *Be } // EnableRefresh 设置 bean 为可刷新的。 -func (d *BeanDefinition) EnableRefresh(tag string) *BeanDefinition { +func (d *BeanDefinition) setEnableRefresh(tag string) *BeanDefinition { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index c4cb2cf3..5b1dd5b2 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -3,6 +3,7 @@ package gs import ( "context" "reflect" + "unsafe" "github.com/go-spring/spring-core/conf" ) @@ -66,84 +67,92 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } -type BeanRegistration struct { +type beanRegistrationImpl[T RegisteredBean | ToBeRegisteredBean] struct { b *BeanDefinition } -func NewBeanRegistration(d *BeanDefinition) *BeanRegistration { - return &BeanRegistration{d} +func (d *beanRegistrationImpl[T]) BeanDefinition() *BeanDefinition { + return d.b } -func (d *BeanRegistration) Name(name string) *BeanRegistration { - d.b.Name(name) - return d -} - -func (d *BeanRegistration) BeanName() string { - return d.b.BeanName() -} - -func (d *BeanRegistration) ID() string { - return d.b.ID() -} - -func (d *BeanRegistration) Interface() interface{} { - return d.b.Interface() +func (d *beanRegistrationImpl[T]) Name(name string) *T { + d.b.setName(name) + return *(**T)(unsafe.Pointer(&d)) } // On 设置 bean 的 Condition。 -func (d *BeanRegistration) On(cond Condition) *BeanRegistration { - d.b.On(cond) - return d +func (d *beanRegistrationImpl[T]) On(cond Condition) *T { + d.b.setOn(cond) + return *(**T)(unsafe.Pointer(&d)) } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanRegistration) DependsOn(selectors ...BeanSelector) *BeanRegistration { - d.b.DependsOn(selectors...) - return d +func (d *beanRegistrationImpl[T]) DependsOn(selectors ...BeanSelector) *T { + d.b.setDependsOn(selectors...) + return *(**T)(unsafe.Pointer(&d)) } // Primary 设置 bean 为主版本。 -func (d *BeanRegistration) Primary() *BeanRegistration { - d.b.Primary() - return d +func (d *beanRegistrationImpl[T]) Primary() *T { + d.b.setPrimary() + return *(**T)(unsafe.Pointer(&d)) } // Init 设置 bean 的初始化函数。 -func (d *BeanRegistration) Init(fn interface{}) *BeanRegistration { - d.b.Init(fn) - return d +func (d *beanRegistrationImpl[T]) Init(fn interface{}) *T { + d.b.setInit(fn) + return *(**T)(unsafe.Pointer(&d)) } // Destroy 设置 bean 的销毁函数。 -func (d *BeanRegistration) Destroy(fn interface{}) *BeanRegistration { - d.b.Destroy(fn) - return d +func (d *beanRegistrationImpl[T]) Destroy(fn interface{}) *T { + d.b.setDestroy(fn) + return *(**T)(unsafe.Pointer(&d)) } // Export 设置 bean 的导出接口。 -func (d *BeanRegistration) Export(exports ...interface{}) *BeanRegistration { - d.b.Export(exports...) - return d +func (d *beanRegistrationImpl[T]) Export(exports ...interface{}) *T { + d.b.setExport(exports...) + return *(**T)(unsafe.Pointer(&d)) } // Configuration 设置 bean 为配置类。 -func (d *BeanRegistration) Configuration(includes []string, excludes []string) *BeanRegistration { - d.b.Configuration(includes, excludes) - return d +func (d *beanRegistrationImpl[T]) Configuration(includes []string, excludes []string) *T { + d.b.setConfiguration(includes, excludes) + return *(**T)(unsafe.Pointer(&d)) } // EnableRefresh 设置 bean 为可刷新的。 -func (d *BeanRegistration) EnableRefresh(tag string) *BeanRegistration { - d.b.EnableRefresh(tag) - return d +func (d *beanRegistrationImpl[T]) EnableRefresh(tag string) *T { + d.b.setEnableRefresh(tag) + return *(**T)(unsafe.Pointer(&d)) +} + +type RegisteredBean struct { + beanRegistrationImpl[RegisteredBean] +} + +func NewRegisteredBean(d *BeanDefinition) *RegisteredBean { + return &RegisteredBean{ + beanRegistrationImpl: beanRegistrationImpl[RegisteredBean]{d}, + } +} + +type ToBeRegisteredBean struct { + beanRegistrationImpl[ToBeRegisteredBean] +} + +func NewToBeRegisteredBean(d *BeanDefinition) *ToBeRegisteredBean { + return &ToBeRegisteredBean{ + beanRegistrationImpl: beanRegistrationImpl[ToBeRegisteredBean]{d}, + } } type Container interface { - Object(i interface{}) *BeanRegistration - Provide(ctor interface{}, args ...Arg) *BeanRegistration - Accept(b *BeanDefinition) *BeanRegistration - Group(fn func(p Properties) ([]*BeanDefinition, error)) + Object(i interface{}) *RegisteredBean + Provide(ctor interface{}, args ...Arg) *RegisteredBean + Accept(b *ToBeRegisteredBean) *RegisteredBean + Group(fn func(p Properties) ([]*ToBeRegisteredBean, error)) RefreshProperties(p Properties) error Refresh() error SimplifyMemory() diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 4cccb026..33e9ab06 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -44,28 +44,28 @@ func (b *Boot) Config() *gs_conf.BootConfig { return b.p } -func (b *Boot) Runner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanRegistration { +func (b *Boot) Runner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.RegisteredBean { bd := gs_core.NewBean(objOrCtor, ctorArgs...) bd.Export((*Runner)(nil)) return b.c.Accept(bd) } // Object 参考 Container.Object 的解释。 -func (b *Boot) Object(i interface{}) *gs.BeanRegistration { +func (b *Boot) Object(i interface{}) *gs.RegisteredBean { return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) } // Provide 参考 Container.Provide 的解释。 -func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { +func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { return b.c.Accept(gs_core.NewBean(ctor, args...)) } // Accept 参考 Container.Accept 的解释。 -func (b *Boot) Accept(bd *gs.BeanDefinition) *gs.BeanRegistration { +func (b *Boot) Accept(bd *gs.ToBeRegisteredBean) *gs.RegisteredBean { return b.c.Accept(bd) } -func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.BeanDefinition, error)) { +func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.ToBeRegisteredBean, error)) { b.c.Group(fn) } diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index 5fb80fcf..f92676a3 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -29,7 +29,7 @@ import ( ) // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.ToBeRegisteredBean { var v reflect.Value var fromValue bool @@ -106,5 +106,6 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { name = strings.TrimPrefix(s[len(s)-1], "*") } - return gs.NewBean(t, v, f, name, method, file, line) + d := gs.NewBean(t, v, f, name, method, file, line) + return gs.NewToBeRegisteredBean(d) } diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index cb1463eb..57e8a530 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -32,7 +32,7 @@ import ( ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 -func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.ToBeRegisteredBean { return gs_core.NewBean(objOrCtor, ctorArgs...) } @@ -108,7 +108,7 @@ func TestIsFuncBeanType(t *testing.T) { func TestBeanDefinition_Match(t *testing.T) { data := []struct { - bd *gs.BeanDefinition + bd *gs.ToBeRegisteredBean typeName string beanName string expect bool @@ -128,7 +128,7 @@ func TestBeanDefinition_Match(t *testing.T) { } for i, s := range data { - if ok := s.bd.Match(s.typeName, s.beanName); ok != s.expect { + if ok := s.bd.BeanDefinition().Match(s.typeName, s.beanName); ok != s.expect { t.Errorf("%d expect %v but %v", i, s.expect, ok) } } @@ -160,7 +160,7 @@ func TestObjectBean(t *testing.T) { t.Run("check name && typename", func(t *testing.T) { - data := map[*gs.BeanDefinition]struct { + data := map[*gs.ToBeRegisteredBean]struct { name string typeName string }{ @@ -185,8 +185,8 @@ func TestObjectBean(t *testing.T) { } for bd, v := range data { - assert.Equal(t, bd.BeanName(), v.name) - assert.Equal(t, bd.TypeName(), v.typeName) + assert.Equal(t, bd.BeanDefinition().BeanName(), v.name) + assert.Equal(t, bd.BeanDefinition().TypeName(), v.typeName) } }) } @@ -194,10 +194,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanDefinition().Type().String(), "*gs_core_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanDefinition().Type().String(), "*gs_core_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -209,11 +209,11 @@ func TestConstructorBean(t *testing.T) { funcFn := func() func(int) { return nil } bd = newBean(funcFn) - assert.Equal(t, bd.Type().String(), "func(int)") + assert.Equal(t, bd.BeanDefinition().Type().String(), "func(int)") interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.Type().String(), "gs_core_test.Teacher") + assert.Equal(t, bd.BeanDefinition().Type().String(), "gs_core_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 75d76e30..d6a000a1 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -45,9 +45,9 @@ const ( Refreshed // 已刷新 ) -var BeanDefinitionPtrType = reflect.TypeOf((*gs.BeanDefinition)(nil)) +var ToBeRegisteredBeanType = reflect.TypeOf((*gs.ToBeRegisteredBean)(nil)) -type GroupFunc = func(p gs.Properties) ([]*gs.BeanDefinition, error) +type GroupFunc = func(p gs.Properties) ([]*gs.ToBeRegisteredBean, error) // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { @@ -105,23 +105,23 @@ func New() gs.Container { } // Object 注册对象形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Object(i interface{}) *gs.BeanRegistration { +func (c *Container) Object(i interface{}) *gs.RegisteredBean { b := NewBean(reflect.ValueOf(i)) return c.Accept(b) } // Provide 注册构造函数形式的 bean ,需要注意的是该方法在注入开始后就不能再调用了。 -func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.BeanRegistration { +func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { b := NewBean(ctor, args...) return c.Accept(b) } -func (c *Container) Accept(b *gs.BeanDefinition) *gs.BeanRegistration { +func (c *Container) Accept(b *gs.ToBeRegisteredBean) *gs.RegisteredBean { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } - c.beans = append(c.beans, b) - return gs.NewBeanRegistration(b) + c.beans = append(c.beans, b.BeanDefinition()) + return gs.NewRegisteredBean(b.BeanDefinition()) } func (c *Container) Group(fn GroupFunc) { @@ -168,12 +168,14 @@ func (c *Container) Refresh() (err error) { // 处理 group 逻辑 for _, fn := range c.groupFuncs { - var beans []*gs.BeanDefinition + var beans []*gs.ToBeRegisteredBean beans, err = fn(c.p.Data()) if err != nil { return err } - c.beans = append(c.beans, beans...) + for _, b := range beans { + c.beans = append(c.beans, b.BeanDefinition()) + } } c.groupFuncs = nil @@ -337,16 +339,16 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } fnType := m.Func.Type() out0 := fnType.Out(0) - if out0 == BeanDefinitionPtrType { + if out0 == ToBeRegisteredBeanType { ret := m.Func.Call([]reflect.Value{bd.Value()}) if len(ret) > 1 { if err := ret[1].Interface().(error); err != nil { return nil, err } } - b := ret[0].Interface().(*gs.BeanDefinition) - newBeans = append(newBeans, b) - retBeans, err := c.scanConfiguration(b) + b := ret[0].Interface().(*gs.ToBeRegisteredBean) + newBeans = append(newBeans, b.BeanDefinition()) + retBeans, err := c.scanConfiguration(b.BeanDefinition()) if err != nil { return nil, err } @@ -362,7 +364,8 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti v = v.Elem() } name := bd.GetName() + "_" + m.Name - b := gs.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()).On(gs_cond.OnBean(bd)) + b := gs.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()) + gs.NewToBeRegisteredBean(b).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) } break @@ -534,16 +537,16 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} var err error switch c.state { case Refreshing: - err = c.wireBeanInRefreshing(b, stack) + err = c.wireBeanInRefreshing(b.BeanDefinition(), stack) case Refreshed: - err = c.wireBeanAfterRefreshed(b, stack) + err = c.wireBeanAfterRefreshed(b.BeanDefinition(), stack) default: err = errors.New("state is error for wiring") } if err != nil { return nil, err } - return b.Interface(), nil + return b.BeanDefinition().Interface(), nil } // Invoke 调用函数,函数的参数会自动注入,函数的返回值也会自动注入。 diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 03dd0adc..b5436e31 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -889,7 +889,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanName(), "NewManager") + assert.Equal(t, bd.BeanDefinition().BeanName(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1186,7 +1186,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanName(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.BeanDefinition().BeanName(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1436,7 +1436,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent.ID()) + bd := c.Provide((*Server).Consumer, parent.BeanDefinition().ID()) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1454,7 +1454,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanName(), "Consumer") + assert.Equal(t, bd.BeanDefinition().BeanName(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1477,7 +1477,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanName(), "Consumer") + assert.Equal(t, bd.BeanDefinition().BeanName(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1486,7 +1486,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent.ID(), "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.BeanDefinition().ID(), "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1512,7 +1512,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.BeanDefinition().ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1566,7 +1566,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.BeanDefinition().ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2022,7 +2022,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface().(*callDestroy) + d := bd.BeanDefinition().Interface().(*callDestroy) assert.True(t, d.destroyed) }) @@ -2039,7 +2039,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface() + d := bd.BeanDefinition().Interface() assert.False(t, d.(*callDestroy).destroyed) } @@ -2059,7 +2059,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.Interface() + d := bd.BeanDefinition().Interface() assert.True(t, d.(*callDestroy).destroyed) } }) @@ -2467,7 +2467,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.BeanDefinition().ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -2898,7 +2898,7 @@ func TestContextAware(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() assert.Nil(t, err) - a := b.Interface().(*ContextAware) + a := b.BeanDefinition().Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } @@ -3036,7 +3036,7 @@ func (c *ConfigurationBean) NewChild() *ChildBean { return &ChildBean{c.s} } -func (c *ConfigurationBean) NewBean() *gs.BeanDefinition { +func (c *ConfigurationBean) NewBean() *gs.ToBeRegisteredBean { return gs_core.NewBean(&ChildBean{"100"}).Name("100") } From d4454f09b0b5343ceea1dc37730da7cbc4fb0261 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 10 Jan 2025 14:01:46 +0800 Subject: [PATCH 64/75] code refactor --- gs/gs.go | 34 ++--- gs/internal/gs/bean.go | 227 ++++++++++++++----------------- gs/internal/gs/gs.go | 40 +++--- gs/internal/gs_app/boot.go | 4 +- gs/internal/gs_core/bean.go | 4 +- gs/internal/gs_core/bean_test.go | 8 +- gs/internal/gs_core/core.go | 46 +++---- gs/internal/gs_core/core_test.go | 10 +- gs/internal/gs_core/wire.go | 26 ++-- 9 files changed, 186 insertions(+), 213 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 069c3ef9..eb56ff4a 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -37,21 +37,21 @@ const ( ) type ( - Arg = gs.Arg - BeanDefinition = gs.BeanDefinition - BeanInit = gs.BeanInit - BeanDestroy = gs.BeanDestroy - RegisteredBean = gs.RegisteredBean - ToBeRegisteredBean = gs.ToBeRegisteredBean - BeanSelector = gs.BeanSelector - CondContext = gs.CondContext - Condition = gs.Condition - Properties = gs.Properties - Context = gs.Context - ContextAware = gs_core.ContextAware - Dync[T any] = gs_dync.Value[T] - Runner = gs_app.Runner - Server = gs_app.Server + Arg = gs.Arg + BeanDefinition = gs.BeanDefinition + BeanInit = gs.BeanInit + BeanDestroy = gs.BeanDestroy + RegisteredBean = gs.RegisteredBean + UnregisteredBean = gs.UnregisteredBean + BeanSelector = gs.BeanSelector + CondContext = gs.CondContext + Condition = gs.Condition + Properties = gs.Properties + Context = gs.Context + ContextAware = gs_core.ContextAware + Dync[T any] = gs_dync.Value[T] + Runner = gs_app.Runner + Server = gs_app.Server ) /************************************ arg ***********************************/ @@ -257,11 +257,11 @@ func Provide(ctor interface{}, args ...Arg) *RegisteredBean { } // Accept 参考 Container.Accept 的解释。 -func Accept(b *ToBeRegisteredBean) *RegisteredBean { +func Accept(b *UnregisteredBean) *RegisteredBean { return app.C.Accept(b) } -func Group(fn func(p Properties) ([]*ToBeRegisteredBean, error)) { +func Group(fn func(p Properties) ([]*UnregisteredBean, error)) { app.C.Group(fn) } diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index f745aff2..4bb1323f 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -54,8 +54,7 @@ type BeanDestroy interface { OnDestroy() } -// beanRegistration bean 注册数据。 -type beanRegistration struct { +type BeanMetadata struct { f Callable // 构造函数 method bool // 是否为成员方法 cond Condition // 判断条件 @@ -75,172 +74,154 @@ type beanRegistration struct { refreshParam conf.BindParam } -type BeanRuntimeMeta struct { - v reflect.Value // 值 - t reflect.Type // 类型 - name string // 名称 - typeName string // 原始类型的全限定名 - primary bool // 是否为主版本 -} - -func (d *BeanRuntimeMeta) Callable() Callable { - return nil +func (d *BeanMetadata) SetStatus(status BeanStatus) { + d.status = status } -// Interface 返回 bean 的真实值。 -func (d *BeanRuntimeMeta) Interface() interface{} { - return d.v.Interface() +func (d *BeanMetadata) IsMethod() bool { + return d.method } -func (d *BeanRuntimeMeta) GetName() string { - return d.name +func (d *BeanMetadata) GetCond() Condition { + return d.cond } -func (d *BeanRuntimeMeta) IsPrimary() bool { - return d.primary +func (d *BeanMetadata) GetInit() interface{} { + return d.init } -func (d *BeanRuntimeMeta) Type() reflect.Type { - return d.t +func (d *BeanMetadata) GetDestroy() interface{} { + return d.destroy } -func (d *BeanRuntimeMeta) Value() reflect.Value { - return d.v +func (d *BeanMetadata) GetDepends() []BeanSelector { + return d.depends } -func (d *BeanRuntimeMeta) GetStatus() BeanStatus { - return Wired +func (d *BeanMetadata) GetExports() []reflect.Type { + return d.exports } -func (d *BeanRuntimeMeta) Match(typeName string, beanName string) bool { - - typeIsSame := false - if typeName == "" || d.typeName == typeName { - typeIsSame = true - } - - nameIsSame := false - if beanName == "" || d.name == beanName { - nameIsSame = true - } - - return typeIsSame && nameIsSame +func (d *BeanMetadata) IsConfiguration() bool { + return d.configuration } -func (d *BeanRuntimeMeta) String() string { - return d.name +func (d *BeanMetadata) GetIncludeMethod() []string { + return d.includeMethod } -// BeanDefinition bean 元数据。 -type BeanDefinition struct { - *beanRegistration - *BeanRuntimeMeta +func (d *BeanMetadata) GetExcludeMethod() []string { + return d.excludeMethod } -func (d *BeanDefinition) Callable() Callable { - return d.f +func (d *BeanMetadata) IsRefreshEnable() bool { + return d.enableRefresh } -func (d *BeanDefinition) GetTypeName() string { - return d.typeName +func (d *BeanMetadata) GetRefreshParam() conf.BindParam { + return d.refreshParam } -func (d *BeanDefinition) GetStatus() BeanStatus { - return d.status +func (d *BeanMetadata) File() string { + return d.file } -func (d *BeanDefinition) SetStatus(status BeanStatus) { - d.status = status +func (d *BeanMetadata) Line() int { + return d.line } -func (d *BeanDefinition) IsMethod() bool { - return d.method +// FileLine 返回 bean 的注册点。 +func (d *BeanMetadata) FileLine() string { + return fmt.Sprintf("%s:%d", d.file, d.line) } -func (d *BeanDefinition) GetCond() Condition { - return d.cond +// GetClass 返回 bean 的类型描述。 +func (d *BeanMetadata) GetClass() string { + if d.f == nil { + return "object bean" + } + return "constructor bean" } -func (d *BeanDefinition) GetInit() interface{} { - return d.init +type BeanRuntime struct { + v reflect.Value // 值 + t reflect.Type // 类型 + name string // 名称 + typeName string // 原始类型的全限定名 + primary bool // 是否为主版本 } -func (d *BeanDefinition) GetDestroy() interface{} { - return d.destroy +// ID 返回 bean 的 ID 。 +func (d *BeanRuntime) ID() string { + return d.typeName + ":" + d.name } -func (d *BeanDefinition) GetDepends() []BeanSelector { - return d.depends +// Name 返回 bean 的名称。 +func (d *BeanRuntime) Name() string { + return d.name } -func (d *BeanDefinition) GetExports() []reflect.Type { - return d.exports +// TypeName 返回 bean 的原始类型的全限定名。 +func (d *BeanRuntime) TypeName() string { + return d.typeName } -func (d *BeanDefinition) IsConfiguration() bool { - return d.configuration +func (d *BeanRuntime) Callable() Callable { + return nil } -func (d *BeanDefinition) GetIncludeMethod() []string { - return d.includeMethod +// Interface 返回 bean 的真实值。 +func (d *BeanRuntime) Interface() interface{} { + return d.v.Interface() } -func (d *BeanDefinition) GetExcludeMethod() []string { - return d.excludeMethod +func (d *BeanRuntime) IsPrimary() bool { + return d.primary } -func (d *BeanDefinition) IsRefreshEnable() bool { - return d.enableRefresh +func (d *BeanRuntime) Type() reflect.Type { + return d.t } -func (d *BeanDefinition) GetRefreshParam() conf.BindParam { - return d.refreshParam +func (d *BeanRuntime) Value() reflect.Value { + return d.v } -// ID 返回 bean 的 ID 。 -func (d *BeanDefinition) ID() string { - return d.typeName + ":" + d.name +func (d *BeanRuntime) Status() BeanStatus { + return Wired } -// BeanName 返回 bean 的名称。 -func (d *BeanDefinition) BeanName() string { - return d.name -} +func (d *BeanRuntime) Match(typeName string, beanName string) bool { -// TypeName 返回 bean 的原始类型的全限定名。 -func (d *BeanDefinition) TypeName() string { - return d.typeName -} + typeIsSame := false + if typeName == "" || d.typeName == typeName { + typeIsSame = true + } -// Created 返回是否已创建。 -func (d *BeanDefinition) Created() bool { - return d.status >= Created -} + nameIsSame := false + if beanName == "" || d.name == beanName { + nameIsSame = true + } -// Wired 返回 bean 是否已经注入。 -func (d *BeanDefinition) Wired() bool { - return d.status == Wired + return typeIsSame && nameIsSame } -func (d *BeanDefinition) File() string { - return d.file +func (d *BeanRuntime) String() string { + return d.name } -func (d *BeanDefinition) Line() int { - return d.line +// BeanDefinition bean 元数据。 +type BeanDefinition struct { + *BeanMetadata + *BeanRuntime } -// FileLine 返回 bean 的注册点。 -func (d *BeanDefinition) FileLine() string { - return fmt.Sprintf("%s:%d", d.file, d.line) +func (d *BeanDefinition) Callable() Callable { + return d.f } -// GetClass 返回 bean 的类型描述。 -func (d *BeanDefinition) GetClass() string { - if d.f == nil { - return "object bean" - } - return "constructor bean" +func (d *BeanDefinition) Status() BeanStatus { + return d.status } func (d *BeanDefinition) String() string { @@ -248,32 +229,27 @@ func (d *BeanDefinition) String() string { } // Name 设置 bean 的名称。 -func (d *BeanDefinition) setName(name string) *BeanDefinition { +func (d *BeanDefinition) setName(name string) { d.name = name - return d } -func (d *BeanDefinition) setCaller(skip int) *BeanDefinition { +func (d *BeanDefinition) setCaller(skip int) { _, d.file, d.line, _ = runtime.Caller(skip) - return d } // On 设置 bean 的 Condition。 -func (d *BeanDefinition) setOn(cond Condition) *BeanDefinition { +func (d *BeanDefinition) setOn(cond Condition) { d.cond = cond - return d } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) setDependsOn(selectors ...BeanSelector) *BeanDefinition { +func (d *BeanDefinition) setDependsOn(selectors ...BeanSelector) { d.depends = append(d.depends, selectors...) - return d } // Primary 设置 bean 为主版本。 -func (d *BeanDefinition) setPrimary() *BeanDefinition { +func (d *BeanDefinition) setPrimary() { d.primary = true - return d } // validLifeCycleFunc 判断是否是合法的用于 bean 生命周期控制的函数,生命周期函数 @@ -289,25 +265,25 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { } // Init 设置 bean 的初始化函数。 -func (d *BeanDefinition) setInit(fn interface{}) *BeanDefinition { +func (d *BeanDefinition) setInit(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.init = fn - return d + return } panic(errors.New("init should be func(bean) or func(bean)error")) } // Destroy 设置 bean 的销毁函数。 -func (d *BeanDefinition) setDestroy(fn interface{}) *BeanDefinition { +func (d *BeanDefinition) setDestroy(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.destroy = fn - return d + return } panic(errors.New("destroy should be func(bean) or func(bean)error")) } // Export 设置 bean 的导出接口。 -func (d *BeanDefinition) setExport(exports ...interface{}) *BeanDefinition { +func (d *BeanDefinition) setExport(exports ...interface{}) { for _, o := range exports { t, ok := o.(reflect.Type) if !ok { @@ -331,19 +307,17 @@ func (d *BeanDefinition) setExport(exports ...interface{}) *BeanDefinition { } d.exports = append(d.exports, t) } - return d } // Configuration 设置 bean 为配置类。 -func (d *BeanDefinition) setConfiguration(includes []string, excludes []string) *BeanDefinition { +func (d *BeanDefinition) setConfiguration(includes []string, excludes []string) { d.configuration = true d.includeMethod = includes d.excludeMethod = excludes - return d } // EnableRefresh 设置 bean 为可刷新的。 -func (d *BeanDefinition) setEnableRefresh(tag string) *BeanDefinition { +func (d *BeanDefinition) setEnableRefresh(tag string) { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } @@ -352,20 +326,19 @@ func (d *BeanDefinition) setEnableRefresh(tag string) *BeanDefinition { if err != nil { panic(err) } - return d } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ - beanRegistration: &beanRegistration{ + BeanMetadata: &BeanMetadata{ f: f, method: method, file: file, line: line, status: Default, }, - BeanRuntimeMeta: &BeanRuntimeMeta{ + BeanRuntime: &BeanRuntime{ t: t, v: v, name: name, diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 5b1dd5b2..75f4cc3e 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -67,92 +67,92 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } -type beanRegistrationImpl[T RegisteredBean | ToBeRegisteredBean] struct { +type beanBuilder[T RegisteredBean | UnregisteredBean] struct { b *BeanDefinition } -func (d *beanRegistrationImpl[T]) BeanDefinition() *BeanDefinition { +func (d *beanBuilder[T]) BeanDefinition() *BeanDefinition { return d.b } -func (d *beanRegistrationImpl[T]) Name(name string) *T { +func (d *beanBuilder[T]) Name(name string) *T { d.b.setName(name) return *(**T)(unsafe.Pointer(&d)) } // On 设置 bean 的 Condition。 -func (d *beanRegistrationImpl[T]) On(cond Condition) *T { +func (d *beanBuilder[T]) On(cond Condition) *T { d.b.setOn(cond) return *(**T)(unsafe.Pointer(&d)) } // DependsOn 设置 bean 的间接依赖项。 -func (d *beanRegistrationImpl[T]) DependsOn(selectors ...BeanSelector) *T { +func (d *beanBuilder[T]) DependsOn(selectors ...BeanSelector) *T { d.b.setDependsOn(selectors...) return *(**T)(unsafe.Pointer(&d)) } // Primary 设置 bean 为主版本。 -func (d *beanRegistrationImpl[T]) Primary() *T { +func (d *beanBuilder[T]) Primary() *T { d.b.setPrimary() return *(**T)(unsafe.Pointer(&d)) } // Init 设置 bean 的初始化函数。 -func (d *beanRegistrationImpl[T]) Init(fn interface{}) *T { +func (d *beanBuilder[T]) Init(fn interface{}) *T { d.b.setInit(fn) return *(**T)(unsafe.Pointer(&d)) } // Destroy 设置 bean 的销毁函数。 -func (d *beanRegistrationImpl[T]) Destroy(fn interface{}) *T { +func (d *beanBuilder[T]) Destroy(fn interface{}) *T { d.b.setDestroy(fn) return *(**T)(unsafe.Pointer(&d)) } // Export 设置 bean 的导出接口。 -func (d *beanRegistrationImpl[T]) Export(exports ...interface{}) *T { +func (d *beanBuilder[T]) Export(exports ...interface{}) *T { d.b.setExport(exports...) return *(**T)(unsafe.Pointer(&d)) } // Configuration 设置 bean 为配置类。 -func (d *beanRegistrationImpl[T]) Configuration(includes []string, excludes []string) *T { +func (d *beanBuilder[T]) Configuration(includes []string, excludes []string) *T { d.b.setConfiguration(includes, excludes) return *(**T)(unsafe.Pointer(&d)) } // EnableRefresh 设置 bean 为可刷新的。 -func (d *beanRegistrationImpl[T]) EnableRefresh(tag string) *T { +func (d *beanBuilder[T]) EnableRefresh(tag string) *T { d.b.setEnableRefresh(tag) return *(**T)(unsafe.Pointer(&d)) } type RegisteredBean struct { - beanRegistrationImpl[RegisteredBean] + beanBuilder[RegisteredBean] } func NewRegisteredBean(d *BeanDefinition) *RegisteredBean { return &RegisteredBean{ - beanRegistrationImpl: beanRegistrationImpl[RegisteredBean]{d}, + beanBuilder: beanBuilder[RegisteredBean]{d}, } } -type ToBeRegisteredBean struct { - beanRegistrationImpl[ToBeRegisteredBean] +type UnregisteredBean struct { + beanBuilder[UnregisteredBean] } -func NewToBeRegisteredBean(d *BeanDefinition) *ToBeRegisteredBean { - return &ToBeRegisteredBean{ - beanRegistrationImpl: beanRegistrationImpl[ToBeRegisteredBean]{d}, +func NewUnregisteredBean(d *BeanDefinition) *UnregisteredBean { + return &UnregisteredBean{ + beanBuilder: beanBuilder[UnregisteredBean]{d}, } } type Container interface { Object(i interface{}) *RegisteredBean Provide(ctor interface{}, args ...Arg) *RegisteredBean - Accept(b *ToBeRegisteredBean) *RegisteredBean - Group(fn func(p Properties) ([]*ToBeRegisteredBean, error)) + Accept(b *UnregisteredBean) *RegisteredBean + Group(fn func(p Properties) ([]*UnregisteredBean, error)) RefreshProperties(p Properties) error Refresh() error SimplifyMemory() diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 33e9ab06..fc84294c 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -61,11 +61,11 @@ func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { } // Accept 参考 Container.Accept 的解释。 -func (b *Boot) Accept(bd *gs.ToBeRegisteredBean) *gs.RegisteredBean { +func (b *Boot) Accept(bd *gs.UnregisteredBean) *gs.RegisteredBean { return b.c.Accept(bd) } -func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.ToBeRegisteredBean, error)) { +func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.UnregisteredBean, error)) { b.c.Group(fn) } diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index f92676a3..5c4afc99 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -29,7 +29,7 @@ import ( ) // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.ToBeRegisteredBean { +func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { var v reflect.Value var fromValue bool @@ -107,5 +107,5 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.ToBeRegisteredBean { } d := gs.NewBean(t, v, f, name, method, file, line) - return gs.NewToBeRegisteredBean(d) + return gs.NewUnregisteredBean(d) } diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index 57e8a530..5ced49f3 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -32,7 +32,7 @@ import ( ) // newBean 该方法是为了平衡调用栈的深度,一般情况下 gs.NewBean 不应该被直接使用。 -func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.ToBeRegisteredBean { +func newBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { return gs_core.NewBean(objOrCtor, ctorArgs...) } @@ -108,7 +108,7 @@ func TestIsFuncBeanType(t *testing.T) { func TestBeanDefinition_Match(t *testing.T) { data := []struct { - bd *gs.ToBeRegisteredBean + bd *gs.UnregisteredBean typeName string beanName string expect bool @@ -160,7 +160,7 @@ func TestObjectBean(t *testing.T) { t.Run("check name && typename", func(t *testing.T) { - data := map[*gs.ToBeRegisteredBean]struct { + data := map[*gs.UnregisteredBean]struct { name string typeName string }{ @@ -185,7 +185,7 @@ func TestObjectBean(t *testing.T) { } for bd, v := range data { - assert.Equal(t, bd.BeanDefinition().BeanName(), v.name) + assert.Equal(t, bd.BeanDefinition().Name(), v.name) assert.Equal(t, bd.BeanDefinition().TypeName(), v.typeName) } }) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index d6a000a1..8fc61c25 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -45,9 +45,9 @@ const ( Refreshed // 已刷新 ) -var ToBeRegisteredBeanType = reflect.TypeOf((*gs.ToBeRegisteredBean)(nil)) +var UnregisteredBeanType = reflect.TypeOf((*gs.UnregisteredBean)(nil)) -type GroupFunc = func(p gs.Properties) ([]*gs.ToBeRegisteredBean, error) +type GroupFunc = func(p gs.Properties) ([]*gs.UnregisteredBean, error) // ContextAware injects the Context into a struct as the field GSContext. type ContextAware struct { @@ -56,8 +56,8 @@ type ContextAware struct { type SimpleBean interface { Callable() gs.Callable - GetName() string - GetStatus() gs.BeanStatus + Name() string + Status() gs.BeanStatus Interface() interface{} IsPrimary() bool Match(typeName string, beanName string) bool @@ -116,7 +116,7 @@ func (c *Container) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean return c.Accept(b) } -func (c *Container) Accept(b *gs.ToBeRegisteredBean) *gs.RegisteredBean { +func (c *Container) Accept(b *gs.UnregisteredBean) *gs.RegisteredBean { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } @@ -168,7 +168,7 @@ func (c *Container) Refresh() (err error) { // 处理 group 逻辑 for _, fn := range c.groupFuncs { - var beans []*gs.ToBeRegisteredBean + var beans []*gs.UnregisteredBean beans, err = fn(c.p.Data()) if err != nil { return err @@ -207,11 +207,11 @@ func (c *Container) Refresh() (err error) { beansById := make(map[string]*gs.BeanDefinition) { for _, b := range c.beans { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } - if b.GetStatus() != gs.Resolved { - return fmt.Errorf("unexpected status %d", b.GetStatus()) + if b.Status() != gs.Resolved { + return fmt.Errorf("unexpected status %d", b.Status()) } beanID := b.ID() if d, ok := beansById[beanID]; ok { @@ -261,13 +261,13 @@ func (c *Container) Refresh() (err error) { c.beansByName = make(map[string][]SimpleBean) c.beansByType = make(map[reflect.Type][]SimpleBean) for _, b := range c.beans { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b.BeanRuntimeMeta) - c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.BeanRuntimeMeta) + c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b.BeanRuntime) + c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.BeanRuntime) for _, t := range b.GetExports() { - c.beansByType[t] = append(c.beansByType[t], b.BeanRuntimeMeta) + c.beansByType[t] = append(c.beansByType[t], b.BeanRuntime) } } } else { // 清空全部数据 @@ -339,14 +339,14 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } fnType := m.Func.Type() out0 := fnType.Out(0) - if out0 == ToBeRegisteredBeanType { + if out0 == UnregisteredBeanType { ret := m.Func.Call([]reflect.Value{bd.Value()}) if len(ret) > 1 { if err := ret[1].Interface().(error); err != nil { return nil, err } } - b := ret[0].Interface().(*gs.ToBeRegisteredBean) + b := ret[0].Interface().(*gs.UnregisteredBean) newBeans = append(newBeans, b.BeanDefinition()) retBeans, err := c.scanConfiguration(b.BeanDefinition()) if err != nil { @@ -363,9 +363,9 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti if util.IsBeanType(out0) { v = v.Elem() } - name := bd.GetName() + "_" + m.Name + name := bd.Name() + "_" + m.Name b := gs.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()) - gs.NewToBeRegisteredBean(b).On(gs_cond.OnBean(bd)) + gs.NewUnregisteredBean(b).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) } break @@ -375,11 +375,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } func (c *Container) registerBean(b *gs.BeanDefinition) { - syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), b.Type(), b.FileLine()) - c.beansByName[b.GetName()] = append(c.beansByName[b.GetName()], b) + syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.Name(), b.Type(), b.FileLine()) + c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) for _, t := range b.GetExports() { - syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.BeanName(), t, b.FileLine()) + syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.Name(), t, b.FileLine()) c.beansByType[t] = append(c.beansByType[t], b) } } @@ -387,7 +387,7 @@ func (c *Container) registerBean(b *gs.BeanDefinition) { // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 func (c *Container) resolveBean(b *gs.BeanDefinition) error { - if b.GetStatus() >= gs.Resolving { + if b.Status() >= gs.Resolving { return nil } @@ -437,13 +437,13 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) finder := func(fn func(*gs.BeanDefinition) bool) ([]*gs.BeanDefinition, error) { var result []*gs.BeanDefinition for _, b := range c.beans { - if b.GetStatus() == gs.Resolving || b.GetStatus() == gs.Deleted || !fn(b) { + if b.Status() == gs.Resolving || b.Status() == gs.Deleted || !fn(b) { continue } if err := c.resolveBean(b); err != nil { return nil, err } - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } result = append(result, b) diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index b5436e31..6b42a669 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -889,7 +889,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanDefinition().BeanName(), "NewManager") + assert.Equal(t, bd.BeanDefinition().Name(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1186,7 +1186,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanDefinition().BeanName(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.BeanDefinition().Name(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1454,7 +1454,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().BeanName(), "Consumer") + assert.Equal(t, bd.BeanDefinition().Name(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1477,7 +1477,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().BeanName(), "Consumer") + assert.Equal(t, bd.BeanDefinition().Name(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -3036,7 +3036,7 @@ func (c *ConfigurationBean) NewChild() *ChildBean { return &ChildBean{c.s} } -func (c *ConfigurationBean) NewBean() *gs.ToBeRegisteredBean { +func (c *ConfigurationBean) NewBean() *gs.UnregisteredBean { return gs_core.NewBean(&ChildBean{"100"}).Name("100") } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index fd691f04..968579d2 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -78,7 +78,7 @@ func newWiringStack() *wiringStack { // pushBack 添加一个即将注入的 bean 。 func (s *wiringStack) pushBack(b *gs.BeanDefinition) { - syslog.Debug("push %s %s", b, gs.GetStatusString(b.GetStatus())) + syslog.Debug("push %s %s", b, gs.GetStatusString(b.Status())) s.beans = append(s.beans, b) } @@ -87,7 +87,7 @@ func (s *wiringStack) popBack() { n := len(s.beans) b := s.beans[n-1] s.beans = s.beans[:n-1] - syslog.Debug("pop %s %s", b, gs.GetStatusString(b.GetStatus())) + syslog.Debug("pop %s %s", b, gs.GetStatusString(b.Status())) } // path 返回 bean 的注入路径。 @@ -246,7 +246,7 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta type byBeanName []SimpleBean func (b byBeanName) Len() int { return len(b) } -func (b byBeanName) Less(i, j int) bool { return b[i].GetName() < b[j].GetName() } +func (b byBeanName) Less(i, j int) bool { return b[i].Name() < b[j].Name() } func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 @@ -298,7 +298,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, { var arr []SimpleBean for _, b := range beans { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } arr = append(arr, b) @@ -375,7 +375,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return err } case Refreshed: - if err := c.wireBeanAfterRefreshed(b.(*gs.BeanRuntimeMeta), stack); err != nil { + if err := c.wireBeanAfterRefreshed(b.(*gs.BeanRuntime), stack); err != nil { return err } default: @@ -394,7 +394,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, case reflect.Map: ret = reflect.MakeMap(t) for _, b := range beans { - ret.SetMapIndex(reflect.ValueOf(b.GetName()), b.Value()) + ret.SetMapIndex(reflect.ValueOf(b.Name()), b.Value()) } default: } @@ -416,7 +416,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er var foundBeans []SimpleBean for _, b := range c.beansByType[t] { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } if !b.Match(tag.typeName, tag.beanName) { @@ -428,7 +428,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 if t.Kind() == reflect.Interface && tag.beanName != "" { for _, b := range c.beansByName[tag.beanName] { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { continue } if !b.Type().AssignableTo(t) { @@ -516,12 +516,12 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 实例化被依赖的 bean 然后对它们进行注入。 func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStack) error { - if b.GetStatus() == gs.Deleted { + if b.Status() == gs.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) } // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.GetStatus() == gs.Wired { + if c.state == Refreshed && b.Status() == gs.Wired { return nil } @@ -545,14 +545,14 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac stack.pushBack(b) - if b.GetStatus() == gs.Creating && b.Callable() != nil { + if b.Status() == gs.Creating && b.Callable() != nil { prev := stack.beans[len(stack.beans)-2] - if prev.GetStatus() == gs.Creating { + if prev.Status() == gs.Creating { return errors.New("found circle autowire") } } - if b.GetStatus() >= gs.Creating { + if b.Status() >= gs.Creating { stack.popBack() return nil } From 1f87156f52241e15325bd665365ec95eae7de859 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 10 Jan 2025 17:57:45 +0800 Subject: [PATCH 65/75] code refactor --- gs/internal/gs/bean.go | 45 ++++++++++++++++++-------------- gs/internal/gs/gs.go | 4 +-- gs/internal/gs_core/core.go | 12 ++++----- gs/internal/gs_core/core_test.go | 4 +-- gs/internal/gs_core/wire.go | 16 ++++++------ 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/gs/internal/gs/bean.go b/gs/internal/gs/bean.go index 4bb1323f..75db184d 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs/bean.go @@ -66,9 +66,7 @@ type BeanMetadata struct { line int // 注册点所在行数 status BeanStatus // 状态 - configuration bool // 是否扫描成员方法 - includeMethod []string // 包含哪些成员方法 - excludeMethod []string // 排除那些成员方法 + configuration ConfigurationParam enableRefresh bool refreshParam conf.BindParam @@ -82,43 +80,43 @@ func (d *BeanMetadata) IsMethod() bool { return d.method } -func (d *BeanMetadata) GetCond() Condition { +func (d *BeanMetadata) Cond() Condition { return d.cond } -func (d *BeanMetadata) GetInit() interface{} { +func (d *BeanMetadata) Init() interface{} { return d.init } -func (d *BeanMetadata) GetDestroy() interface{} { +func (d *BeanMetadata) Destroy() interface{} { return d.destroy } -func (d *BeanMetadata) GetDepends() []BeanSelector { +func (d *BeanMetadata) Depends() []BeanSelector { return d.depends } -func (d *BeanMetadata) GetExports() []reflect.Type { +func (d *BeanMetadata) Exports() []reflect.Type { return d.exports } func (d *BeanMetadata) IsConfiguration() bool { - return d.configuration + return d.configuration.Enable } func (d *BeanMetadata) GetIncludeMethod() []string { - return d.includeMethod + return d.configuration.Include } func (d *BeanMetadata) GetExcludeMethod() []string { - return d.excludeMethod + return d.configuration.Exclude } -func (d *BeanMetadata) IsRefreshEnable() bool { +func (d *BeanMetadata) EnableRefresh() bool { return d.enableRefresh } -func (d *BeanMetadata) GetRefreshParam() conf.BindParam { +func (d *BeanMetadata) RefreshParam() conf.BindParam { return d.refreshParam } @@ -135,8 +133,8 @@ func (d *BeanMetadata) FileLine() string { return fmt.Sprintf("%s:%d", d.file, d.line) } -// GetClass 返回 bean 的类型描述。 -func (d *BeanMetadata) GetClass() string { +// Class 返回 bean 的类型描述。 +func (d *BeanMetadata) Class() string { if d.f == nil { return "object bean" } @@ -225,7 +223,7 @@ func (d *BeanDefinition) Status() BeanStatus { } func (d *BeanDefinition) String() string { - return fmt.Sprintf("%s name:%q %s", d.GetClass(), d.name, d.FileLine()) + return fmt.Sprintf("%s name:%q %s", d.Class(), d.name, d.FileLine()) } // Name 设置 bean 的名称。 @@ -309,11 +307,18 @@ func (d *BeanDefinition) setExport(exports ...interface{}) { } } +type ConfigurationParam struct { + Enable bool // 是否扫描成员方法 + Include []string // 包含哪些成员方法 + Exclude []string // 排除那些成员方法 +} + // Configuration 设置 bean 为配置类。 -func (d *BeanDefinition) setConfiguration(includes []string, excludes []string) { - d.configuration = true - d.includeMethod = includes - d.excludeMethod = excludes +func (d *BeanDefinition) setConfiguration(param ...ConfigurationParam) { + if len(param) > 0 { + d.configuration = param[0] + } + d.configuration.Enable = true } // EnableRefresh 设置 bean 为可刷新的。 diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 75f4cc3e..bbfc634f 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -117,8 +117,8 @@ func (d *beanBuilder[T]) Export(exports ...interface{}) *T { } // Configuration 设置 bean 为配置类。 -func (d *beanBuilder[T]) Configuration(includes []string, excludes []string) *T { - d.b.setConfiguration(includes, excludes) +func (d *beanBuilder[T]) Configuration(param ...ConfigurationParam) *T { + d.b.setConfiguration(param...) return *(**T)(unsafe.Pointer(&d)) } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 8fc61c25..295b7e7f 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -266,7 +266,7 @@ func (c *Container) Refresh() (err error) { } c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b.BeanRuntime) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b.BeanRuntime) - for _, t := range b.GetExports() { + for _, t := range b.Exports() { c.beansByType[t] = append(c.beansByType[t], b.BeanRuntime) } } @@ -375,11 +375,11 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } func (c *Container) registerBean(b *gs.BeanDefinition) { - syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.Name(), b.Type(), b.FileLine()) + syslog.Debug("register %s name:%q type:%q %s", b.Class(), b.Name(), b.Type(), b.FileLine()) c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) - for _, t := range b.GetExports() { - syslog.Debug("register %s name:%q type:%q %s", b.GetClass(), b.Name(), t, b.FileLine()) + for _, t := range b.Exports() { + syslog.Debug("register %s name:%q type:%q %s", b.Class(), b.Name(), t, b.FileLine()) c.beansByType[t] = append(c.beansByType[t], b) } } @@ -417,8 +417,8 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { } } - if b.GetCond() != nil { - if ok, err := b.GetCond().Matches(c); err != nil { + if b.Cond() != nil { + if ok, err := b.Cond().Matches(c); err != nil { return err } else if !ok { b.SetStatus(gs.Deleted) diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 6b42a669..67f8e136 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -3042,8 +3042,8 @@ func (c *ConfigurationBean) NewBean() *gs.UnregisteredBean { func TestConfiguration(t *testing.T) { c := gs_core.New() - c.Object(&ConfigurationBean{"123"}).Configuration(nil, []string{"NewBean"}).Name("123") - c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration(nil, nil).Name("456") + c.Object(&ConfigurationBean{"123"}).Configuration(gs.ConfigurationParam{Exclude: []string{"NewBean"}}).Name("123") + c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration().Name("456") ctx := &gs_core.ContextAware{} c.Object(ctx) if err := c.Refresh(); err != nil { diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 968579d2..8a4b6274 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -134,7 +134,7 @@ func (s *wiringStack) sortDestroyers() []func() { var ret []func() for e := destroyers.Front(); e != nil; e = e.Next() { d := e.Value.(*destroyer).current - ret = append(ret, destroy(d.Value(), d.GetDestroy())) + ret = append(ret, destroy(d.Value(), d.Destroy())) } return ret } @@ -534,7 +534,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac }() // 记录注入路径上的销毁函数及其执行的先后顺序。 - if _, ok := b.Interface().(gs.BeanDestroy); ok || b.GetDestroy() != nil { + if _, ok := b.Interface().(gs.BeanDestroy); ok || b.Destroy() != nil { haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { @@ -560,7 +560,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac b.SetStatus(gs.Creating) // 对当前 bean 的间接依赖项进行注入。 - for _, s := range b.GetDepends() { + for _, s := range b.Depends() { beans, err := c.Find(s) if err != nil { return err @@ -581,7 +581,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac b.SetStatus(gs.Created) t := v.Type() - for _, typ := range b.GetExports() { + for _, typ := range b.Exports() { if !t.Implements(typ) { return fmt.Errorf("%s doesn't implement interface %s", b, typ) } @@ -593,8 +593,8 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac } // 如果 bean 有初始化函数,则执行其初始化函数。 - if b.GetInit() != nil { - fnValue := reflect.ValueOf(b.GetInit()) + if b.Init() != nil { + fnValue := reflect.ValueOf(b.Init()) out := fnValue.Call([]reflect.Value{b.Value()}) if len(out) > 0 && !out[0].IsNil() { return out[0].Interface().(error) @@ -609,9 +609,9 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac } // 如果 bean 实现了 dync.Refreshable 接口,则将 bean 添加到可刷新对象列表中。 - if b.IsRefreshEnable() { + if b.EnableRefresh() { i := b.Interface().(gs.Refreshable) - refreshParam := b.GetRefreshParam() + refreshParam := b.RefreshParam() watch := c.state == Refreshing if err = c.p.RefreshBean(i, refreshParam, watch); err != nil { return err From d412a3257285a62c9df9d01ebe5196ae25f064c5 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Fri, 10 Jan 2025 22:44:12 +0800 Subject: [PATCH 66/75] code refactor --- gs/gs.go | 61 +++++++++++++++++++++++++++----------- gs/internal/gs_app/app.go | 8 ++--- gs/internal/gs_app/boot.go | 53 +++++++-------------------------- 3 files changed, 59 insertions(+), 63 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index eb56ff4a..44ab4292 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -50,8 +50,8 @@ type ( Context = gs.Context ContextAware = gs_core.ContextAware Dync[T any] = gs_dync.Value[T] - Runner = gs_app.Runner - Server = gs_app.Server + IRunner = gs_app.IRunner + IServer = gs_app.IServer ) /************************************ arg ***********************************/ @@ -192,14 +192,41 @@ func bootRun() error { return nil } -// Boot 参考 App.Boot 的解释。 -func Boot() *gs_app.Boot { +func getBoot() *gs_app.Boot { if boot == nil { boot = gs_app.NewBoot() } return boot } +func BootConfig() *gs_conf.BootConfig { + return getBoot().P +} + +func BootObject(i interface{}) *gs.RegisteredBean { + b := gs_core.NewBean(reflect.ValueOf(i)) + return getBoot().C.Accept(b) +} + +func BootProvide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { + b := gs_core.NewBean(ctor, args...) + return getBoot().C.Accept(b) +} + +func BootAccept(bd *gs.UnregisteredBean) *gs.RegisteredBean { + return getBoot().C.Accept(bd) +} + +func BootGroup(fn func(p gs.Properties) ([]*gs.UnregisteredBean, error)) { + getBoot().C.Group(fn) +} + +func BootRunner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.RegisteredBean { + bd := gs_core.NewBean(objOrCtor, ctorArgs...) + bd.Export((*IRunner)(nil)) + return getBoot().C.Accept(bd) +} + /*********************************** app *************************************/ var app = gs_app.NewApp() @@ -228,22 +255,10 @@ func ShutDown(msg ...string) { app.ShutDown(msg...) } -func AppConfig() *gs_conf.AppConfig { +func Config() *gs_conf.AppConfig { return app.P } -func AppRunner(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { - b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*gs_app.Runner)(nil)) - return app.C.Accept(b) -} - -func AppServer(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { - b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*gs_app.Server)(nil)) - return app.C.Accept(b) -} - // Object 参考 Container.Object 的解释。 func Object(i interface{}) *RegisteredBean { b := gs_core.NewBean(reflect.ValueOf(i)) @@ -265,6 +280,18 @@ func Group(fn func(p Properties) ([]*UnregisteredBean, error)) { app.C.Group(fn) } +func Runner(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { + b := gs_core.NewBean(objOrCtor, ctorArgs...) + b.Export((*IRunner)(nil)) + return app.C.Accept(b) +} + +func Server(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { + b := gs_core.NewBean(objOrCtor, ctorArgs...) + b.Export((*IServer)(nil)) + return app.C.Accept(b) +} + func RefreshProperties(p Properties) error { return app.C.RefreshProperties(p) } diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 67f40f5c..2f163d3c 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -28,11 +28,11 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" ) -type Runner interface { +type IRunner interface { Run(ctx gs.Context) } -type Server interface { +type IServer interface { OnAppStart(ctx gs.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -44,8 +44,8 @@ type App struct { exitChan chan struct{} - Runners []Runner `autowire:"${spring.app.runners:=*?}"` - Servers []Server `autowire:"${spring.app.servers:=*?}"` + Runners []IRunner `autowire:"${spring.app.runners:=*?}"` + Servers []IServer `autowire:"${spring.app.servers:=*?}"` } // NewApp application 的构造函数 diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index fc84294c..0dafc4e7 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -17,80 +17,49 @@ package gs_app import ( - "reflect" - "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" ) type Boot struct { - c gs.Container - p *gs_conf.BootConfig + C gs.Container + P *gs_conf.BootConfig - Runners []Runner `autowire:"${spring.boot.runners:=*?}"` + Runners []IRunner `autowire:"${spring.boot.runners:=*?}"` } func NewBoot() *Boot { b := &Boot{ - c: gs_core.New(), - p: gs_conf.NewBootConfig(), + C: gs_core.New(), + P: gs_conf.NewBootConfig(), } - b.c.Object(b) + b.C.Object(b) return b } -func (b *Boot) Config() *gs_conf.BootConfig { - return b.p -} - -func (b *Boot) Runner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.RegisteredBean { - bd := gs_core.NewBean(objOrCtor, ctorArgs...) - bd.Export((*Runner)(nil)) - return b.c.Accept(bd) -} - -// Object 参考 Container.Object 的解释。 -func (b *Boot) Object(i interface{}) *gs.RegisteredBean { - return b.c.Accept(gs_core.NewBean(reflect.ValueOf(i))) -} - -// Provide 参考 Container.Provide 的解释。 -func (b *Boot) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { - return b.c.Accept(gs_core.NewBean(ctor, args...)) -} - -// Accept 参考 Container.Accept 的解释。 -func (b *Boot) Accept(bd *gs.UnregisteredBean) *gs.RegisteredBean { - return b.c.Accept(bd) -} - -func (b *Boot) Group(fn func(p gs.Properties) ([]*gs.UnregisteredBean, error)) { - b.c.Group(fn) -} - func (b *Boot) Run() error { - p, err := b.p.Refresh() + p, err := b.P.Refresh() if err != nil { return err } - err = b.c.RefreshProperties(p) + err = b.C.RefreshProperties(p) if err != nil { return err } - err = b.c.Refresh() + err = b.C.Refresh() if err != nil { return err } // 执行命令行启动器 for _, r := range b.Runners { - r.Run(b.c.(gs.Context)) + r.Run(b.C.(gs.Context)) } - b.c.Close() + b.C.Close() return nil } From a3776e1782d2242c0adb3ff50fb11d16a8b0449c Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 11 Jan 2025 08:50:14 +0800 Subject: [PATCH 67/75] code refactor --- gs/gs.go | 18 ++++---- gs/internal/gs/gs.go | 48 +++++++++++++------- gs/internal/gs_app/app.go | 8 ++-- gs/internal/gs_app/boot.go | 2 +- gs/internal/gs_arg/arg.go | 3 +- gs/internal/{gs => gs_bean}/bean.go | 69 +++++++++++++---------------- gs/internal/gs_cond/cond_mock.go | 5 ++- gs/internal/gs_cond/cond_test.go | 13 +++--- gs/internal/gs_core/bean.go | 3 +- gs/internal/gs_core/bean_test.go | 15 ++++--- gs/internal/gs_core/core.go | 67 ++++++++++++++-------------- gs/internal/gs_core/core_test.go | 27 +++++------ gs/internal/gs_core/wire.go | 65 ++++++++++++++------------- 13 files changed, 181 insertions(+), 162 deletions(-) rename gs/internal/{gs => gs_bean}/bean.go (78%) diff --git a/gs/gs.go b/gs/gs.go index 44ab4292..d449a7c6 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -24,6 +24,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_app" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_conf" "github.com/go-spring/spring-core/gs/internal/gs_core" @@ -38,20 +39,19 @@ const ( type ( Arg = gs.Arg - BeanDefinition = gs.BeanDefinition - BeanInit = gs.BeanInit - BeanDestroy = gs.BeanDestroy + BeanSelector = gs.BeanSelector + BeanInit = gs_bean.BeanInit + BeanDestroy = gs_bean.BeanDestroy RegisteredBean = gs.RegisteredBean UnregisteredBean = gs.UnregisteredBean - BeanSelector = gs.BeanSelector CondContext = gs.CondContext Condition = gs.Condition Properties = gs.Properties Context = gs.Context ContextAware = gs_core.ContextAware Dync[T any] = gs_dync.Value[T] - IRunner = gs_app.IRunner - IServer = gs_app.IServer + AppRunner = gs_app.AppRunner + AppServer = gs_app.AppServer ) /************************************ arg ***********************************/ @@ -223,7 +223,7 @@ func BootGroup(fn func(p gs.Properties) ([]*gs.UnregisteredBean, error)) { func BootRunner(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.RegisteredBean { bd := gs_core.NewBean(objOrCtor, ctorArgs...) - bd.Export((*IRunner)(nil)) + bd.Export((*AppRunner)(nil)) return getBoot().C.Accept(bd) } @@ -282,13 +282,13 @@ func Group(fn func(p Properties) ([]*UnregisteredBean, error)) { func Runner(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*IRunner)(nil)) + b.Export((*AppRunner)(nil)) return app.C.Accept(b) } func Server(objOrCtor interface{}, ctorArgs ...Arg) *RegisteredBean { b := gs_core.NewBean(objOrCtor, ctorArgs...) - b.Export((*IServer)(nil)) + b.Export((*AppServer)(nil)) return app.C.Accept(b) } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index bbfc634f..3b94ec4f 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -20,7 +20,7 @@ type CondContext interface { // returns empty string when the IoC container doesn't have it. Prop(key string, opts ...conf.GetOption) string // Find returns bean definitions that matched with the bean selector. - Find(selector BeanSelector) ([]*BeanDefinition, error) + Find(selector BeanSelector) ([]BeanDefinition, error) } // Condition is used when registering a bean to determine whether it's valid. @@ -67,64 +67,82 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } -type beanBuilder[T RegisteredBean | UnregisteredBean] struct { - b *BeanDefinition +type ConfigurationParam struct { + Enable bool // 是否扫描成员方法 + Include []string // 包含哪些成员方法 + Exclude []string // 排除那些成员方法 } -func (d *beanBuilder[T]) BeanDefinition() *BeanDefinition { +type BeanDefinition interface { + SetName(name string) + SetOn(cond Condition) + SetDependsOn(selectors ...BeanSelector) + SetPrimary() + SetInit(fn interface{}) + SetDestroy(fn interface{}) + SetExport(exports ...interface{}) + SetConfiguration(param ...ConfigurationParam) + SetEnableRefresh(tag string) +} + +type beanBuilder[T any] struct { + b BeanDefinition +} + +func (d *beanBuilder[T]) BeanDefinition() BeanDefinition { return d.b } func (d *beanBuilder[T]) Name(name string) *T { - d.b.setName(name) + d.b.SetName(name) return *(**T)(unsafe.Pointer(&d)) } // On 设置 bean 的 Condition。 func (d *beanBuilder[T]) On(cond Condition) *T { - d.b.setOn(cond) + d.b.SetOn(cond) return *(**T)(unsafe.Pointer(&d)) } // DependsOn 设置 bean 的间接依赖项。 func (d *beanBuilder[T]) DependsOn(selectors ...BeanSelector) *T { - d.b.setDependsOn(selectors...) + d.b.SetDependsOn(selectors...) return *(**T)(unsafe.Pointer(&d)) } // Primary 设置 bean 为主版本。 func (d *beanBuilder[T]) Primary() *T { - d.b.setPrimary() + d.b.SetPrimary() return *(**T)(unsafe.Pointer(&d)) } // Init 设置 bean 的初始化函数。 func (d *beanBuilder[T]) Init(fn interface{}) *T { - d.b.setInit(fn) + d.b.SetInit(fn) return *(**T)(unsafe.Pointer(&d)) } // Destroy 设置 bean 的销毁函数。 func (d *beanBuilder[T]) Destroy(fn interface{}) *T { - d.b.setDestroy(fn) + d.b.SetDestroy(fn) return *(**T)(unsafe.Pointer(&d)) } // Export 设置 bean 的导出接口。 func (d *beanBuilder[T]) Export(exports ...interface{}) *T { - d.b.setExport(exports...) + d.b.SetExport(exports...) return *(**T)(unsafe.Pointer(&d)) } // Configuration 设置 bean 为配置类。 func (d *beanBuilder[T]) Configuration(param ...ConfigurationParam) *T { - d.b.setConfiguration(param...) + d.b.SetConfiguration(param...) return *(**T)(unsafe.Pointer(&d)) } // EnableRefresh 设置 bean 为可刷新的。 func (d *beanBuilder[T]) EnableRefresh(tag string) *T { - d.b.setEnableRefresh(tag) + d.b.SetEnableRefresh(tag) return *(**T)(unsafe.Pointer(&d)) } @@ -132,7 +150,7 @@ type RegisteredBean struct { beanBuilder[RegisteredBean] } -func NewRegisteredBean(d *BeanDefinition) *RegisteredBean { +func NewRegisteredBean(d BeanDefinition) *RegisteredBean { return &RegisteredBean{ beanBuilder: beanBuilder[RegisteredBean]{d}, } @@ -142,7 +160,7 @@ type UnregisteredBean struct { beanBuilder[UnregisteredBean] } -func NewUnregisteredBean(d *BeanDefinition) *UnregisteredBean { +func NewUnregisteredBean(d BeanDefinition) *UnregisteredBean { return &UnregisteredBean{ beanBuilder: beanBuilder[UnregisteredBean]{d}, } diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 2f163d3c..7e83e5be 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -28,11 +28,11 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs_core" ) -type IRunner interface { +type AppRunner interface { Run(ctx gs.Context) } -type IServer interface { +type AppServer interface { OnAppStart(ctx gs.Context) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } @@ -44,8 +44,8 @@ type App struct { exitChan chan struct{} - Runners []IRunner `autowire:"${spring.app.runners:=*?}"` - Servers []IServer `autowire:"${spring.app.servers:=*?}"` + Runners []AppRunner `autowire:"${spring.app.runners:=*?}"` + Servers []AppServer `autowire:"${spring.app.servers:=*?}"` } // NewApp application 的构造函数 diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 0dafc4e7..1089391a 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -26,7 +26,7 @@ type Boot struct { C gs.Container P *gs_conf.BootConfig - Runners []IRunner `autowire:"${spring.boot.runners:=*?}"` + Runners []AppRunner `autowire:"${spring.boot.runners:=*?}"` } func NewBoot() *Boot { diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 19c8fe80..dd55c370 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -26,6 +26,7 @@ import ( "runtime" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/macro" @@ -226,7 +227,7 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return reflect.ValueOf(g.v), nil case *OptionArg: return g.call(ctx) - case *gs.BeanDefinition: + case *gs_bean.BeanDefinition: tag = g.ID() case string: tag = g diff --git a/gs/internal/gs/bean.go b/gs/internal/gs_bean/bean.go similarity index 78% rename from gs/internal/gs/bean.go rename to gs/internal/gs_bean/bean.go index 75db184d..95ced140 100644 --- a/gs/internal/gs/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -1,4 +1,4 @@ -package gs +package gs_bean import ( "errors" @@ -7,10 +7,11 @@ import ( "runtime" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/util" ) -var refreshableType = reflect.TypeFor[Refreshable]() +var refreshableType = reflect.TypeFor[gs.Refreshable]() type BeanStatus int8 @@ -47,7 +48,7 @@ func GetStatusString(status BeanStatus) string { } type BeanInit interface { - OnInit(ctx Context) error + OnInit(ctx gs.Context) error } type BeanDestroy interface { @@ -55,18 +56,18 @@ type BeanDestroy interface { } type BeanMetadata struct { - f Callable // 构造函数 - method bool // 是否为成员方法 - cond Condition // 判断条件 - init interface{} // 初始化函数 - destroy interface{} // 销毁函数 - depends []BeanSelector // 间接依赖项 - exports []reflect.Type // 导出的接口 - file string // 注册点所在文件 - line int // 注册点所在行数 - status BeanStatus // 状态 - - configuration ConfigurationParam + f gs.Callable // 构造函数 + method bool // 是否为成员方法 + cond gs.Condition // 判断条件 + init interface{} // 初始化函数 + destroy interface{} // 销毁函数 + depends []gs.BeanSelector // 间接依赖项 + exports []reflect.Type // 导出的接口 + file string // 注册点所在文件 + line int // 注册点所在行数 + status BeanStatus // 状态 + + configuration gs.ConfigurationParam enableRefresh bool refreshParam conf.BindParam @@ -80,7 +81,7 @@ func (d *BeanMetadata) IsMethod() bool { return d.method } -func (d *BeanMetadata) Cond() Condition { +func (d *BeanMetadata) Cond() gs.Condition { return d.cond } @@ -92,7 +93,7 @@ func (d *BeanMetadata) Destroy() interface{} { return d.destroy } -func (d *BeanMetadata) Depends() []BeanSelector { +func (d *BeanMetadata) Depends() []gs.BeanSelector { return d.depends } @@ -164,7 +165,7 @@ func (d *BeanRuntime) TypeName() string { return d.typeName } -func (d *BeanRuntime) Callable() Callable { +func (d *BeanRuntime) Callable() gs.Callable { return nil } @@ -214,7 +215,7 @@ type BeanDefinition struct { *BeanRuntime } -func (d *BeanDefinition) Callable() Callable { +func (d *BeanDefinition) Callable() gs.Callable { return d.f } @@ -227,26 +228,26 @@ func (d *BeanDefinition) String() string { } // Name 设置 bean 的名称。 -func (d *BeanDefinition) setName(name string) { +func (d *BeanDefinition) SetName(name string) { d.name = name } -func (d *BeanDefinition) setCaller(skip int) { +func (d *BeanDefinition) SetCaller(skip int) { _, d.file, d.line, _ = runtime.Caller(skip) } // On 设置 bean 的 Condition。 -func (d *BeanDefinition) setOn(cond Condition) { +func (d *BeanDefinition) SetOn(cond gs.Condition) { d.cond = cond } // DependsOn 设置 bean 的间接依赖项。 -func (d *BeanDefinition) setDependsOn(selectors ...BeanSelector) { +func (d *BeanDefinition) SetDependsOn(selectors ...gs.BeanSelector) { d.depends = append(d.depends, selectors...) } // Primary 设置 bean 为主版本。 -func (d *BeanDefinition) setPrimary() { +func (d *BeanDefinition) SetPrimary() { d.primary = true } @@ -263,7 +264,7 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { } // Init 设置 bean 的初始化函数。 -func (d *BeanDefinition) setInit(fn interface{}) { +func (d *BeanDefinition) SetInit(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.init = fn return @@ -272,7 +273,7 @@ func (d *BeanDefinition) setInit(fn interface{}) { } // Destroy 设置 bean 的销毁函数。 -func (d *BeanDefinition) setDestroy(fn interface{}) { +func (d *BeanDefinition) SetDestroy(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.destroy = fn return @@ -281,7 +282,7 @@ func (d *BeanDefinition) setDestroy(fn interface{}) { } // Export 设置 bean 的导出接口。 -func (d *BeanDefinition) setExport(exports ...interface{}) { +func (d *BeanDefinition) SetExport(exports ...interface{}) { for _, o := range exports { t, ok := o.(reflect.Type) if !ok { @@ -307,22 +308,14 @@ func (d *BeanDefinition) setExport(exports ...interface{}) { } } -type ConfigurationParam struct { - Enable bool // 是否扫描成员方法 - Include []string // 包含哪些成员方法 - Exclude []string // 排除那些成员方法 -} - -// Configuration 设置 bean 为配置类。 -func (d *BeanDefinition) setConfiguration(param ...ConfigurationParam) { +func (d *BeanDefinition) SetConfiguration(param ...gs.ConfigurationParam) { if len(param) > 0 { d.configuration = param[0] } d.configuration.Enable = true } -// EnableRefresh 设置 bean 为可刷新的。 -func (d *BeanDefinition) setEnableRefresh(tag string) { +func (d *BeanDefinition) SetEnableRefresh(tag string) { if !d.Type().Implements(refreshableType) { panic(errors.New("must implement dync.Refreshable interface")) } @@ -334,7 +327,7 @@ func (d *BeanDefinition) setEnableRefresh(tag string) { } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(t reflect.Type, v reflect.Value, f Callable, name string, method bool, file string, line int) *BeanDefinition { +func NewBean(t reflect.Type, v reflect.Value, f gs.Callable, name string, method bool, file string, line int) *BeanDefinition { return &BeanDefinition{ BeanMetadata: &BeanMetadata{ f: f, diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index cf412253..4f5e92a0 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -14,6 +14,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "go.uber.org/mock/gomock" ) @@ -42,10 +43,10 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { +func (m *MockContext) Find(selector gs.BeanSelector) ([]*gs_bean.BeanDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]*gs.BeanDefinition) + ret0, _ := ret[0].([]*gs_bean.BeanDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/gs/internal/gs_cond/cond_test.go b/gs/internal/gs_cond/cond_test.go index 9cca0f96..efd1f3ca 100644 --- a/gs/internal/gs_cond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/util/assert" "go.uber.org/mock/gomock" @@ -238,7 +239,7 @@ func TestOnBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, }, nil) ok, err := gs_cond.OnBean("a").Matches(ctx) @@ -249,7 +250,7 @@ func TestOnBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, nil, }, nil) ok, err := gs_cond.OnBean("a").Matches(ctx) @@ -281,7 +282,7 @@ func TestOnMissingBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, }, nil) ok, err := gs_cond.OnMissingBean("a").Matches(ctx) @@ -292,7 +293,7 @@ func TestOnMissingBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, nil, }, nil) ok, err := gs_cond.OnMissingBean("a").Matches(ctx) @@ -324,7 +325,7 @@ func TestOnSingleBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, }, nil) ok, err := gs_cond.OnSingleBean("a").Matches(ctx) @@ -335,7 +336,7 @@ func TestOnSingleBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ nil, nil, }, nil) ok, err := gs_cond.OnSingleBean("a").Matches(ctx) diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index 5c4afc99..1c82bb5d 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -25,6 +25,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/util" ) @@ -106,6 +107,6 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { name = strings.TrimPrefix(s[len(s)-1], "*") } - d := gs.NewBean(t, v, f, name, method, file, line) + d := gs_bean.NewBean(t, v, f, name, method, file, line) return gs.NewUnregisteredBean(d) } diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index 5ced49f3..6ffbf2df 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_core" pkg1 "github.com/go-spring/spring-core/gs/testdata/pkg/bar" pkg2 "github.com/go-spring/spring-core/gs/testdata/pkg/foo" @@ -128,7 +129,7 @@ func TestBeanDefinition_Match(t *testing.T) { } for i, s := range data { - if ok := s.bd.BeanDefinition().Match(s.typeName, s.beanName); ok != s.expect { + if ok := s.bd.BeanDefinition().(*gs_bean.BeanDefinition).Match(s.typeName, s.beanName); ok != s.expect { t.Errorf("%d expect %v but %v", i, s.expect, ok) } } @@ -185,8 +186,8 @@ func TestObjectBean(t *testing.T) { } for bd, v := range data { - assert.Equal(t, bd.BeanDefinition().Name(), v.name) - assert.Equal(t, bd.BeanDefinition().TypeName(), v.typeName) + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), v.name) + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).TypeName(), v.typeName) } }) } @@ -194,10 +195,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.BeanDefinition().Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.BeanDefinition().Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -209,11 +210,11 @@ func TestConstructorBean(t *testing.T) { funcFn := func() func(int) { return nil } bd = newBean(funcFn) - assert.Equal(t, bd.BeanDefinition().Type().String(), "func(int)") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "func(int)") interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.BeanDefinition().Type().String(), "gs_core_test.Teacher") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "gs_core_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 295b7e7f..4f51ce45 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -30,6 +30,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_dync" "github.com/go-spring/spring-core/gs/syslog" @@ -57,7 +58,7 @@ type ContextAware struct { type SimpleBean interface { Callable() gs.Callable Name() string - Status() gs.BeanStatus + Status() gs_bean.BeanStatus Interface() interface{} IsPrimary() bool Match(typeName string, beanName string) bool @@ -74,7 +75,7 @@ type SimpleBean interface { // go-spring 严格区分了这两种概念,在描述对 bean 的处理时要么单独使用依赖注入或属 // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { - beans []*gs.BeanDefinition + beans []*gs_bean.BeanDefinition beansByName map[string][]SimpleBean beansByType map[reflect.Type][]SimpleBean groupFuncs []GroupFunc @@ -120,7 +121,7 @@ func (c *Container) Accept(b *gs.UnregisteredBean) *gs.RegisteredBean { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } - c.beans = append(c.beans, b.BeanDefinition()) + c.beans = append(c.beans, b.BeanDefinition().(*gs_bean.BeanDefinition)) return gs.NewRegisteredBean(b.BeanDefinition()) } @@ -174,7 +175,7 @@ func (c *Container) Refresh() (err error) { return err } for _, b := range beans { - c.beans = append(c.beans, b.BeanDefinition()) + c.beans = append(c.beans, b.BeanDefinition().(*gs_bean.BeanDefinition)) } } c.groupFuncs = nil @@ -184,7 +185,7 @@ func (c *Container) Refresh() (err error) { if !bd.IsConfiguration() { continue } - var newBeans []*gs.BeanDefinition + var newBeans []*gs_bean.BeanDefinition newBeans, err = c.scanConfiguration(bd) if err != nil { return err @@ -204,13 +205,13 @@ func (c *Container) Refresh() (err error) { } } - beansById := make(map[string]*gs.BeanDefinition) + beansById := make(map[string]*gs_bean.BeanDefinition) { for _, b := range c.beans { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } - if b.Status() != gs.Resolved { + if b.Status() != gs_bean.Resolved { return fmt.Errorf("unexpected status %d", b.Status()) } beanID := b.ID() @@ -261,7 +262,7 @@ func (c *Container) Refresh() (err error) { c.beansByName = make(map[string][]SimpleBean) c.beansByType = make(map[reflect.Type][]SimpleBean) for _, b := range c.beans { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b.BeanRuntime) @@ -293,7 +294,7 @@ func (c *Container) SimplifyMemory() { } -func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefinition, error) { +func (c *Container) scanConfiguration(bd *gs_bean.BeanDefinition) ([]*gs_bean.BeanDefinition, error) { var ( includes []*regexp.Regexp excludes []*regexp.Regexp @@ -319,7 +320,7 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } excludes = append(excludes, x) } - var newBeans []*gs.BeanDefinition + var newBeans []*gs_bean.BeanDefinition n := bd.Type().NumMethod() for i := 0; i < n; i++ { m := bd.Type().Method(i) @@ -347,8 +348,8 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti } } b := ret[0].Interface().(*gs.UnregisteredBean) - newBeans = append(newBeans, b.BeanDefinition()) - retBeans, err := c.scanConfiguration(b.BeanDefinition()) + newBeans = append(newBeans, b.BeanDefinition().(*gs_bean.BeanDefinition)) + retBeans, err := c.scanConfiguration(b.BeanDefinition().(*gs_bean.BeanDefinition)) if err != nil { return nil, err } @@ -364,7 +365,7 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti v = v.Elem() } name := bd.Name() + "_" + m.Name - b := gs.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()) + b := gs_bean.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()) gs.NewUnregisteredBean(b).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) } @@ -374,7 +375,7 @@ func (c *Container) scanConfiguration(bd *gs.BeanDefinition) ([]*gs.BeanDefiniti return newBeans, nil } -func (c *Container) registerBean(b *gs.BeanDefinition) { +func (c *Container) registerBean(b *gs_bean.BeanDefinition) { syslog.Debug("register %s name:%q type:%q %s", b.Class(), b.Name(), b.Type(), b.FileLine()) c.beansByName[b.Name()] = append(c.beansByName[b.Name()], b) c.beansByType[b.Type()] = append(c.beansByType[b.Type()], b) @@ -385,13 +386,13 @@ func (c *Container) registerBean(b *gs.BeanDefinition) { } // resolveBean 判断 bean 的有效性,如果 bean 是无效的则被标记为已删除。 -func (c *Container) resolveBean(b *gs.BeanDefinition) error { +func (c *Container) resolveBean(b *gs_bean.BeanDefinition) error { - if b.Status() >= gs.Resolving { + if b.Status() >= gs_bean.Resolving { return nil } - b.SetStatus(gs.Resolving) + b.SetStatus(gs_bean.Resolving) // method bean 先确定 parent bean 是否存在 if b.IsMethod() { @@ -407,12 +408,12 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { if n > 1 { msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.Type().In(0)) for _, b := range parents { - msg += "( " + b.String() + " ), " + msg += "( " + b.(*gs_bean.BeanDefinition).String() + " ), " } msg = msg[:len(msg)-2] + "]" return errors.New(msg) } else if n == 0 { - b.SetStatus(gs.Deleted) + b.SetStatus(gs_bean.Deleted) return nil } } @@ -421,29 +422,29 @@ func (c *Container) resolveBean(b *gs.BeanDefinition) error { if ok, err := b.Cond().Matches(c); err != nil { return err } else if !ok { - b.SetStatus(gs.Deleted) + b.SetStatus(gs_bean.Deleted) return nil } } - b.SetStatus(gs.Resolved) + b.SetStatus(gs_bean.Resolved) return nil } // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) { +func (c *Container) Find(selector gs.BeanSelector) ([]gs.BeanDefinition, error) { - finder := func(fn func(*gs.BeanDefinition) bool) ([]*gs.BeanDefinition, error) { - var result []*gs.BeanDefinition + finder := func(fn func(*gs_bean.BeanDefinition) bool) ([]gs.BeanDefinition, error) { + var result []gs.BeanDefinition for _, b := range c.beans { - if b.Status() == gs.Resolving || b.Status() == gs.Deleted || !fn(b) { + if b.Status() == gs_bean.Resolving || b.Status() == gs_bean.Deleted || !fn(b) { continue } if err := c.resolveBean(b); err != nil { return nil, err } - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } result = append(result, b) @@ -453,12 +454,12 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) var t reflect.Type switch st := selector.(type) { - case string, *gs.BeanDefinition: + case string, *gs_bean.BeanDefinition: tag, err := c.toWireTag(selector) if err != nil { return nil, err } - return finder(func(b *gs.BeanDefinition) bool { + return finder(func(b *gs_bean.BeanDefinition) bool { return b.Match(tag.typeName, tag.beanName) }) case reflect.Type: @@ -473,7 +474,7 @@ func (c *Container) Find(selector gs.BeanSelector) ([]*gs.BeanDefinition, error) } } - return finder(func(b *gs.BeanDefinition) bool { + return finder(func(b *gs_bean.BeanDefinition) bool { if b.Type() == t { return true } @@ -537,16 +538,16 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} var err error switch c.state { case Refreshing: - err = c.wireBeanInRefreshing(b.BeanDefinition(), stack) + err = c.wireBeanInRefreshing(b.BeanDefinition().(*gs_bean.BeanDefinition), stack) case Refreshed: - err = c.wireBeanAfterRefreshed(b.BeanDefinition(), stack) + err = c.wireBeanAfterRefreshed(b.BeanDefinition().(*gs_bean.BeanDefinition), stack) default: err = errors.New("state is error for wiring") } if err != nil { return nil, err } - return b.BeanDefinition().Interface(), nil + return b.BeanDefinition().(*gs_bean.BeanDefinition).Interface(), nil } // Invoke 调用函数,函数的参数会自动注入,函数的返回值也会自动注入。 diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 67f8e136..e778cd54 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -31,6 +31,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/gs/internal/gs_core" "github.com/go-spring/spring-core/gs/internal/gs_dync" @@ -889,7 +890,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanDefinition().Name(), "NewManager") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1186,7 +1187,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanDefinition().Name(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1436,7 +1437,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent.BeanDefinition().ID()) + bd := c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1454,7 +1455,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().Name(), "Consumer") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1477,7 +1478,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().Name(), "Consumer") + assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1486,7 +1487,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent.BeanDefinition().ID(), "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID(), "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1512,7 +1513,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.BeanDefinition().ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1566,7 +1567,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.BeanDefinition().ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2022,7 +2023,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().Interface().(*callDestroy) + d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface().(*callDestroy) assert.True(t, d.destroyed) }) @@ -2039,7 +2040,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().Interface() + d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface() assert.False(t, d.(*callDestroy).destroyed) } @@ -2059,7 +2060,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().Interface() + d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface() assert.True(t, d.(*callDestroy).destroyed) } }) @@ -2467,7 +2468,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.BeanDefinition().ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -2898,7 +2899,7 @@ func TestContextAware(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() assert.Nil(t, err) - a := b.BeanDefinition().Interface().(*ContextAware) + a := b.BeanDefinition().(*gs_bean.BeanDefinition).Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 8a4b6274..9166a50c 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -11,6 +11,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" ) @@ -27,11 +28,11 @@ type lazyField struct { // destroyer 保存具有销毁函数的 bean 以及销毁函数的调用顺序。 type destroyer struct { - current *gs.BeanDefinition - earlier []*gs.BeanDefinition + current *gs_bean.BeanDefinition + earlier []*gs_bean.BeanDefinition } -func (d *destroyer) foundEarlier(b *gs.BeanDefinition) bool { +func (d *destroyer) foundEarlier(b *gs_bean.BeanDefinition) bool { for _, c := range d.earlier { if c == b { return true @@ -41,7 +42,7 @@ func (d *destroyer) foundEarlier(b *gs.BeanDefinition) bool { } // after 添加一个需要在该 bean 的销毁函数执行之前调用销毁函数的 bean 。 -func (d *destroyer) after(b *gs.BeanDefinition) { +func (d *destroyer) after(b *gs_bean.BeanDefinition) { if d.foundEarlier(b) { return } @@ -65,7 +66,7 @@ func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { type wiringStack struct { destroyers *list.List destroyerMap map[string]*destroyer - beans []*gs.BeanDefinition + beans []*gs_bean.BeanDefinition lazyFields []lazyField } @@ -77,8 +78,8 @@ func newWiringStack() *wiringStack { } // pushBack 添加一个即将注入的 bean 。 -func (s *wiringStack) pushBack(b *gs.BeanDefinition) { - syslog.Debug("push %s %s", b, gs.GetStatusString(b.Status())) +func (s *wiringStack) pushBack(b *gs_bean.BeanDefinition) { + syslog.Debug("push %s %s", b, gs_bean.GetStatusString(b.Status())) s.beans = append(s.beans, b) } @@ -87,7 +88,7 @@ func (s *wiringStack) popBack() { n := len(s.beans) b := s.beans[n-1] s.beans = s.beans[:n-1] - syslog.Debug("pop %s %s", b, gs.GetStatusString(b.Status())) + syslog.Debug("pop %s %s", b, gs_bean.GetStatusString(b.Status())) } // path 返回 bean 的注入路径。 @@ -99,7 +100,7 @@ func (s *wiringStack) path() (path string) { } // saveDestroyer 记录具有销毁函数的 bean ,因为可能有多个依赖,因此需要排重处理。 -func (s *wiringStack) saveDestroyer(b *gs.BeanDefinition) *destroyer { +func (s *wiringStack) saveDestroyer(b *gs_bean.BeanDefinition) *destroyer { d, ok := s.destroyerMap[b.ID()] if !ok { d = &destroyer{current: b} @@ -114,7 +115,7 @@ func (s *wiringStack) sortDestroyers() []func() { destroy := func(v reflect.Value, fn interface{}) func() { return func() { if fn == nil { - v.Interface().(gs.BeanDestroy).OnDestroy() + v.Interface().(gs_bean.BeanDestroy).OnDestroy() } else { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) @@ -214,9 +215,9 @@ func (c *Container) toWireTag(selector gs.BeanSelector) (wireTag, error) { return wireTag{}, err } return parseWireTag(s), nil - case gs.BeanDefinition: + case gs_bean.BeanDefinition: return parseWireTag(s.ID()), nil - case *gs.BeanDefinition: + case *gs_bean.BeanDefinition: return parseWireTag(s.ID()), nil default: return parseWireTag(util.TypeName(s) + ":"), nil @@ -298,7 +299,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, { var arr []SimpleBean for _, b := range beans { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } arr = append(arr, b) @@ -371,11 +372,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, for _, b := range beans { switch c.state { case Refreshing: - if err := c.wireBeanInRefreshing(b.(*gs.BeanDefinition), stack); err != nil { + if err := c.wireBeanInRefreshing(b.(*gs_bean.BeanDefinition), stack); err != nil { return err } case Refreshed: - if err := c.wireBeanAfterRefreshed(b.(*gs.BeanRuntime), stack); err != nil { + if err := c.wireBeanAfterRefreshed(b.(*gs_bean.BeanRuntime), stack); err != nil { return err } default: @@ -416,7 +417,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er var foundBeans []SimpleBean for _, b := range c.beansByType[t] { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } if !b.Match(tag.typeName, tag.beanName) { @@ -428,7 +429,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 指定 bean 名称时通过名称获取,防止未通过 Export 方法导出接口。 if t.Kind() == reflect.Interface && tag.beanName != "" { for _, b := range c.beansByName[tag.beanName] { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { continue } if !b.Type().AssignableTo(t) { @@ -496,7 +497,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // 确保找到的 bean 已经完成依赖注入。 switch c.state { case Refreshing: - if err := c.wireBeanInRefreshing(result.(*gs.BeanDefinition), stack); err != nil { + if err := c.wireBeanInRefreshing(result.(*gs_bean.BeanDefinition), stack); err != nil { return err } case Refreshed: @@ -514,14 +515,14 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er // wireBean 对 bean 进行属性绑定和依赖注入,同时追踪其注入路径。如果 bean 有初始 // 化函数,则在注入完成之后执行其初始化函数。如果 bean 依赖了其他 bean,则首先尝试 // 实例化被依赖的 bean 然后对它们进行注入。 -func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStack) error { +func (c *Container) wireBeanInRefreshing(b *gs_bean.BeanDefinition, stack *wiringStack) error { - if b.Status() == gs.Deleted { + if b.Status() == gs_bean.Deleted { return fmt.Errorf("bean:%q have been deleted", b.ID()) } // 运行时 Get 或者 Wire 会出现下面这种情况。 - if c.state == Refreshed && b.Status() == gs.Wired { + if c.state == Refreshed && b.Status() == gs_bean.Wired { return nil } @@ -534,30 +535,30 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac }() // 记录注入路径上的销毁函数及其执行的先后顺序。 - if _, ok := b.Interface().(gs.BeanDestroy); ok || b.Destroy() != nil { + if _, ok := b.Interface().(gs_bean.BeanDestroy); ok || b.Destroy() != nil { haveDestroy = true d := stack.saveDestroyer(b) if i := stack.destroyers.Back(); i != nil { - d.after(i.Value.(*gs.BeanDefinition)) + d.after(i.Value.(*gs_bean.BeanDefinition)) } stack.destroyers.PushBack(b) } stack.pushBack(b) - if b.Status() == gs.Creating && b.Callable() != nil { + if b.Status() == gs_bean.Creating && b.Callable() != nil { prev := stack.beans[len(stack.beans)-2] - if prev.Status() == gs.Creating { + if prev.Status() == gs_bean.Creating { return errors.New("found circle autowire") } } - if b.Status() >= gs.Creating { + if b.Status() >= gs_bean.Creating { stack.popBack() return nil } - b.SetStatus(gs.Creating) + b.SetStatus(gs_bean.Creating) // 对当前 bean 的间接依赖项进行注入。 for _, s := range b.Depends() { @@ -566,7 +567,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac return err } for _, d := range beans { - err = c.wireBeanInRefreshing(d, stack) + err = c.wireBeanInRefreshing(d.(*gs_bean.BeanDefinition), stack) if err != nil { return err } @@ -578,7 +579,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac return err } - b.SetStatus(gs.Created) + b.SetStatus(gs_bean.Created) t := v.Type() for _, typ := range b.Exports() { @@ -602,7 +603,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac } // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 - if f, ok := b.Interface().(gs.BeanInit); ok { + if f, ok := b.Interface().(gs_bean.BeanInit); ok { if err = f.OnInit(c); err != nil { return err } @@ -618,7 +619,7 @@ func (c *Container) wireBeanInRefreshing(b *gs.BeanDefinition, stack *wiringStac } } - b.SetStatus(gs.Wired) + b.SetStatus(gs_bean.Wired) stack.popBack() return nil } @@ -637,7 +638,7 @@ func (c *Container) wireBeanAfterRefreshed(b SimpleBean, stack *wiringStack) err } // 如果 bean 实现了 BeanInit 接口,则执行其 OnInit 方法。 - if f, ok := b.Interface().(gs.BeanInit); ok { + if f, ok := b.Interface().(gs_bean.BeanInit); ok { if err = f.OnInit(c); err != nil { return err } From d51788c7c056784d6d6b154b153dc4021a1a3f95 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sat, 11 Jan 2025 10:26:41 +0800 Subject: [PATCH 68/75] code refactor --- gs/internal/gs/gs.go | 31 +++++++++++++++++++------------ gs/internal/gs_arg/arg.go | 5 ++--- gs/internal/gs_cond/cond_mock.go | 5 ++--- gs/internal/gs_cond/cond_test.go | 13 ++++++------- gs/internal/gs_core/bean_test.go | 14 +++++++------- gs/internal/gs_core/core.go | 24 ++++++++++++------------ gs/internal/gs_core/core_test.go | 26 +++++++++++++------------- 7 files changed, 61 insertions(+), 57 deletions(-) diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 3b94ec4f..c505f66c 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -12,6 +12,13 @@ import ( // `(*error)(nil)`, or a BeanDefinition value. type BeanSelector interface{} +type CondBean interface { + ID() string + Name() string + TypeName() string + Type() reflect.Type +} + // CondContext defines some methods of IoC container that conditions use. type CondContext interface { // Has returns whether the IoC container has a property. @@ -20,7 +27,7 @@ type CondContext interface { // returns empty string when the IoC container doesn't have it. Prop(key string, opts ...conf.GetOption) string // Find returns bean definitions that matched with the bean selector. - Find(selector BeanSelector) ([]BeanDefinition, error) + Find(selector BeanSelector) ([]CondBean, error) } // Condition is used when registering a bean to determine whether it's valid. @@ -45,12 +52,6 @@ type ArgContext interface { Wire(v reflect.Value, tag string) error } -type Callable interface { - Arg(i int) (Arg, bool) - In(i int) (reflect.Type, bool) - Call(ctx ArgContext) ([]reflect.Value, error) -} - type Properties interface { Data() map[string]string Keys() []string @@ -67,13 +68,19 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } +type Callable interface { + Arg(i int) (Arg, bool) + In(i int) (reflect.Type, bool) + Call(ctx ArgContext) ([]reflect.Value, error) +} + type ConfigurationParam struct { Enable bool // 是否扫描成员方法 Include []string // 包含哪些成员方法 Exclude []string // 排除那些成员方法 } -type BeanDefinition interface { +type BeanRegistration interface { SetName(name string) SetOn(cond Condition) SetDependsOn(selectors ...BeanSelector) @@ -86,10 +93,10 @@ type BeanDefinition interface { } type beanBuilder[T any] struct { - b BeanDefinition + b BeanRegistration } -func (d *beanBuilder[T]) BeanDefinition() BeanDefinition { +func (d *beanBuilder[T]) BeanRegistration() BeanRegistration { return d.b } @@ -150,7 +157,7 @@ type RegisteredBean struct { beanBuilder[RegisteredBean] } -func NewRegisteredBean(d BeanDefinition) *RegisteredBean { +func NewRegisteredBean(d BeanRegistration) *RegisteredBean { return &RegisteredBean{ beanBuilder: beanBuilder[RegisteredBean]{d}, } @@ -160,7 +167,7 @@ type UnregisteredBean struct { beanBuilder[UnregisteredBean] } -func NewUnregisteredBean(d BeanDefinition) *UnregisteredBean { +func NewUnregisteredBean(d BeanRegistration) *UnregisteredBean { return &UnregisteredBean{ beanBuilder: beanBuilder[UnregisteredBean]{d}, } diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index dd55c370..c5c35e76 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -26,7 +26,6 @@ import ( "runtime" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/syslog" "github.com/go-spring/spring-core/util" "github.com/go-spring/spring-core/util/macro" @@ -227,8 +226,8 @@ func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine return reflect.ValueOf(g.v), nil case *OptionArg: return g.call(ctx) - case *gs_bean.BeanDefinition: - tag = g.ID() + // case *gs_bean.BeanDefinition: + // tag = g.ID() case string: tag = g default: diff --git a/gs/internal/gs_cond/cond_mock.go b/gs/internal/gs_cond/cond_mock.go index 4f5e92a0..07285c89 100644 --- a/gs/internal/gs_cond/cond_mock.go +++ b/gs/internal/gs_cond/cond_mock.go @@ -14,7 +14,6 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_bean" "go.uber.org/mock/gomock" ) @@ -43,10 +42,10 @@ func (m *MockContext) EXPECT() *MockContextMockRecorder { } // Find mocks base method. -func (m *MockContext) Find(selector gs.BeanSelector) ([]*gs_bean.BeanDefinition, error) { +func (m *MockContext) Find(selector gs.BeanSelector) ([]gs.CondBean, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Find", selector) - ret0, _ := ret[0].([]*gs_bean.BeanDefinition) + ret0, _ := ret[0].([]gs.CondBean) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/gs/internal/gs_cond/cond_test.go b/gs/internal/gs_cond/cond_test.go index efd1f3ca..44f7fc73 100644 --- a/gs/internal/gs_cond/cond_test.go +++ b/gs/internal/gs_cond/cond_test.go @@ -21,7 +21,6 @@ import ( "testing" "github.com/go-spring/spring-core/gs/internal/gs" - "github.com/go-spring/spring-core/gs/internal/gs_bean" "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/util/assert" "go.uber.org/mock/gomock" @@ -239,7 +238,7 @@ func TestOnBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, }, nil) ok, err := gs_cond.OnBean("a").Matches(ctx) @@ -250,7 +249,7 @@ func TestOnBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, nil, }, nil) ok, err := gs_cond.OnBean("a").Matches(ctx) @@ -282,7 +281,7 @@ func TestOnMissingBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, }, nil) ok, err := gs_cond.OnMissingBean("a").Matches(ctx) @@ -293,7 +292,7 @@ func TestOnMissingBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, nil, }, nil) ok, err := gs_cond.OnMissingBean("a").Matches(ctx) @@ -325,7 +324,7 @@ func TestOnSingleBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, }, nil) ok, err := gs_cond.OnSingleBean("a").Matches(ctx) @@ -336,7 +335,7 @@ func TestOnSingleBean(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := gs_cond.NewMockContext(ctrl) - ctx.EXPECT().Find("a").Return([]*gs_bean.BeanDefinition{ + ctx.EXPECT().Find("a").Return([]gs.CondBean{ nil, nil, }, nil) ok, err := gs_cond.OnSingleBean("a").Matches(ctx) diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index 6ffbf2df..d248fe35 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -129,7 +129,7 @@ func TestBeanDefinition_Match(t *testing.T) { } for i, s := range data { - if ok := s.bd.BeanDefinition().(*gs_bean.BeanDefinition).Match(s.typeName, s.beanName); ok != s.expect { + if ok := s.bd.BeanRegistration().(*gs_bean.BeanDefinition).Match(s.typeName, s.beanName); ok != s.expect { t.Errorf("%d expect %v but %v", i, s.expect, ok) } } @@ -186,8 +186,8 @@ func TestObjectBean(t *testing.T) { } for bd, v := range data { - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), v.name) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).TypeName(), v.typeName) + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), v.name) + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).TypeName(), v.typeName) } }) } @@ -195,10 +195,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -210,11 +210,11 @@ func TestConstructorBean(t *testing.T) { funcFn := func() func(int) { return nil } bd = newBean(funcFn) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "func(int)") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "func(int)") interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Type().String(), "gs_core_test.Teacher") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "gs_core_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 4f51ce45..c836d914 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -121,8 +121,8 @@ func (c *Container) Accept(b *gs.UnregisteredBean) *gs.RegisteredBean { if c.state >= Refreshing { panic(errors.New("should call before Refresh")) } - c.beans = append(c.beans, b.BeanDefinition().(*gs_bean.BeanDefinition)) - return gs.NewRegisteredBean(b.BeanDefinition()) + c.beans = append(c.beans, b.BeanRegistration().(*gs_bean.BeanDefinition)) + return gs.NewRegisteredBean(b.BeanRegistration()) } func (c *Container) Group(fn GroupFunc) { @@ -175,7 +175,7 @@ func (c *Container) Refresh() (err error) { return err } for _, b := range beans { - c.beans = append(c.beans, b.BeanDefinition().(*gs_bean.BeanDefinition)) + c.beans = append(c.beans, b.BeanRegistration().(*gs_bean.BeanDefinition)) } } c.groupFuncs = nil @@ -348,15 +348,15 @@ func (c *Container) scanConfiguration(bd *gs_bean.BeanDefinition) ([]*gs_bean.Be } } b := ret[0].Interface().(*gs.UnregisteredBean) - newBeans = append(newBeans, b.BeanDefinition().(*gs_bean.BeanDefinition)) - retBeans, err := c.scanConfiguration(b.BeanDefinition().(*gs_bean.BeanDefinition)) + newBeans = append(newBeans, b.BeanRegistration().(*gs_bean.BeanDefinition)) + retBeans, err := c.scanConfiguration(b.BeanRegistration().(*gs_bean.BeanDefinition)) if err != nil { return nil, err } newBeans = append(newBeans, retBeans...) } else { var f gs.Callable - f, err := gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd}, 0) + f, err := gs_arg.Bind(m.Func.Interface(), []gs.Arg{bd.ID()}, 0) if err != nil { return nil, err } @@ -433,10 +433,10 @@ func (c *Container) resolveBean(b *gs_bean.BeanDefinition) error { // Find 查找符合条件的 bean 对象,注意该函数只能保证返回的 bean 是有效的, // 即未被标记为删除的,而不能保证已经完成属性绑定和依赖注入。 -func (c *Container) Find(selector gs.BeanSelector) ([]gs.BeanDefinition, error) { +func (c *Container) Find(selector gs.BeanSelector) ([]gs.CondBean, error) { - finder := func(fn func(*gs_bean.BeanDefinition) bool) ([]gs.BeanDefinition, error) { - var result []gs.BeanDefinition + finder := func(fn func(*gs_bean.BeanDefinition) bool) ([]gs.CondBean, error) { + var result []gs.CondBean for _, b := range c.beans { if b.Status() == gs_bean.Resolving || b.Status() == gs_bean.Deleted || !fn(b) { continue @@ -538,16 +538,16 @@ func (c *Container) Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{} var err error switch c.state { case Refreshing: - err = c.wireBeanInRefreshing(b.BeanDefinition().(*gs_bean.BeanDefinition), stack) + err = c.wireBeanInRefreshing(b.BeanRegistration().(*gs_bean.BeanDefinition), stack) case Refreshed: - err = c.wireBeanAfterRefreshed(b.BeanDefinition().(*gs_bean.BeanDefinition), stack) + err = c.wireBeanAfterRefreshed(b.BeanRegistration().(*gs_bean.BeanDefinition), stack) default: err = errors.New("state is error for wiring") } if err != nil { return nil, err } - return b.BeanDefinition().(*gs_bean.BeanDefinition).Interface(), nil + return b.BeanRegistration().(*gs_bean.BeanDefinition).Interface(), nil } // Invoke 调用函数,函数的参数会自动注入,函数的返回值也会自动注入。 diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index e778cd54..7c37e50d 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -890,7 +890,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "NewManager") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "NewManager") err := runTest(c, func(p gs.Context) { @@ -1187,7 +1187,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1437,7 +1437,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()) + bd := c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1455,7 +1455,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "Consumer") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1478,7 +1478,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanDefinition().(*gs_bean.BeanDefinition).Name(), "Consumer") + assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1487,7 +1487,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID(), "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID(), "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1513,7 +1513,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1567,7 +1567,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2023,7 +2023,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface().(*callDestroy) + d := bd.BeanRegistration().(*gs_bean.BeanDefinition).Interface().(*callDestroy) assert.True(t, d.destroyed) }) @@ -2040,7 +2040,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface() + d := bd.BeanRegistration().(*gs_bean.BeanDefinition).Interface() assert.False(t, d.(*callDestroy).destroyed) } @@ -2060,7 +2060,7 @@ func TestApplicationContext_Close(t *testing.T) { }) assert.Nil(t, err) c.Close() - d := bd.BeanDefinition().(*gs_bean.BeanDefinition).Interface() + d := bd.BeanRegistration().(*gs_bean.BeanDefinition).Interface() assert.True(t, d.(*callDestroy).destroyed) } }) @@ -2468,7 +2468,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.BeanDefinition().(*gs_bean.BeanDefinition).ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -2899,7 +2899,7 @@ func TestContextAware(t *testing.T) { c.RefreshProperties(prop) err := c.Refresh() assert.Nil(t, err) - a := b.BeanDefinition().(*gs_bean.BeanDefinition).Interface().(*ContextAware) + a := b.BeanRegistration().(*gs_bean.BeanDefinition).Interface().(*ContextAware) assert.Equal(t, a.Echo("gopher"), "hello gopher!") } From 6340ee808edf3a3c535074b177969b7e4000e060 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 08:08:46 +0800 Subject: [PATCH 69/75] replace method by condition --- gs/internal/gs_bean/bean.go | 15 +++++++-------- gs/internal/gs_core/bean.go | 16 ++++++++++++---- gs/internal/gs_core/core.go | 26 +------------------------- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index 95ced140..e5bdc5ce 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -8,6 +8,7 @@ import ( "github.com/go-spring/spring-core/conf" "github.com/go-spring/spring-core/gs/internal/gs" + "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/util" ) @@ -57,7 +58,6 @@ type BeanDestroy interface { type BeanMetadata struct { f gs.Callable // 构造函数 - method bool // 是否为成员方法 cond gs.Condition // 判断条件 init interface{} // 初始化函数 destroy interface{} // 销毁函数 @@ -77,10 +77,6 @@ func (d *BeanMetadata) SetStatus(status BeanStatus) { d.status = status } -func (d *BeanMetadata) IsMethod() bool { - return d.method -} - func (d *BeanMetadata) Cond() gs.Condition { return d.cond } @@ -238,7 +234,11 @@ func (d *BeanDefinition) SetCaller(skip int) { // On 设置 bean 的 Condition。 func (d *BeanDefinition) SetOn(cond gs.Condition) { - d.cond = cond + if d.cond == nil { + d.cond = cond + } else { + d.cond = gs_cond.And(d.cond, cond) + } } // DependsOn 设置 bean 的间接依赖项。 @@ -327,11 +327,10 @@ func (d *BeanDefinition) SetEnableRefresh(tag string) { } // NewBean 普通函数注册时需要使用 reflect.ValueOf(fn) 形式以避免和构造函数发生冲突。 -func NewBean(t reflect.Type, v reflect.Value, f gs.Callable, name string, method bool, file string, line int) *BeanDefinition { +func NewBean(t reflect.Type, v reflect.Value, f gs.Callable, name string, file string, line int) *BeanDefinition { return &BeanDefinition{ BeanMetadata: &BeanMetadata{ f: f, - method: method, file: file, line: line, status: Default, diff --git a/gs/internal/gs_core/bean.go b/gs/internal/gs_core/bean.go index 1c82bb5d..6c7ad15b 100644 --- a/gs/internal/gs_core/bean.go +++ b/gs/internal/gs_core/bean.go @@ -26,6 +26,7 @@ import ( "github.com/go-spring/spring-core/gs/internal/gs" "github.com/go-spring/spring-core/gs/internal/gs_arg" "github.com/go-spring/spring-core/gs/internal/gs_bean" + "github.com/go-spring/spring-core/gs/internal/gs_cond" "github.com/go-spring/spring-core/util" ) @@ -34,8 +35,8 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { var v reflect.Value var fromValue bool - var method bool var name string + var cond gs.Condition switch i := objOrCtor.(type) { case reflect.Value: @@ -93,7 +94,14 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { if name[0] == '(' { name = name[strings.Index(name, ".")+1:] } - method = strings.LastIndexByte(fnInfo.Name(), ')') > 0 + method := strings.LastIndexByte(fnInfo.Name(), ')') > 0 + if method { + selector, ok := f.Arg(0) + if !ok || selector == "" { + selector, _ = f.In(0) + } + cond = gs_cond.OnBean(selector) + } } if t.Kind() == reflect.Ptr && !util.IsValueType(t.Elem()) { @@ -107,6 +115,6 @@ func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.UnregisteredBean { name = strings.TrimPrefix(s[len(s)-1], "*") } - d := gs_bean.NewBean(t, v, f, name, method, file, line) - return gs.NewUnregisteredBean(d) + d := gs_bean.NewBean(t, v, f, name, file, line) + return gs.NewUnregisteredBean(d).On(cond) } diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index c836d914..5e8ff25e 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -365,7 +365,7 @@ func (c *Container) scanConfiguration(bd *gs_bean.BeanDefinition) ([]*gs_bean.Be v = v.Elem() } name := bd.Name() + "_" + m.Name - b := gs_bean.NewBean(v.Type(), v, f, name, true, bd.File(), bd.Line()) + b := gs_bean.NewBean(v.Type(), v, f, name, bd.File(), bd.Line()) gs.NewUnregisteredBean(b).On(gs_cond.OnBean(bd)) newBeans = append(newBeans, b) } @@ -394,30 +394,6 @@ func (c *Container) resolveBean(b *gs_bean.BeanDefinition) error { b.SetStatus(gs_bean.Resolving) - // method bean 先确定 parent bean 是否存在 - if b.IsMethod() { - selector, ok := b.Callable().Arg(0) - if !ok || selector == "" { - selector, _ = b.Callable().In(0) - } - parents, err := c.Find(selector) - if err != nil { - return err - } - n := len(parents) - if n > 1 { - msg := fmt.Sprintf("found %d parent beans, bean:%q type:%q [", n, selector, b.Type().In(0)) - for _, b := range parents { - msg += "( " + b.(*gs_bean.BeanDefinition).String() + " ), " - } - msg = msg[:len(msg)-2] + "]" - return errors.New(msg) - } else if n == 0 { - b.SetStatus(gs_bean.Deleted) - return nil - } - } - if b.Cond() != nil { if ok, err := b.Cond().Matches(c); err != nil { return err From 713bad17f50045177f91d87c4e4503ed64235405 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 08:17:04 +0800 Subject: [PATCH 70/75] add ID() Type() to beanBuilder[T] --- gs/internal/gs/gs.go | 10 ++++++++++ gs/internal/gs_core/bean_test.go | 8 ++++---- gs/internal/gs_core/core_test.go | 18 +++++++++--------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index c505f66c..a9b5e8a1 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -81,6 +81,8 @@ type ConfigurationParam struct { } type BeanRegistration interface { + ID() string + Type() reflect.Type SetName(name string) SetOn(cond Condition) SetDependsOn(selectors ...BeanSelector) @@ -100,6 +102,14 @@ func (d *beanBuilder[T]) BeanRegistration() BeanRegistration { return d.b } +func (d *beanBuilder[T]) ID() string { + return d.b.ID() +} + +func (d *beanBuilder[T]) Type() reflect.Type { + return d.b.Type() +} + func (d *beanBuilder[T]) Name(name string) *T { d.b.SetName(name) return *(**T)(unsafe.Pointer(&d)) diff --git a/gs/internal/gs_core/bean_test.go b/gs/internal/gs_core/bean_test.go index d248fe35..b56b4779 100644 --- a/gs/internal/gs_core/bean_test.go +++ b/gs/internal/gs_core/bean_test.go @@ -195,10 +195,10 @@ func TestObjectBean(t *testing.T) { func TestConstructorBean(t *testing.T) { bd := newBean(NewStudent) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") bd = newBean(NewPtrStudent) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "*gs_core_test.Student") + assert.Equal(t, bd.Type().String(), "*gs_core_test.Student") // mapFn := func() map[int]string { return make(map[int]string) } // bd = newBean(mapFn) @@ -210,11 +210,11 @@ func TestConstructorBean(t *testing.T) { funcFn := func() func(int) { return nil } bd = newBean(funcFn) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "func(int)") + assert.Equal(t, bd.Type().String(), "func(int)") interfaceFn := func(name string) Teacher { return newHistoryTeacher(name) } bd = newBean(interfaceFn) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Type().String(), "gs_core_test.Teacher") + assert.Equal(t, bd.Type().String(), "gs_core_test.Teacher") // assert.Panic(t, func() { // _ = newBean(func() (*int, *int) { return nil, nil }) diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 7c37e50d..bfd548b1 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -890,7 +890,7 @@ func TestApplicationContext_RegisterBeanFn2(t *testing.T) { c.RefreshProperties(prop) bd := c.Provide(NewManager) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "NewManager") + assert.Matches(t, bd.ID(), ".*:NewManager") err := runTest(c, func(p gs.Context) { @@ -1187,7 +1187,7 @@ func TestApplicationContext_Collect(t *testing.T) { return func() {} }) - assert.Equal(t, intBean.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "TestApplicationContext_Collect.func6.1") + assert.Equal(t, intBean.ID(), "func():TestApplicationContext_Collect.func6.1") c.RefreshProperties(prop) err := c.Refresh() @@ -1437,7 +1437,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - bd := c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()) + bd := c.Provide((*Server).Consumer, parent.ID()) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1455,7 +1455,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Equal(t, consumer.s.Version, "2.0.0") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "Consumer") + assert.Matches(t, bd.ID(), ".*:Consumer") }) t.Run("method bean condition", func(t *testing.T) { @@ -1478,7 +1478,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { assert.Error(t, err, "can't find bean, bean:\"\" type:\"\\*gs_core_test.Consumer\"") }) assert.Nil(t, err) - assert.Equal(t, bd.BeanRegistration().(*gs_bean.BeanDefinition).Name(), "Consumer") + assert.Matches(t, bd.ID(), ".*:Consumer") }) t.Run("method bean arg", func(t *testing.T) { @@ -1487,7 +1487,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).ConsumerArg, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID(), "${i:=9}") + c.Provide((*Server).ConsumerArg, parent.ID(), "${i:=9}") c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { @@ -1513,7 +1513,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Provide(NewServerInterface) - c.Provide(ServerInterface.Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).DependsOn("ServerInterface") + c.Provide(ServerInterface.Consumer, parent.ID()).DependsOn("ServerInterface") c.Object(new(Service)) c.RefreshProperties(prop) @@ -1567,7 +1567,7 @@ func TestApplicationContext_RegisterMethodBean(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)).DependsOn("Service") - c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).DependsOn("Server") + c.Provide((*Server).Consumer, parent.ID()).DependsOn("Server") c.Object(new(Service)) c.RefreshProperties(prop) err := c.Refresh() @@ -2468,7 +2468,7 @@ func TestDefaultSpringContext(t *testing.T) { c := gs_core.New() parent := c.Object(new(Server)) - c.Provide((*Server).Consumer, parent.BeanRegistration().(*gs_bean.BeanDefinition).ID()).On(gs_cond.OnProperty("consumer.enable")) + c.Provide((*Server).Consumer, parent.ID()).On(gs_cond.OnProperty("consumer.enable")) c.RefreshProperties(prop) err := runTest(c, func(p gs.Context) { From 3eeda9dcd8e773b5ce23c890a648e971bca4ef80 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 08:28:02 +0800 Subject: [PATCH 71/75] code refactor --- gs/gs.go | 2 +- gs/internal/gs/gs.go | 15 +++++++++++++++ gs/internal/gs_core/core.go | 15 +++++---------- gs/internal/gs_core/core_test.go | 4 ++-- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index d449a7c6..41ac9fe6 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -48,7 +48,7 @@ type ( Condition = gs.Condition Properties = gs.Properties Context = gs.Context - ContextAware = gs_core.ContextAware + ContextAware = gs.ContextAware Dync[T any] = gs_dync.Value[T] AppRunner = gs_app.AppRunner AppServer = gs_app.AppServer diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index a9b5e8a1..4294927f 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -12,6 +12,8 @@ import ( // `(*error)(nil)`, or a BeanDefinition value. type BeanSelector interface{} +/********************************** condition ********************************/ + type CondBean interface { ID() string Name() string @@ -35,6 +37,8 @@ type Condition interface { Matches(ctx CondContext) (bool, error) } +/************************************* arg ***********************************/ + // Arg 用于为函数参数提供绑定值。可以是 bean.Selector 类型,表示注入 bean ; // 可以是 ${X:=Y} 形式的字符串,表示属性绑定或者注入 bean ;可以是 ValueArg // 类型,表示不从 IoC 容器获取而是用户传入的普通值;可以是 IndexArg 类型,表示 @@ -52,6 +56,8 @@ type ArgContext interface { Wire(v reflect.Value, tag string) error } +/*********************************** conf ************************************/ + type Properties interface { Data() map[string]string Keys() []string @@ -68,6 +74,8 @@ type Refreshable interface { OnRefresh(prop Properties, param conf.BindParam) error } +/*********************************** bean ************************************/ + type Callable interface { Arg(i int) (Arg, bool) In(i int) (reflect.Type, bool) @@ -183,6 +191,8 @@ func NewUnregisteredBean(d BeanRegistration) *UnregisteredBean { } } +/*********************************** ioc ************************************/ + type Container interface { Object(i interface{}) *RegisteredBean Provide(ctor interface{}, args ...Arg) *RegisteredBean @@ -214,3 +224,8 @@ type Context interface { Invoke(fn interface{}, args ...Arg) ([]interface{}, error) Go(fn func(ctx context.Context)) } + +// ContextAware injects the Context into a struct as the field GSContext. +type ContextAware struct { + GSContext Context `autowire:""` +} diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 5e8ff25e..c02c8400 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -40,21 +40,16 @@ import ( type refreshState int const ( - Unrefreshed = refreshState(iota) // 未刷新 - RefreshInit // 准备刷新 - Refreshing // 正在刷新 - Refreshed // 已刷新 + RefreshDefault = refreshState(iota) // 未刷新 + RefreshInit // 准备刷新 + Refreshing // 正在刷新 + Refreshed // 已刷新 ) var UnregisteredBeanType = reflect.TypeOf((*gs.UnregisteredBean)(nil)) type GroupFunc = func(p gs.Properties) ([]*gs.UnregisteredBean, error) -// ContextAware injects the Context into a struct as the field GSContext. -type ContextAware struct { - GSContext gs.Context `autowire:""` -} - type SimpleBean interface { Callable() gs.Callable Name() string @@ -160,7 +155,7 @@ func (c *Container) RefreshProperties(p gs.Properties) error { func (c *Container) Refresh() (err error) { - if c.state != Unrefreshed { + if c.state != RefreshDefault { return errors.New("Container already refreshed") } c.state = RefreshInit diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index bfd548b1..97db0813 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -2882,7 +2882,7 @@ func TestDestroyDependence(t *testing.T) { } type ContextAware struct { - gs_core.ContextAware + gs.ContextAware } func (c *ContextAware) Echo(str string) string { @@ -3045,7 +3045,7 @@ func TestConfiguration(t *testing.T) { c := gs_core.New() c.Object(&ConfigurationBean{"123"}).Configuration(gs.ConfigurationParam{Exclude: []string{"NewBean"}}).Name("123") c.Provide(NewConfigurationBean, gs_arg.Value("456")).Configuration().Name("456") - ctx := &gs_core.ContextAware{} + ctx := &gs.ContextAware{} c.Object(ctx) if err := c.Refresh(); err != nil { t.Fatal(err) From 33898cb898253a3e36ab4e3e322d8a0d54ce00ba Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 10:07:31 +0800 Subject: [PATCH 72/75] add AppContext --- gs/gs.go | 1 + gs/internal/gs_app/app.go | 20 ++++++++++++---- gs/internal/gs_app/boot.go | 2 +- gs/internal/gs_core/core.go | 46 +++++++++++++++++++------------------ gs/internal/gs_core/wire.go | 28 +++++++++++----------- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 41ac9fe6..3ca388b8 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -50,6 +50,7 @@ type ( Context = gs.Context ContextAware = gs.ContextAware Dync[T any] = gs_dync.Value[T] + AppContext = gs_app.AppContext AppRunner = gs_app.AppRunner AppServer = gs_app.AppServer ) diff --git a/gs/internal/gs_app/app.go b/gs/internal/gs_app/app.go index 7e83e5be..cc886003 100644 --- a/gs/internal/gs_app/app.go +++ b/gs/internal/gs_app/app.go @@ -29,14 +29,26 @@ import ( ) type AppRunner interface { - Run(ctx gs.Context) + Run(ctx *AppContext) } type AppServer interface { - OnAppStart(ctx gs.Context) // 应用启动的事件 + OnAppStart(ctx *AppContext) // 应用启动的事件 OnAppStop(ctx context.Context) // 应用停止的事件 } +type AppContext struct { + c gs.Context +} + +func (p *AppContext) Unsafe() gs.Context { + return p.c +} + +func (p *AppContext) Go(fn func(ctx context.Context)) { + p.c.Go(fn) +} + // App 应用 type App struct { C gs.Container @@ -95,12 +107,12 @@ func (app *App) Start() error { // 执行命令行启动器 for _, r := range app.Runners { - r.Run(ctx) + r.Run(&AppContext{ctx}) } // 通知应用启动事件 for _, svr := range app.Servers { - svr.OnAppStart(ctx) + svr.OnAppStart(&AppContext{ctx}) } app.C.SimplifyMemory() diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 1089391a..5e9cd1f8 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -57,7 +57,7 @@ func (b *Boot) Run() error { // 执行命令行启动器 for _, r := range b.Runners { - r.Run(b.C.(gs.Context)) + r.Run(&AppContext{b.C.(gs.Context)}) } b.C.Close() diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index c02c8400..18f415c1 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -50,16 +50,16 @@ var UnregisteredBeanType = reflect.TypeOf((*gs.UnregisteredBean)(nil)) type GroupFunc = func(p gs.Properties) ([]*gs.UnregisteredBean, error) -type SimpleBean interface { - Callable() gs.Callable +type BeanRuntime interface { Name() string - Status() gs_bean.BeanStatus + Type() reflect.Type + Value() reflect.Value Interface() interface{} - IsPrimary() bool + Callable() gs.Callable Match(typeName string, beanName string) bool + Status() gs_bean.BeanStatus + IsPrimary() bool String() string - Type() reflect.Type - Value() reflect.Value } // Container 是 go-spring 框架的基石,实现了 Martin Fowler 在 << Inversion @@ -71,8 +71,8 @@ type SimpleBean interface { // 性绑定,要么同时使用依赖注入和属性绑定。 type Container struct { beans []*gs_bean.BeanDefinition - beansByName map[string][]SimpleBean - beansByType map[reflect.Type][]SimpleBean + beansByName map[string][]BeanRuntime + beansByType map[reflect.Type][]BeanRuntime groupFuncs []GroupFunc p *gs_dync.Properties ctx context.Context @@ -93,8 +93,8 @@ func New() gs.Container { ctx: ctx, cancel: cancel, p: gs_dync.New(), - beansByName: make(map[string][]SimpleBean), - beansByType: make(map[reflect.Type][]SimpleBean), + beansByName: make(map[string][]BeanRuntime), + beansByType: make(map[reflect.Type][]BeanRuntime), } c.Object(c).Export((*gs.Context)(nil)) return c @@ -253,9 +253,12 @@ func (c *Container) Refresh() (err error) { return errors.New("remove the dependency cycle between beans") } - if c.ContextAware { // 保留核心数据 - c.beansByName = make(map[string][]SimpleBean) - c.beansByType = make(map[reflect.Type][]SimpleBean) + c.destroyers = stack.sortDestroyers() + + // 精简内存 + { + c.beansByName = make(map[string][]BeanRuntime) + c.beansByType = make(map[reflect.Type][]BeanRuntime) for _, b := range c.beans { if b.Status() == gs_bean.Deleted { continue @@ -266,16 +269,8 @@ func (c *Container) Refresh() (err error) { c.beansByType[t] = append(c.beansByType[t], b.BeanRuntime) } } - } else { // 清空全部数据 - if c.p.ObjectsCount() == 0 { - c.p = nil - } - c.beansByName = nil - c.beansByType = nil } - c.beans = nil - c.destroyers = stack.sortDestroyers() c.state = Refreshed cost := time.Now().Sub(start) @@ -286,7 +281,14 @@ func (c *Container) Refresh() (err error) { // SimplifyMemory 清理运行时不需要的空间。 func (c *Container) SimplifyMemory() { - + if !c.ContextAware { // 保留核心数据 + if c.p.ObjectsCount() == 0 { + c.p = nil + } + c.beansByName = nil + c.beansByType = nil + } + c.beans = nil } func (c *Container) scanConfiguration(bd *gs_bean.BeanDefinition) ([]*gs_bean.BeanDefinition, error) { diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 9166a50c..92278577 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -244,14 +244,14 @@ func (c *Container) autowire(v reflect.Value, tags []wireTag, nullable bool, sta } } -type byBeanName []SimpleBean +type byBeanName []BeanRuntime func (b byBeanName) Len() int { return len(b) } func (b byBeanName) Less(i, j int) bool { return b[i].Name() < b[j].Name() } func (b byBeanName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // filterBean 返回 tag 对应的 bean 在数组中的索引,找不到返回 -1。 -func filterBean(beans []SimpleBean, tag wireTag, t reflect.Type) (int, error) { +func filterBean(beans []BeanRuntime, tag wireTag, t reflect.Type) (int, error) { var found []int for i, b := range beans { @@ -293,11 +293,11 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, return fmt.Errorf("%s is not valid receiver type", t.String()) } - var beans []SimpleBean + var beans []BeanRuntime beans = c.beansByType[et] { - var arr []SimpleBean + var arr []BeanRuntime for _, b := range beans { if b.Status() == gs_bean.Deleted { continue @@ -310,9 +310,9 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, if len(tags) > 0 { var ( - anyBeans []SimpleBean - afterAny []SimpleBean - beforeAny []SimpleBean + anyBeans []BeanRuntime + afterAny []BeanRuntime + beforeAny []BeanRuntime ) foundAny := false @@ -341,7 +341,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, beforeAny = append(beforeAny, beans[index]) } - tmpBeans := append([]SimpleBean{}, beans[:index]...) + tmpBeans := append([]BeanRuntime{}, beans[:index]...) beans = append(tmpBeans, beans[index+1:]...) } @@ -350,7 +350,7 @@ func (c *Container) collectBeans(v reflect.Value, tags []wireTag, nullable bool, } n := len(beforeAny) + len(anyBeans) + len(afterAny) - arr := make([]SimpleBean, 0, n) + arr := make([]BeanRuntime, 0, n) arr = append(arr, beforeAny...) arr = append(arr, anyBeans...) arr = append(arr, afterAny...) @@ -415,7 +415,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return fmt.Errorf("%s is not valid receiver type", t.String()) } - var foundBeans []SimpleBean + var foundBeans []BeanRuntime for _, b := range c.beansByType[t] { if b.Status() == gs_bean.Deleted { continue @@ -461,7 +461,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er } // 优先使用设置成主版本的 bean - var primaryBeans []SimpleBean + var primaryBeans []BeanRuntime for _, b := range foundBeans { if b.IsPrimary() { @@ -487,7 +487,7 @@ func (c *Container) getBean(v reflect.Value, tag wireTag, stack *wiringStack) er return errors.New(msg) } - var result SimpleBean + var result BeanRuntime if len(primaryBeans) == 1 { result = primaryBeans[0] } else { @@ -624,7 +624,7 @@ func (c *Container) wireBeanInRefreshing(b *gs_bean.BeanDefinition, stack *wirin return nil } -func (c *Container) wireBeanAfterRefreshed(b SimpleBean, stack *wiringStack) error { +func (c *Container) wireBeanAfterRefreshed(b BeanRuntime, stack *wiringStack) error { v, err := c.getBeanValue(b, stack) if err != nil { @@ -665,7 +665,7 @@ func (a *argContext) Wire(v reflect.Value, tag string) error { } // getBeanValue 获取 bean 的值,如果是构造函数 bean 则执行其构造函数然后返回执行结果。 -func (c *Container) getBeanValue(b SimpleBean, stack *wiringStack) (reflect.Value, error) { +func (c *Container) getBeanValue(b BeanRuntime, stack *wiringStack) (reflect.Value, error) { if b.Callable() == nil { return b.Value(), nil From fd7021e7636c8dcf4727cac970270bd5f95c11ad Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 10:12:49 +0800 Subject: [PATCH 73/75] add SubKeys --- conf/bind.go | 2 +- conf/conf.go | 6 +----- gs/internal/gs/gs.go | 1 + gs/internal/gs_core/core.go | 4 ++++ 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/conf/bind.go b/conf/bind.go index fbb37529..687826a8 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -295,7 +295,7 @@ func getSlice(p readOnlyProperties, et reflect.Type, param BindParam) (readOnlyP r := New() for i, s := range arrVal { k := fmt.Sprintf("%s[%d]", param.Key, i) - _ = r.store(k, s) + _ = r.storage.Set(k, s) } return r, nil } diff --git a/conf/conf.go b/conf/conf.go index 13de6bba..0135c459 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -186,17 +186,13 @@ func (p *Properties) Merge(m map[string]interface{}) error { func (p *Properties) merge(m map[string]string) error { for key, val := range m { - if err := p.store(key, val); err != nil { + if err := p.storage.Set(key, val); err != nil { return err } } return nil } -func (p *Properties) store(key, val string) error { - return p.storage.Set(key, val) -} - func (p *Properties) Data() map[string]string { return p.storage.Data() } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index 4294927f..bd78c904 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -216,6 +216,7 @@ type Context interface { Context() context.Context Keys() []string Has(key string) bool + SubKeys(key string) ([]string, error) Prop(key string, opts ...conf.GetOption) string Resolve(s string) (string, error) Bind(i interface{}, opts ...conf.BindArg) error diff --git a/gs/internal/gs_core/core.go b/gs/internal/gs_core/core.go index 18f415c1..cd74736d 100755 --- a/gs/internal/gs_core/core.go +++ b/gs/internal/gs_core/core.go @@ -137,6 +137,10 @@ func (c *Container) Has(key string) bool { return c.p.Data().Has(key) } +func (c *Container) SubKeys(key string) ([]string, error) { + return c.p.Data().SubKeys(key) +} + func (c *Container) Prop(key string, opts ...conf.GetOption) string { return c.p.Data().Get(key, opts...) } From fe86a772fd8b9c20c357a32080b3ea95054ed11b Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 11:51:47 +0800 Subject: [PATCH 74/75] code refactor --- gs/gs.go | 39 +++++++---------------------- gs/gstest/gstest.go | 25 ++++++++++++++++--- gs/gstest/gstest_test.go | 2 +- gs/internal/gs/gs.go | 43 ++++++++++++++++++++------------ gs/internal/gs_arg/arg.go | 39 +++++++---------------------- gs/internal/gs_bean/bean.go | 30 ++++++++++++++++------ gs/internal/gs_core/core_test.go | 2 +- gs/internal/gs_core/wire.go | 16 ++++++++++++ 8 files changed, 107 insertions(+), 89 deletions(-) diff --git a/gs/gs.go b/gs/gs.go index 3ca388b8..941706bb 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -57,32 +57,6 @@ type ( /************************************ arg ***********************************/ -// IndexArg returns an IndexArg. -func IndexArg(n int, arg Arg) gs_arg.IndexArg { - return gs_arg.Index(n, arg) -} - -// R0 returns an IndexArg with index 0. -func R0(arg Arg) gs_arg.IndexArg { return gs_arg.R0(arg) } - -// R1 returns an IndexArg with index 1. -func R1(arg Arg) gs_arg.IndexArg { return gs_arg.R1(arg) } - -// R2 returns an IndexArg with index 2. -func R2(arg Arg) gs_arg.IndexArg { return gs_arg.R2(arg) } - -// R3 returns an IndexArg with index 3. -func R3(arg Arg) gs_arg.IndexArg { return gs_arg.R3(arg) } - -// R4 returns an IndexArg with index 4. -func R4(arg Arg) gs_arg.IndexArg { return gs_arg.R4(arg) } - -// R5 returns an IndexArg with index 5. -func R5(arg Arg) gs_arg.IndexArg { return gs_arg.R5(arg) } - -// R6 returns an IndexArg with index 6. -func R6(arg Arg) gs_arg.IndexArg { return gs_arg.R6(arg) } - // NilArg return a ValueArg with a value of nil. func NilArg() gs_arg.ValueArg { return gs_arg.Nil() @@ -93,20 +67,25 @@ func ValueArg(v interface{}) gs_arg.ValueArg { return gs_arg.Value(v) } +// IndexArg returns an IndexArg. +func IndexArg(n int, arg Arg) gs_arg.IndexArg { + return gs_arg.Index(n, arg) +} + // OptionArg 返回 Option 函数的参数绑定。 func OptionArg(fn interface{}, args ...Arg) *gs_arg.OptionArg { return gs_arg.Option(fn, args...) } +func BindArg(fn interface{}, args []Arg, skip int) (*gs_arg.Callable, error) { + return gs_arg.Bind(fn, args, skip) +} + // MustBindArg 为 Option 方法绑定运行时参数。 func MustBindArg(fn interface{}, args ...Arg) *gs_arg.Callable { return gs_arg.MustBind(fn, args...) } -func BindArg(fn interface{}, args []Arg, skip int) (*gs_arg.Callable, error) { - return gs_arg.Bind(fn, args, skip) -} - /************************************ cond ***********************************/ type ( diff --git a/gs/gstest/gstest.go b/gs/gstest/gstest.go index 4d863dce..bbd70fc7 100644 --- a/gs/gstest/gstest.go +++ b/gs/gstest/gstest.go @@ -17,6 +17,7 @@ package gstest import ( + "context" "testing" "github.com/go-spring/spring-core/conf" @@ -37,13 +38,25 @@ func Run(m *testing.M) (code int) { return m.Run() } -// HasProperty 判断属性是否存在 -func HasProperty(key string) bool { +func Context() context.Context { + return ctx.GSContext.Context() +} + +func Keys() []string { + return ctx.GSContext.Keys() +} + +// Has 判断属性是否存在 +func Has(key string) bool { return ctx.GSContext.Has(key) } -// GetProperty 获取属性值 -func GetProperty(key string, opts ...conf.GetOption) string { +func SubKeys(key string) ([]string, error) { + return ctx.GSContext.SubKeys(key) +} + +// Prop 获取属性值 +func Prop(key string, opts ...conf.GetOption) string { return ctx.GSContext.Prop(key, opts...) } @@ -71,3 +84,7 @@ func Wire(objOrCtor interface{}, ctorArgs ...gs.Arg) (interface{}, error) { func Invoke(fn interface{}, args ...gs.Arg) ([]interface{}, error) { return ctx.GSContext.Invoke(fn, args...) } + +func RefreshProperties(p gs.Properties) error { + return gs.RefreshProperties(p) +} diff --git a/gs/gstest/gstest_test.go b/gs/gstest/gstest_test.go index acaa9a72..c76d02cd 100644 --- a/gs/gstest/gstest_test.go +++ b/gs/gstest/gstest_test.go @@ -35,5 +35,5 @@ func TestMain(m *testing.M) { func TestConfig(t *testing.T) { os.Clearenv() os.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev") - assert.Equal(t, gstest.GetProperty("spring.profiles.active"), "dev") + assert.Equal(t, gstest.Prop("spring.profiles.active"), "dev") } diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index bd78c904..faf14e90 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -1,3 +1,19 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package gs import ( @@ -8,8 +24,8 @@ import ( "github.com/go-spring/spring-core/conf" ) -// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as -// `(*error)(nil)`, or a BeanDefinition value. +// A BeanSelector can be the ID of a bean, a `reflect.Type`, an interface{} +// pointer such as `(*error)(nil)`, or `new(error)`. type BeanSelector interface{} /********************************** condition ********************************/ @@ -56,6 +72,12 @@ type ArgContext interface { Wire(v reflect.Value, tag string) error } +type Callable interface { + Arg(i int) (Arg, bool) + In(i int) (reflect.Type, bool) + Call(ctx ArgContext) ([]reflect.Value, error) +} + /*********************************** conf ************************************/ type Properties interface { @@ -76,12 +98,6 @@ type Refreshable interface { /*********************************** bean ************************************/ -type Callable interface { - Arg(i int) (Arg, bool) - In(i int) (reflect.Type, bool) - Call(ctx ArgContext) ([]reflect.Value, error) -} - type ConfigurationParam struct { Enable bool // 是否扫描成员方法 Include []string // 包含哪些成员方法 @@ -91,6 +107,7 @@ type ConfigurationParam struct { type BeanRegistration interface { ID() string Type() reflect.Type + SetCaller(skip int) SetName(name string) SetOn(cond Condition) SetDependsOn(selectors ...BeanSelector) @@ -193,6 +210,7 @@ func NewUnregisteredBean(d BeanRegistration) *UnregisteredBean { /*********************************** ioc ************************************/ +// Container ... type Container interface { Object(i interface{}) *RegisteredBean Provide(ctor interface{}, args ...Arg) *RegisteredBean @@ -204,14 +222,7 @@ type Container interface { Close() } -// Context 提供了一些在 IoC 容器启动后基于反射获取和使用 property 与 bean 的接 -// 口。因为很多人会担心在运行时大量使用反射会降低程序性能,所以命名为 Context,取 -// 其诱人但危险的含义。事实上,这些在 IoC 容器启动后使用属性绑定和依赖注入的方案, -// 都可以转换为启动阶段的方案以提高程序的性能。 -// 另一方面,为了统一 Container 和 App 两种启动方式下这些方法的使用方式,需要提取 -// 出一个可共用的接口来,也就是说,无论程序是 Container 方式启动还是 App 方式启动, -// 都可以在需要使用这些方法的地方注入一个 Context 对象而不是 Container 对象或者 -// App 对象,从而实现使用方式的统一。 +// Context ... type Context interface { Context() context.Context Keys() []string diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index c5c35e76..3372ec7d 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -42,27 +42,6 @@ func Index(n int, arg gs.Arg) IndexArg { return IndexArg{n: n, arg: arg} } -// R0 returns an IndexArg with index 0. -func R0(arg gs.Arg) IndexArg { return Index(0, arg) } - -// R1 returns an IndexArg with index 1. -func R1(arg gs.Arg) IndexArg { return Index(1, arg) } - -// R2 returns an IndexArg with index 2. -func R2(arg gs.Arg) IndexArg { return Index(2, arg) } - -// R3 returns an IndexArg with index 3. -func R3(arg gs.Arg) IndexArg { return Index(3, arg) } - -// R4 returns an IndexArg with index 4. -func R4(arg gs.Arg) IndexArg { return Index(4, arg) } - -// R5 returns an IndexArg with index 5. -func R5(arg gs.Arg) IndexArg { return Index(5, arg) } - -// R6 returns an IndexArg with index 6. -func R6(arg gs.Arg) IndexArg { return Index(6, arg) } - // ValueArg is an Arg that has a value. type ValueArg struct { v interface{} @@ -78,14 +57,14 @@ func Value(v interface{}) ValueArg { return ValueArg{v: v} } -// argList stores the arguments of a function. -type argList struct { +// ArgList stores the arguments of a function. +type ArgList struct { fnType reflect.Type args []gs.Arg } -// newArgList returns a new argList. -func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { +// NewArgList returns a new ArgList. +func NewArgList(fnType reflect.Type, args []gs.Arg) (*ArgList, error) { fixedArgCount := fnType.NumIn() if fnType.IsVariadic() { @@ -160,11 +139,11 @@ func newArgList(fnType reflect.Type, args []gs.Arg) (*argList, error) { } } - return &argList{fnType: fnType, args: fnArgs}, nil + return &ArgList{fnType: fnType, args: fnArgs}, nil } // get returns all processed Args value. fileLine is the binding position of Callable. -func (r *argList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, error) { +func (r *ArgList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, error) { fnType := r.fnType numIn := fnType.NumIn() @@ -193,7 +172,7 @@ func (r *argList) get(ctx gs.ArgContext, fileLine string) ([]reflect.Value, erro return result, nil } -func (r *argList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine string) (reflect.Value, error) { +func (r *ArgList) getArg(ctx gs.ArgContext, arg gs.Arg, t reflect.Type, fileLine string) (reflect.Value, error) { var ( err error @@ -322,7 +301,7 @@ func (arg *OptionArg) call(ctx gs.ArgContext) (reflect.Value, error) { type Callable struct { fn interface{} fnType reflect.Type - argList *argList + argList *ArgList fileLine string } @@ -340,7 +319,7 @@ func MustBind(fn interface{}, args ...gs.Arg) *Callable { func Bind(fn interface{}, args []gs.Arg, skip int) (*Callable, error) { fnType := reflect.TypeOf(fn) - argList, err := newArgList(fnType, args) + argList, err := NewArgList(fnType, args) if err != nil { return nil, err } diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index e5bdc5ce..594d3391 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -1,3 +1,19 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package gs_bean import ( @@ -223,7 +239,7 @@ func (d *BeanDefinition) String() string { return fmt.Sprintf("%s name:%q %s", d.Class(), d.name, d.FileLine()) } -// Name 设置 bean 的名称。 +// SetName 设置 bean 的名称。 func (d *BeanDefinition) SetName(name string) { d.name = name } @@ -232,7 +248,7 @@ func (d *BeanDefinition) SetCaller(skip int) { _, d.file, d.line, _ = runtime.Caller(skip) } -// On 设置 bean 的 Condition。 +// SetOn 设置 bean 的 Condition。 func (d *BeanDefinition) SetOn(cond gs.Condition) { if d.cond == nil { d.cond = cond @@ -241,12 +257,12 @@ func (d *BeanDefinition) SetOn(cond gs.Condition) { } } -// DependsOn 设置 bean 的间接依赖项。 +// SetDependsOn 设置 bean 的间接依赖项。 func (d *BeanDefinition) SetDependsOn(selectors ...gs.BeanSelector) { d.depends = append(d.depends, selectors...) } -// Primary 设置 bean 为主版本。 +// SetPrimary 设置 bean 为主版本。 func (d *BeanDefinition) SetPrimary() { d.primary = true } @@ -263,7 +279,7 @@ func validLifeCycleFunc(fnType reflect.Type, beanValue reflect.Value) bool { return util.ReturnNothing(fnType) || util.ReturnOnlyError(fnType) } -// Init 设置 bean 的初始化函数。 +// SetInit 设置 bean 的初始化函数。 func (d *BeanDefinition) SetInit(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.init = fn @@ -272,7 +288,7 @@ func (d *BeanDefinition) SetInit(fn interface{}) { panic(errors.New("init should be func(bean) or func(bean)error")) } -// Destroy 设置 bean 的销毁函数。 +// SetDestroy 设置 bean 的销毁函数。 func (d *BeanDefinition) SetDestroy(fn interface{}) { if validLifeCycleFunc(reflect.TypeOf(fn), d.Value()) { d.destroy = fn @@ -281,7 +297,7 @@ func (d *BeanDefinition) SetDestroy(fn interface{}) { panic(errors.New("destroy should be func(bean) or func(bean)error")) } -// Export 设置 bean 的导出接口。 +// SetExport 设置 bean 的导出接口。 func (d *BeanDefinition) SetExport(exports ...interface{}) { for _, o := range exports { t, ok := o.(reflect.Type) diff --git a/gs/internal/gs_core/core_test.go b/gs/internal/gs_core/core_test.go index 97db0813..6736b614 100755 --- a/gs/internal/gs_core/core_test.go +++ b/gs/internal/gs_core/core_test.go @@ -2351,7 +2351,7 @@ func TestApplicationContext_CreateBean(t *testing.T) { c := gs_core.New() c.Object(&ObjFactory{}) err := runTest(c, func(p gs.Context) { - b, err := p.Wire((*ObjFactory).NewObj, gs_arg.R1("${i:=5}")) + b, err := p.Wire((*ObjFactory).NewObj, gs_arg.Index(1, "${i:=5}")) fmt.Println(b, err) }) assert.Nil(t, err) diff --git a/gs/internal/gs_core/wire.go b/gs/internal/gs_core/wire.go index 92278577..794f5de2 100644 --- a/gs/internal/gs_core/wire.go +++ b/gs/internal/gs_core/wire.go @@ -1,3 +1,19 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package gs_core import ( From 7e68d838ff202090f45be5218aa3328c3d04ce5a Mon Sep 17 00:00:00 2001 From: lvan100 Date: Sun, 12 Jan 2025 12:03:56 +0800 Subject: [PATCH 75/75] tag v1.2.0.alpha --- util/type.go | 43 +-------- util/type_mock.go | 200 ----------------------------------------- util/type_mock_test.go | 62 ------------- util/type_test.go | 16 ---- 4 files changed, 2 insertions(+), 319 deletions(-) delete mode 100644 util/type_mock.go delete mode 100644 util/type_mock_test.go diff --git a/util/type.go b/util/type.go index 9d7523b4..f2d6a1d0 100644 --- a/util/type.go +++ b/util/type.go @@ -14,12 +14,9 @@ * limitations under the License. */ -//go:generate mockgen -build_flags="-mod=mod" -package=util -source=type.go -destination=type_mock.go - package util import ( - "context" "reflect" "strings" ) @@ -27,9 +24,6 @@ import ( // errorType the reflection type of error. var errorType = reflect.TypeOf((*error)(nil)).Elem() -// contextType the reflection type of context.Context. -var contextType = reflect.TypeOf((*context.Context)(nil)).Elem() - // TypeName returns a fully qualified name consisting of package path and type name. func TypeName(i interface{}) string { @@ -58,35 +52,6 @@ func TypeName(i interface{}) string { return typ.String() // the path of built-in type is empty } -// A BeanSelector can be the ID of a bean, a `reflect.Type`, a pointer such as -// `(*error)(nil)`, or a BeanDefinition value. -type BeanSelector interface{} - -// A BeanDefinition describes a bean whose lifecycle is managed by IoC container. -type BeanDefinition interface { - Type() reflect.Type - Value() reflect.Value - Interface() interface{} - ID() string - BeanName() string - TypeName() string - Created() bool - Wired() bool -} - -// Converter converts string value into user-defined value. It should be function -// type, and its prototype is func(string)(type,error). -type Converter interface{} - -// IsConverter returns whether `t` is a converter type. -func IsConverter(t reflect.Type) bool { - return IsFuncType(t) && - t.NumIn() == 1 && - t.In(0).Kind() == reflect.String && - t.NumOut() == 2 && - (IsValueType(t.Out(0)) || IsFuncType(t.Out(0))) && IsErrorType(t.Out(1)) -} - // IsFuncType returns whether `t` is func type. func IsFuncType(t reflect.Type) bool { return t.Kind() == reflect.Func @@ -97,11 +62,6 @@ func IsErrorType(t reflect.Type) bool { return t == errorType || t.Implements(errorType) } -// IsContextType returns whether `t` is context.Context type. -func IsContextType(t reflect.Type) bool { - return t == contextType || t.Implements(contextType) -} - // ReturnNothing returns whether the function has no return value. func ReturnNothing(t reflect.Type) bool { return t.NumOut() == 0 @@ -153,8 +113,9 @@ func IsPrimitiveValueType(t reflect.Type) bool { return true case reflect.Bool: return true + default: + return false } - return false } // IsValueType returns whether the input type is the primitive value type and their diff --git a/util/type_mock.go b/util/type_mock.go deleted file mode 100644 index 02bb5dc0..00000000 --- a/util/type_mock.go +++ /dev/null @@ -1,200 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: type.go -// -// Generated by this command: -// -// mockgen -build_flags="-mod=mod" -package=util -source=type.go -destination=type_mock.go -// - -// Package util is a generated GoMock package. -package util - -import ( - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockBeanSelector is a mock of BeanSelector interface. -type MockBeanSelector struct { - ctrl *gomock.Controller - recorder *MockBeanSelectorMockRecorder - isgomock struct{} -} - -// MockBeanSelectorMockRecorder is the mock recorder for MockBeanSelector. -type MockBeanSelectorMockRecorder struct { - mock *MockBeanSelector -} - -// NewMockBeanSelector creates a new mock instance. -func NewMockBeanSelector(ctrl *gomock.Controller) *MockBeanSelector { - mock := &MockBeanSelector{ctrl: ctrl} - mock.recorder = &MockBeanSelectorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBeanSelector) EXPECT() *MockBeanSelectorMockRecorder { - return m.recorder -} - -// MockBeanDefinition is a mock of BeanDefinition interface. -type MockBeanDefinition struct { - ctrl *gomock.Controller - recorder *MockBeanDefinitionMockRecorder - isgomock struct{} -} - -// MockBeanDefinitionMockRecorder is the mock recorder for MockBeanDefinition. -type MockBeanDefinitionMockRecorder struct { - mock *MockBeanDefinition -} - -// NewMockBeanDefinition creates a new mock instance. -func NewMockBeanDefinition(ctrl *gomock.Controller) *MockBeanDefinition { - mock := &MockBeanDefinition{ctrl: ctrl} - mock.recorder = &MockBeanDefinitionMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBeanDefinition) EXPECT() *MockBeanDefinitionMockRecorder { - return m.recorder -} - -// BeanName mocks base method. -func (m *MockBeanDefinition) BeanName() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BeanName") - ret0, _ := ret[0].(string) - return ret0 -} - -// BeanName indicates an expected call of BeanName. -func (mr *MockBeanDefinitionMockRecorder) BeanName() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeanName", reflect.TypeOf((*MockBeanDefinition)(nil).BeanName)) -} - -// Created mocks base method. -func (m *MockBeanDefinition) Created() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Created") - ret0, _ := ret[0].(bool) - return ret0 -} - -// Created indicates an expected call of Created. -func (mr *MockBeanDefinitionMockRecorder) Created() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Created", reflect.TypeOf((*MockBeanDefinition)(nil).Created)) -} - -// ID mocks base method. -func (m *MockBeanDefinition) ID() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ID") - ret0, _ := ret[0].(string) - return ret0 -} - -// ID indicates an expected call of ID. -func (mr *MockBeanDefinitionMockRecorder) ID() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockBeanDefinition)(nil).ID)) -} - -// Interface mocks base method. -func (m *MockBeanDefinition) Interface() any { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Interface") - ret0, _ := ret[0].(any) - return ret0 -} - -// Interface indicates an expected call of Interface. -func (mr *MockBeanDefinitionMockRecorder) Interface() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Interface", reflect.TypeOf((*MockBeanDefinition)(nil).Interface)) -} - -// Type mocks base method. -func (m *MockBeanDefinition) Type() reflect.Type { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Type") - ret0, _ := ret[0].(reflect.Type) - return ret0 -} - -// Type indicates an expected call of Type. -func (mr *MockBeanDefinitionMockRecorder) Type() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*MockBeanDefinition)(nil).Type)) -} - -// TypeName mocks base method. -func (m *MockBeanDefinition) TypeName() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TypeName") - ret0, _ := ret[0].(string) - return ret0 -} - -// TypeName indicates an expected call of TypeName. -func (mr *MockBeanDefinitionMockRecorder) TypeName() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TypeName", reflect.TypeOf((*MockBeanDefinition)(nil).TypeName)) -} - -// Value mocks base method. -func (m *MockBeanDefinition) Value() reflect.Value { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Value") - ret0, _ := ret[0].(reflect.Value) - return ret0 -} - -// Value indicates an expected call of Value. -func (mr *MockBeanDefinitionMockRecorder) Value() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockBeanDefinition)(nil).Value)) -} - -// Wired mocks base method. -func (m *MockBeanDefinition) Wired() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Wired") - ret0, _ := ret[0].(bool) - return ret0 -} - -// Wired indicates an expected call of Wired. -func (mr *MockBeanDefinitionMockRecorder) Wired() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wired", reflect.TypeOf((*MockBeanDefinition)(nil).Wired)) -} - -// MockConverter is a mock of Converter interface. -type MockConverter struct { - ctrl *gomock.Controller - recorder *MockConverterMockRecorder - isgomock struct{} -} - -// MockConverterMockRecorder is the mock recorder for MockConverter. -type MockConverterMockRecorder struct { - mock *MockConverter -} - -// NewMockConverter creates a new mock instance. -func NewMockConverter(ctrl *gomock.Controller) *MockConverter { - mock := &MockConverter{ctrl: ctrl} - mock.recorder = &MockConverterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockConverter) EXPECT() *MockConverterMockRecorder { - return m.recorder -} diff --git a/util/type_mock_test.go b/util/type_mock_test.go deleted file mode 100644 index ea2e999e..00000000 --- a/util/type_mock_test.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util_test - -import ( - "reflect" - "testing" - - "github.com/go-spring/spring-core/util" - "github.com/go-spring/spring-core/util/assert" - "go.uber.org/mock/gomock" -) - -func TestBeanSelector(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - g := util.NewMockBeanSelector(ctrl) - g.EXPECT() -} - -func TestBeanDefinition(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - g := util.NewMockBeanDefinition(ctrl) - g.EXPECT().Type().Return(reflect.TypeOf(3)) - assert.Equal(t, g.Type(), reflect.TypeOf(3)) - g.EXPECT().Value().Return(reflect.ValueOf(3)) - assert.Equal(t, g.Value(), reflect.ValueOf(3)) - g.EXPECT().BeanName().Return("") - assert.Equal(t, g.BeanName(), "") - g.EXPECT().TypeName().Return("") - assert.Equal(t, g.TypeName(), "") - g.EXPECT().ID().Return("") - assert.Equal(t, g.ID(), "") - g.EXPECT().Created().Return(false) - assert.Equal(t, g.Created(), false) - g.EXPECT().Wired().Return(true) - assert.Equal(t, g.Wired(), true) - g.EXPECT().Interface().Return(nil) - assert.Equal(t, g.Interface(), nil) -} - -func TestConverter(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - g := util.NewMockConverter(ctrl) - g.EXPECT() -} diff --git a/util/type_test.go b/util/type_test.go index 4b111ee8..d2efc204 100644 --- a/util/type_test.go +++ b/util/type_test.go @@ -17,7 +17,6 @@ package util_test import ( - "context" "errors" "fmt" "io" @@ -495,14 +494,6 @@ func TestIsBeanType(t *testing.T) { } } -func TestIsConverter(t *testing.T) { - assert.False(t, util.IsConverter(reflect.TypeOf(3))) - assert.False(t, util.IsConverter(reflect.TypeOf(func() {}))) - assert.False(t, util.IsConverter(reflect.TypeOf(func(key string) {}))) - assert.False(t, util.IsConverter(reflect.TypeOf(func(key string) string { return "" }))) - assert.True(t, util.IsConverter(reflect.TypeOf(func(key string) (string, error) { return "", nil }))) -} - func TestIsErrorType(t *testing.T) { err := fmt.Errorf("error") assert.True(t, util.IsErrorType(reflect.TypeOf(err))) @@ -510,13 +501,6 @@ func TestIsErrorType(t *testing.T) { assert.True(t, util.IsErrorType(reflect.TypeOf(err))) } -func TestIsContextType(t *testing.T) { - ctx := context.TODO() - assert.True(t, util.IsContextType(reflect.TypeOf(ctx))) - ctx = context.WithValue(context.TODO(), "a", "3") - assert.True(t, util.IsContextType(reflect.TypeOf(ctx))) -} - func TestReturnNothing(t *testing.T) { assert.True(t, util.ReturnNothing(reflect.TypeOf(func() {}))) assert.True(t, util.ReturnNothing(reflect.TypeOf(func(key string) {})))