Java开发企业级权限管理系统 Spring Security/Apache Shiro对比分析

#1

download:Java开发企业级权限管理系统 Spring Security/Apache Shiro对比分析

Golang泛型提高了编码效率

Golang的泛型已经出来一段时间了,大家应该对它有所了解,甚至在应用中使用过。虽然Golang的泛型功能简单,而且可能会增加代码的复杂度,但是过度使用也可能会降低代码的可读性。
但不可否认的是,泛型确实使我们在使用Golang时能够提取一些常用代码,避免代码的重复拷贝,提高代码性能(避免类型转换),提高编码效率和体验,提高代码可维护性。
本文主要介绍我用Golang泛型做了什么。
工具功能
虽然标准库中已经提供了大量的工具函数,但是这些工具函数并不是由泛型实现的。为了改善体验,我们可以通过泛型来实现它们。
比如经典的数学。Max()和数学。数值算法中的Min()是float64类型,但是很多时候我们使用这些类型的int和int64。在Golang引入泛型之前,我们经常按照如下类型来实现它们,从而产生了大量的模板代码:
func MaxInt(a,b int) int {
如果a > b {
返回a
}
返回b
}

func MaxInt64(a,b int64) int64 {
如果a > b {
返回a
}
返回b
}

//…其他类型
复制代码
使用泛型时,我们只需要一个实现:
func MaxT约束。有序 T {
如果a > b {
返回a
}
返回b
}
复制代码
约束在哪里。Ordered表示可排序的类型,即[>,=,
代码地址
其他如json解析、参数检查、切片等。也可以通过泛型实现。
数据结构
Golang自己的通用容器是切片和地图。这两种数据结构实际上可以完成大部分工作,但有时我们可能需要其他数据结构,比如优先级队列和链表。
虽然Golang在容器包下有堆、列表、环三种数据结构,但是用起来说实话不是很方便,尤其是元素类型都是接口{}。使用这些结构需要各种类型转换。所以我们可以简单地复制这些代码,然后用泛型对它们进行转换,比如heap:
我们不仅使用泛型来实现,还默认将heap改为slice来实现,所以我们只需要实现一个LessFunc而不是五个。
包堆

类型LessFunc[T any] func(e1 T,e2 T) bool

类型堆[T any]结构{
h []T
lessFunc
}

func New[T any](h []T,lessFunc LessFunc[T]) *Heap[T] {
堆:= &Heap[T]{
h: h,
lessFunc: lessFunc,
}
heap.init()
返回堆
}

//移除堆顶部元素
func (h *Heap[T]) Pop() T {
n := h.Len() - 1
h.swap(0,n)
h.down(0,n)
return h.pop()
}

//获取堆顶部元素
func (h *Heap[T]) Peek() T {
返回h.h[0]
}

//向堆中添加元素
func (h *Heap[T]) Push(x T) {
h.push(x)
h.up(h.Len() - 1)
}
复制代码
代码地址
其他数据结构包括list、set、pqueue等。
模板代码
在后台业务代码中,我们经常会有很多业务处理函数,每个业务处理函数基本上都是由一些代码封装成一个HTTP接口。其实都是模板代码。例如,对于gin实现的HTTP服务,我们需要如下处理每个接口:

指定HTTP方法、URL
证明
参数绑定
处理请求
反应

可以发现参数绑定和处理响应几乎都是同一个模板代码,认证基本都是模板代码(当然有些认证可能比较复杂)。
因此,我们可以编写一个通用模板,并提取相同的部分。用户只需要用不同的接口实现指定的HTTP方法、URL和请求处理逻辑:
//处理请求
func do[Req any,Rsp any,Opt any](reqFunc ReqFunc[Req]),
serviceFunc ServiceFunc[Req,Rsp],serviceopttfunc serviceopttfunc[Req,Rsp,Opt],opts…Opt)杜松子酒。HandlerFunc {
返回函数(c *gin。上下文){
//参数绑定
req,err := BindJSONReq
如果err!=零{
返回
}
//进一步处理请求结构
if reqFunc!=零{
请求函数
}
var rsp *Rsp
//业务逻辑函数调用
if serviceFunc!=零{
rsp,err = serviceFunc(c,req)
} else if serviceOptFunc!=零{
rsp,err = serviceoptpfunc(c,req,opts…)
}否则{
panic("必须设置ServiceFunc或ServiceFuncOpt “)
}
//处理响应
处理器sp(c,Rsp,err)
}
}
复制代码
这样,现在一个接口基本上只需要一行代码就可以实现(不包括具体的业务逻辑功能):
//简单请求,不需要身份验证
e.GET(”/user/info/get “,ginrest。Do(nil,GetUserInfo))
//身份验证、绑定UID、处理
reqFunc := func(c *gin。上下文,req *UpdateUserInfoReq) {
请求。UID = GetUID©
}//这里再多一步就是说明第一个参数是ReqFunc。
e.POST(”/user/info/update ",Verify,ginrest。Do(reqFunc,UpdateUserInfo))
复制代码
地址,并实现了一个基于gin的RESTful风格模板。
对象池/缓存
Golang标准库附带了一个线程安全的高性能对象池同步。池,可以根据物体的热度自动释放物体。但是,作为对象池,我们通常只放一种类型的对象进去,但是元素是同步的。池是接口{}类型,所以我们可以简单地封装sync。池并使其元素具有特定的类型:
事实上,这里有一个简单的对象同步。Pool进行打包,然后添加一个ClearFunc()在回收对象时做一些清理操作。例如,我们需要使字节片的使用长度为零(容量保持不变)。
//创建一个新对象
键入NewFunc[T any] func() T

//清理对象
类型ClearFunc[T any] func(T) T

类型Pool[T any] struct {
p同步。泳池
clearFunc
}

func New[T any](New func New func[T],clear func clear func[T])* Pool[T]{
如果newFunc == nil {
panic("必须提供NewFunc ")
}
p := &Pool[T]{
clearFunc: clearFunc,
}
p.p.New = func() any {
返回newFunc()
}
返回p
}

//获取对象
func (p *Pool[T]) Get() T {
返回p.p.Get()。(吨)
}

//返回对象
func (p *Pool[T]) Put(t T) {
if p.clearFunc!=零{
t = p.clearFunc(t)
}
投入产出
}
复制代码
用作字节数组对象池:
new func:= func()[]字节{
返回make([]字节,大小,上限)
}
clear func:= func(b[]字节)[]字节{
return b[:0]
}
p := New(newFunc,clearFunc)
Bytes := p.Get() //这里字节的类型是[]byte
页(page的缩写)上传(字节)
复制代码
代码地址
缓存也是如此。目前大部分的缓存库都是基于接口{0}或者byte[]实现的,但是我们还是更喜欢直接操纵特定的类型,所以我们可以自己用泛型来实现(或者转换)一个缓存库。我还实现了一个通用的缓存策略库,其中包含LRU、LFU、ARC、NearlyLRU、TinyLFU等缓存策略。
摘要
如你所见,实际上Golang泛型主要提供了代码抽象和封装的能力,使我们能够编写更多可重用的代码,避免到处复制代码,从而提高代码的可维护性和可读性,并从避免类型转换中获得一点点性能提升。