Yuri’s Tech Note

技術系アウトプット

martiniからnegroniが生まれた背景とリフレクション

弊社ではGoのWebフレームワークの中でnegroniを採用している。

negroni

net/httpと直接結びついて動作する、ミドルウェアにフォーカスされたライブラリ。
セッション管理したりユーザ認証の際に利用されることが多い。

negorniでは `gin`というコマンドを使う。
ginコマンドではソースコードをビルドしてバイナリをたちあげる作業を行う。
negroniは変更をlistenしているのでソースコードを変更して保存したタイミングで更新してくれるために開発で便利。

negroniの誕生背景

negroniの作者はnegroniを作る前にmartiniというライブラリを作っていた。
martiniはリフレクションを良く使うライブラリで、そのためにGoにしては処理が遅いが、裏でよろしくやってくれて便利だった。
これはauto magicと称された。

しかし、Goの文化的に利用する側が何が行われているのか把握できないブラックボックスはよくないという批判があった。
これをうけ、作者が新しく作ったのがnegroni。

フレームワークを比較している記事
Go Web Frameworks 比較qiita.com


リフレクション

オブジェクトがそれ自身の構造や計算上の意味を取得することを可能にする。
プログラムのソースコードコンパイルされると、プログラムの構造などの情報は低レベルコードに変換される過程で失われてしまう。
リフレクションをサポートする場合、そのような情報は生成されるコードの中にメタデータとして保存される。

goにはreflectというパッケージがあるが、これを説明する前に一度型についておさらいする。

型には名前を持つ型と名前を持たない型がある。

名前を持つ型

type Team struct

名前を持たない型
`[]byte` や `*int` など型の名前と記号によって表される。

map[string]string
interface{}

複数の型をうけいれる型
型には複数の型を受け入れるインターフェース型がある。
fmt.Prinflnが例として挙げられる。

func Println(a ...interface{})(n int, err error)

型の中でも静的な型と動的な型があるが、インターフェース型はどちらも受け入れることができる。

var n int = 42         // nの(静的な)型はint
var s string = "hello" // sの(静的な)型はstring
var v interface{}      // vの(静的な)型はinterface{}
v = n                  // vの(静的な)型はinterface{}、動的な型はint
v = s                  // vの(静的な)型はinterface{}、動的な型はstring


reflect機能
reflectパッケージを使用すると変数の型についての情報を取得することができる。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type MyInt int
    var x MyInt
    v := reflect.ValueOf(x) // ValueOfでreflect.Value型のオブジェクトを取得
    fmt.Println(v.Type())   // Typeで変数の型を取得
    fmt.Println(v.Kind())   // Kindで変数に格納されている値の種類を取得
}

// main.MyInt
// int

インターフェース型の場合は動的な型の情報も取得することができる。

値を格納していない場合にはメソッドがpanicを起こすので値が格納されているか否かはIsValidメソッドでチェックする。

func HasField(v interface{}, name string) bool {
    rv := reflect.ValueOf(v)

    // ポインタ型であるか
    if rv.Kind() == reflect.Ptr {
    // ポインタの要素の型を返す
        rv = rv.Elem()
    }

    // 構造体であるか
    if rv.Kind() != reflect.Struct {
        return false
    }

    // 引数に渡された項目がオブジェクトの項目に値があるか判定して返す
    return rv.FieldByName(name).IsValid()
}

reflectについて詳しいサイト
reflect パッケージ - golang.jp