mirror of
https://github.com/astaxie/beego.git
synced 2024-07-03 00:44:15 +00:00
232 lines
6.3 KiB
Go
232 lines
6.3 KiB
Go
|
// Copyright 2020
|
||
|
//
|
||
|
// 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 bean
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
"github.com/astaxie/beego/pkg/logs"
|
||
|
)
|
||
|
|
||
|
const DefaultValueTagKey = "default"
|
||
|
|
||
|
// TagAutoWireBeanFactory wire the bean based on Fields' tag
|
||
|
// if field's value is "zero value", we will execute injection
|
||
|
// see reflect.Value.IsZero()
|
||
|
// If field's kind is one of(reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice
|
||
|
// reflect.UnsafePointer, reflect.Array, reflect.Uintptr, reflect.Complex64, reflect.Complex128
|
||
|
// reflect.Ptr, reflect.Struct),
|
||
|
// it will be ignored
|
||
|
type TagAutoWireBeanFactory struct {
|
||
|
// we allow user register their TypeAdapter
|
||
|
Adapters map[string]TypeAdapter
|
||
|
|
||
|
// FieldTagParser is an extension point which means that you can custom how to read field's metadata from tag
|
||
|
FieldTagParser func(field reflect.StructField) *FieldMetadata
|
||
|
}
|
||
|
|
||
|
// NewTagAutoWireBeanFactory create an instance of TagAutoWireBeanFactory
|
||
|
// by default, we register Time adapter, the time will be parse by using layout "2006-01-02 15:04:05"
|
||
|
// If you need more adapter, you can implement interface TypeAdapter
|
||
|
func NewTagAutoWireBeanFactory() *TagAutoWireBeanFactory {
|
||
|
return &TagAutoWireBeanFactory{
|
||
|
Adapters: map[string]TypeAdapter{
|
||
|
"Time": &TimeTypeAdapter{Layout: "2006-01-02 15:04:05"},
|
||
|
},
|
||
|
|
||
|
FieldTagParser: func(field reflect.StructField) *FieldMetadata {
|
||
|
return &FieldMetadata{
|
||
|
DftValue: field.Tag.Get(DefaultValueTagKey),
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AutoWire use value from appCtx to wire the bean, or use default value, or do nothing
|
||
|
func (t *TagAutoWireBeanFactory) AutoWire(ctx context.Context, appCtx ApplicationContext, bean interface{}) error {
|
||
|
if bean == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
v := reflect.Indirect(reflect.ValueOf(bean))
|
||
|
|
||
|
bm := t.getConfig(v)
|
||
|
|
||
|
// field name, field metadata
|
||
|
for fn, fm := range bm.Fields {
|
||
|
|
||
|
fValue := v.FieldByName(fn)
|
||
|
if len(fm.DftValue) == 0 || !t.needInject(fValue) || !fValue.CanSet() {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// handle type adapter
|
||
|
typeName := fValue.Type().Name()
|
||
|
if adapter, ok := t.Adapters[typeName]; ok {
|
||
|
dftValue, err := adapter.DefaultValue(ctx, fm.DftValue)
|
||
|
if err == nil {
|
||
|
fValue.Set(reflect.ValueOf(dftValue))
|
||
|
continue
|
||
|
} else {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch fValue.Kind() {
|
||
|
case reflect.Bool:
|
||
|
if v, err := strconv.ParseBool(fm.DftValue); err != nil {
|
||
|
return errors.WithMessage(err,
|
||
|
fmt.Sprintf("can not convert the field[%s]'s default value[%s] to bool value",
|
||
|
fn, fm.DftValue))
|
||
|
} else {
|
||
|
fValue.SetBool(v)
|
||
|
continue
|
||
|
}
|
||
|
case reflect.Int:
|
||
|
if err := t.setIntXValue(fm.DftValue, 0, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
case reflect.Int8:
|
||
|
if err := t.setIntXValue(fm.DftValue, 8, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
case reflect.Int16:
|
||
|
if err := t.setIntXValue(fm.DftValue, 16, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Int32:
|
||
|
if err := t.setIntXValue(fm.DftValue, 32, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Int64:
|
||
|
if err := t.setIntXValue(fm.DftValue, 64, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Uint:
|
||
|
if err := t.setUIntXValue(fm.DftValue, 0, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
case reflect.Uint8:
|
||
|
if err := t.setUIntXValue(fm.DftValue, 8, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Uint16:
|
||
|
if err := t.setUIntXValue(fm.DftValue, 16, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
case reflect.Uint32:
|
||
|
if err := t.setUIntXValue(fm.DftValue, 32, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Uint64:
|
||
|
if err := t.setUIntXValue(fm.DftValue, 64, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.Float32:
|
||
|
if err := t.setFloatXValue(fm.DftValue, 32, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
case reflect.Float64:
|
||
|
if err := t.setFloatXValue(fm.DftValue, 64, fn, fValue); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
continue
|
||
|
|
||
|
case reflect.String:
|
||
|
fValue.SetString(fm.DftValue)
|
||
|
continue
|
||
|
|
||
|
// case reflect.Ptr:
|
||
|
// case reflect.Struct:
|
||
|
default:
|
||
|
logs.Warn("this field[%s] has default setting, but we don't support this type: %s",
|
||
|
fn, fValue.Kind().String())
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *TagAutoWireBeanFactory) setFloatXValue(dftValue string, bitSize int, fn string, fv reflect.Value) error {
|
||
|
if v, err := strconv.ParseFloat(dftValue, bitSize); err != nil {
|
||
|
return errors.WithMessage(err,
|
||
|
fmt.Sprintf("can not convert the field[%s]'s default value[%s] to float%d value",
|
||
|
fn, dftValue, bitSize))
|
||
|
} else {
|
||
|
fv.SetFloat(v)
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (t *TagAutoWireBeanFactory) setUIntXValue(dftValue string, bitSize int, fn string, fv reflect.Value) error {
|
||
|
if v, err := strconv.ParseUint(dftValue, 10, bitSize); err != nil {
|
||
|
return errors.WithMessage(err,
|
||
|
fmt.Sprintf("can not convert the field[%s]'s default value[%s] to uint%d value",
|
||
|
fn, dftValue, bitSize))
|
||
|
} else {
|
||
|
fv.SetUint(v)
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (t *TagAutoWireBeanFactory) setIntXValue(dftValue string, bitSize int, fn string, fv reflect.Value) error {
|
||
|
if v, err := strconv.ParseInt(dftValue, 10, bitSize); err != nil {
|
||
|
return errors.WithMessage(err,
|
||
|
fmt.Sprintf("can not convert the field[%s]'s default value[%s] to int%d value",
|
||
|
fn, dftValue, bitSize))
|
||
|
} else {
|
||
|
fv.SetInt(v)
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (t *TagAutoWireBeanFactory) needInject(fValue reflect.Value) bool {
|
||
|
return fValue.IsZero()
|
||
|
}
|
||
|
|
||
|
// getConfig never return nil
|
||
|
func (t *TagAutoWireBeanFactory) getConfig(beanValue reflect.Value) *BeanMetadata {
|
||
|
fms := make(map[string]*FieldMetadata, beanValue.NumField())
|
||
|
for i := 0; i < beanValue.NumField(); i++ {
|
||
|
// f => StructField
|
||
|
f := beanValue.Type().Field(i)
|
||
|
fms[f.Name] = t.FieldTagParser(f)
|
||
|
}
|
||
|
return &BeanMetadata{
|
||
|
Fields: fms,
|
||
|
}
|
||
|
}
|