在开始学习beego的过滤器之前,说一个项目开发中遇到的一个bug,这个bug是hybris自己的,导致项目上线后backoffice的一些功能出现问题,当时在服务器上看到日志显示的是某个bean创建失败,原因是依赖类型错误,就是它需要的属性是A类型,但是实际却是B类型。自己查看了spring-bean的配置文件发现有重名bean的情况,但是这两个bean定义的配置文件不同,所以配置文件并不会报错。自己又看了下相应的代码,发现它采用的注解是@Resource,我们知道这个注解是java自身的注解,它注入的时候是按名称注入的,而spring默认采用的单列,也就是说spring创建bean的时候会去查看容器内是否有同名的bean存在,不存在才会创建新的bean。这就是这个bug问题所在,因为bean同名,导致需要类型A的bean没有创建,因为容器内已经有同名但类型为B的bean,因此导致了创建该bean错误,这应该是问题原因。所以最后方法就是修改一个配置文件中bean的别名以及相关的代码。个人觉得如果将注解换成@Autowired,即按照类型注入,应该也能解决这个问题,但是这里还会有个问题,就是如果该类型有多个实现可能依然会出错。spring采用配置文件方式开发,因为有多个配置文件,所以定义bean的时候一定要注意这一点。
beego过滤器
接下来继续学习beego的框架的过滤器,关于beego框架我还是抱着了解和掌握的,不会去过多的学习,毕竟框架这么多,很难什么都去兼顾。个人觉得学习的重点还是应该放在语言本身,go如此,java也一样。上次学习beego的时候说过beego的过滤器和springmvc的拦截器有相似之处,过滤器也好拦截器也罢其实用处大同小异,那就是hold住用户请求以便做一些相关处理。
beego的过滤器函数如下:
beego.InsertFilter(pattern string, position int, filter FilterFunc, params ...bool)
第一个参数表示过滤的路由规则,支持通配符,这点和java的过滤器、拦截器一样;
第二个参数就是过滤器的位置,这个值是个常量,beego支持的有5种,即:BeforeStatic 静态地址之前,BeforeRouter 寻找路由之前,BeforeExec 找到路由之后,但是在执行相应的 controller 之前,AfterExec 执行完 controller 之后执行,FinishRouter 执行完整个逻辑之后执行。
第三个参数为可选参数,都是bool类型。第一个设置 returnOnOutput 的值(默认 true), 即如果有输出是否跳过其他过滤器,默认只要有输出就不再执行其他过滤器,即执行完controller之后不会执行后面的过滤器;第二个表示是否重置过滤器的参数,默认是 false。关于这两个参数的区别看下下面的例子。
首先在main函数里面添加所有的过滤器,并且将过滤器函数的第三个参数全部显性的设置成默认值,代码如下:
func main() {
// 过滤器
beego.InsertFilter("/*", beego.BeforeExec, controllers.BeforeExecFilter,true,false)
beego.InsertFilter("/*", beego.BeforeRouter, controllers.BeforeRouterFilter,true,false)
beego.InsertFilter("/*", beego.BeforeStatic, controllers.BeforeStaticFilter,true,false)
beego.InsertFilter("/*", beego.AfterExec, controllers.AfterExecFilter,true,false)
beego.InsertFilter("/*", beego.FinishRouter, controllers.FinishRouterFilter,true,false)
// run方法前注册错误handler
beego.ErrorController(&controllers.ErrorController{})
beego.Run()
}
然后启动项目并访问,看下日志输出情况:
根据日志输出内容可以看出过滤器的执行顺序,但是我们发现controller执行后的过滤器没有执行,这是因为这个controller有输出,所以之后的过滤器没有执行,改成false再执行一次看下日志输出结果
根据修改后的日志可以看出,AfterExecFilter和FinishRouterFilter在controller执行后都执行了,所以如果需要在controller执行后做一些操作的话一定要将这个参数设置陈false。
第二个bool类型的参数表示是否重置过滤器的参数,即在执行过滤器的时候是否重置 :splat 参数,关于这一点我看了一下文档,不是特别清楚什么意思,而且我设置成true和false分别访问没有看出有什么不同的地方,我也没理解它的用途到底是什么,我官方文档提问了,如果有答案我再来更新吧。
使用过滤器的时候如果有用到session,必须在BeforeStatic过滤器之后才能使用,因为在BeforeStatic之前,session没有初始化。
自定义标签
接下来更正一个上次的错误,就是关于数据模型定义的。先看一下上次用到的一个数据模型代码:
type User struct {
Id int
UserName string
Age int
Sex string
Mobile string
Password string
Email string
}
对于数据模型一般我们都会使用go的标签来重新命名,比如转json或者form表单转对象以及和数据库表的列名对应等等。在和数据库表映射时,如果不专门指定表的列名,那么名称转换的时候可能会出现问题,比如上面定义的User,UserName会转换成user_name,如果数据库的列名是username,那么查询的时候就会出现问题,所以一般会指定字段名称,比如我就想将UserName用username表示,那么需要使用"orm标签"而不是"column"标签,上次代码中我的表达方式是这样的:
UserName string `form:"username" column:"username" json:"userName"`
因为当时我的数据库列名是user_name,自己当时没有发现这个问题,以为"column"标签生效,结果这次重新安装postgresql自己定义表后发现了问题,即"column"标签是错误的,正确的用法应该如下:
UserName string `form:"username" orm:"column(username)" json:"userName"`
到这里可能很多小伙伴就会比较好奇,go到底有多少标签(Tag)??除了"form、json、orm"还有没有其他标签,当然是有的,但是需要明白一点这些标签中orm是beego框架的,如果使用的是其他ORM框架,比如gorm,使用"orm"标签是无效的,应该使用"gorm"。go语言本身自带的有"json、xml、form",也可能还有其他的只是我不知道而已。记得上次学习beego入门的时候说过,go的标签可以看作是java的注解,至少部分功能上相同。我们知道java是可以自定义注解,然后通过反射的形式来获取对应的名称和值的,go也是一样,也可以自定义标签,并指定相应的值,然后通过反射获取,通过一个简单的代码来看一下:
// 自定义标签
type CustomStruct struct {
Name string `customTag:"userName" tags:"user_name"`
Password string `customTag:"pwd"`
Age int `customTag:"userAge"`
}
func main() {
cust := CustomStruct{"张三", "123456", 22}
t := reflect.TypeOf(cust)
//val := reflect.ValueOf(cust)
//for i := 0; i < val.NumField(); i++ {
// println(val.Field(i).Kind().String())
//}
field, _ := t.FieldByName("Name")
println(">>>> custom struct field Tag=" + field.Tag + " <<<<")
println(">>>> struct field name's customTag value=" + field.Tag.Get("customTag") + " <<<<")
v, _ := field.Tag.Lookup("tags")
println(">>>> struct field name's tags value=" + v + " <<<<")
field, _ = t.FieldByName("Password")
println(">>>> struct field password's tag=" + field.Tag + " <<<<")
field, _ = t.FieldByName("Age")
println(">>>> struct field age's tag=" + field.Tag + " <<<<")
}
首先创建一个struct结构,并设置了3个属性,然后使用了自定义的Tag,其中Name属性使用了两个自定义Tag。main函数中首先初始化了一个对象,然后通过反射获取其类型,获取类型之后得到了属性,然后对属性进行操作获取自定义标签的Tag或者某个Tag名称的值,以标签的名称为key获取value时使用了Get("key")和Lookup("key")两种方式,其实Get就是调用的Lookup。程序运行后最终的输出结果如下:
>>>> custom struct field Tag=customTag:"userName" tags:"user_name" <<<<
>>>> struct field name's customTag value=userName <<<<
>>>> struct field name's tags value=user_name <<<<
>>>> struct field password's tag=customTag:"pwd" <<<<
>>>> struct field age's tag=customTag:"userAge" <<<<
go的反射相对java来讲感觉弱了一点,当然自己只是感觉,毕竟反射这部分内容自己还没有好好的去学习,也是今天用到了Tag临时看了一下。但是其实这里我还有个疑问,就是如何才知道我的struct属性上定义了多少个Tag以及Tag的名称是什么应该怎么获取?根据上面输出的情况看,即使某个属性有多个不同类型的标签,输出的时候这个Tag都是一个整体,而Tag只提供了Get("key")和Lookup("key")两个方法。如果真的想获取Tag的数量以及名称,只能自己来实现了。
今天的学习就到这里,内容相对比较少,而且自己对框架不是很热衷,我觉得需要的时候在学习也是来的及的。除了MVC和ORM之外beego其他内置的模块自己暂时不会去学习,比如session、httplib、cache等等,当然这些内容其实都是非常值得一学的,但是因为自己现在没有在做专门的go开发,所以我觉得重点还是放到语言本身更好一些。
代码已经更新到我的github