前言嗨,我的小阿歌回来了。两周没更新了,最近比较忙,比较懒,所以,嗯,你懂的。不过今天带来的分享绝对是干巴巴的,需要在实际项目开发中使用,所以为了能够讲解清楚,特地写了一个示例,仅供参考。本文将从示例开始学习,示例已上传至github,可自行下载。好了,废话少说,我知道你们等不及了,我们直接开始吧!!!WireDependencyInjection在介绍wire之前,我们先来了解一下什么是依赖注入。用过Spring的同学应该对这个不陌生。最常见的控制反转(IOC)方式称为依赖注入。将依赖类作为行参数放入依赖中的类称为依赖注入。所以你可能不明白。通俗点说,一个实例化对象本来是接受各种参数来构造对象的,现在只接受一个参数,对象的依赖被注入并与其构造方法解耦。构建他的控制操作也交给了第三方,即控制反转。比如:go中没有类的概念,它是以结构体的形式体现的。假设我们有一个ship类和一个pulp类,我们现在要设置ship有12个桨,那么我们可以写如下代码:packagemainimport("fmt")typeshipstruct{pulp*pulp}funcNewShip(纸浆*纸浆)*ship{return&ship{pulp:pulp,}}typepulpstruct{countint}funcNewpulp(countint)*pulp{return&pulp{count:count,}}funcmain(){p:=Newpulp(12)s:=NewShip(p)fmt.Println(s.pulp.count)}相信大家一眼就看出问题所在。每当需求发生变化时,我们都必须重新创建一个对象来指定桨。这段代码不容易维护,让我们绕过它。packagemainimport("fmt")typeshipstruct{pulp*pulp}funcNewShip(pulp*pulp)*ship{return&ship{pulp:pulp,}}typepulpstruct{countint}funcNewpulp()*pulp{返回&pulp{}}func(c*pulp)set(countint){c.count=count}func(c*pulp)get()int{returnc.count}funcmain(){p:=Newpulp()s:=NewShip(p)s.pulp.set(12)fmt.Println(s.pulp.get())}这段代码的优点是代码松耦合,易于维护,易于测试。如果我们现在更改要求并需要20个桨,只需s.pulp.set(20)即可。wirewire的使用有两个基本概念,Provider(构造函数)和Injector(注入器)。Provider其实就是一个创建函数,大家都懂的。我们上面的InitializeCron是Injector。每个注入器实际上是一个对象创建和初始化函数。在这个函数中,我们只需要告诉wire要创建什么类型的对象,wire工具就会生成一个函数为我们完成该类依赖的对象创建和初始化。拉了这么久才把线引出来。上面的代码虽然实现了依赖注入,但是在代码量小,结构不复杂的情况下,我们自己实现依赖是没有问题的。当结构之间的关系变得非常复杂时,手动创建依赖关系,然后再进行组装会变得异常繁琐且容易出错。于是线材的作用就来了。在使用之前,让我们先安装电线。$gogetgithub.com/google/wire/cmd/wire执行这条命令会在$GOPATH/bin下生成一个可执行程序wire,这就是代码生成器。不要忘记将$GOPATH/bin添加到系统环境变量$PATH中。首先,基于上面这个简单的例子,我们来看看wire的使用方法。我们先创建一个wire文件,文件内容如下://+buildwireinjectpackagemainimport("github.com/google/wire")typeShipstruct{Pulp*Pulp}funcNewShip(pulp*Pulp)*Ship{return&Ship{纸浆:纸浆,}}typePulpstruct{Countint}funcNewPulp()*Pulp{return&Pulp{}}func(c*Pulp)set(countint){c.count=count}func(c*Pulp)get()int{returnc.count}funcInitShip()*Ship{wire.Build(NewPulp,NewShip,)return&Ship{}}funcmain(){}其中InitShip函数的返回值是我们需要创建的对象,Wire只需要知道类型,它返回什么并不重要。在函数中,我们调用wire.Build()传入船舶所依赖的类型构造函数。这样我们就写好了,现在我们需要到控制台去执行wire。$wirewire:asong.cloud/Golang_Dream/wire_cron_example/ship:wrote/Users/asong/go/src/asong.cloud/Golang_Dream/wire_cron_example/ship/wire_gen.go我们看到生成了wire_gen.go文件://代码由Wire生成。不要编辑。//go:generatewire//+build!wireinjectpackagemain//来自mian.go:funcInitShip()*Ship{pulp:=NewPulp()ship:=NewShip(pulp)returnship}//的注入器mian.go:typeShipstruct{pulp*Pulp}funcNewShip(pulp*Pulp)*Ship{return&Ship{pulp:pulp,}}typePulpstruct{countint}funcNewPulp()*Pulp{返回&Pulp{}}func(c*Pulp)set(countint){c.count=count}func(c*Pulp)get()int{returnc.count}funcmain(){}可以看出生成了这个文件InitShip()函数按照刚才的定义,依赖绑定关系也实现了。我们可以直接调用这个函数,这样就省去了很多自己实现依赖绑定关系的代码。注意:如果你是第一次使用线材,肯定会遇到问题。生成的代码会和原代码冲突,因为两者定义了相同的函数funcInitShip()*Ship,所以需要在原文件的第一行添加//+buildwireinject,并且必须有一个空行与包名称,这解决了冲突。上面的例子相当简单。让我们再看一个例子。我们在开发日常web后台的时候,代码是分层的。比较熟悉的有dao、service、controller、model等,其实dao、service、controller是依次调用的。controller调用service层,service层调用dao层,形成依赖关系。在实际开发中,我们采用分层依赖注入的方式,使其更有层次感,代码也更易于维护。所以,我写了一个示例,让我们学习如何使用它。这个使用cron定时任务代替controller来代替controller。后面会讲解cron定时任务。//+buildwireinjectpackagewireimport("github.com/google/wire""asong.cloud/Golang_Dream/wire_cron_example/config""asong.cloud/Golang_Dream/wire_cron_example/cron""asong.cloud/Golang_Dream/wire_cron_example/cron/task""asong.cloud/Golang_Dream/wire_cron_example/dao""asong.cloud/Golang_Dream/wire_cron_example/service")funcInitializeCron(mysql*config.Mysql)*cron.Cron{wire.Build(dao.NewClientDB,dao.NewUserDB,service.NewUserService,task.NewScanner,cron.NewCron,)return&cron.Cron{}}我们看一下这段代码,dao.NewClientDB创建了一个*sql.DB对象,这个对象依赖于mysql的配置文件,dao.NewUserDB即创建一个*UserDB对象,它依赖于*sql.DB,service.NewUserService创建一个UserService对象,它依赖于*UserDB对象,task.NewScanner创建一个*Scanner对象,它依赖于*UserService对象,cron。NewCron创建一个*Cron对象,它依赖于*Scanner对象。其实这里是一层一层的绑定关系,一层层调整,层级清晰,代码易于维护。好了,基本的使用就介绍到这里了,接下来我们来学习一下cron。Cron基础学习在日常开发或运维中,我们经常会遇到一些周期性执行的任务或需求,例如:每隔一段时间执行一个脚本,每月执行一次操作。Linux为我们提供了一种便捷的方式——crontab定时任务;crontab是一个自定义的定时器,我们可以使用crontab命令来定时执行指定的系统命令或者shellscript脚本。这个时间间隔的写法和我们平时用的cron表达式差不多。功能是用字符或命令设置定时,周期性地执行一些操作。了解了基本概念,让我们来介绍一下cron表达式。有两种常用的cron规范格式:cronlinux系统程序使用的“标准”cron格式,以及QuartzScheduler使用的cron格式。两者的区别是一个支持seconds字段,一个不支持,但是差距不是很大。我们将在接下来的解释中包括秒字段,它没有任何作用。cron表达式是一个字符串,由6个空格分成7个字段,每个字段代表一个时间意义。格式如下:[秒][分][时][日][月][周][年][年]通常是可选的,实际上由前六部分组成。关于各部分的定义,我们以表格的形式呈现:该字段是否必填,范围通配秒为0-59,-*/分钟为0-59,-*/小时为0-23,-*/天是1-31,-*?/LW月份是1-12或JAN-DEC,-*/周是1-7或SUN-SAT,-*?/L#yearno1970-2099,-*/看到这个取值范围还是很容易理解的。最难理解的是通配符。让我们关注通配符,这里指的是在两个或多个时间点执行。如果我们在“分钟”字段中定义5、10、15,则表示定时分别在第5、10、15分钟执行Task。-这个比较容易理解就是在某个字段指定一个连续的范围。如果我们在“小时”字段中定义6-12,则表示在6点到12点之间每隔一整点触发一次。用,来表示6,7,8,9,10,11,12*表示所有的值,可以读作“每个”。如果在“Day”字段中设置*,则表示每天都会触发。?表示没有指定值。使用的场景是不需要关心当前设置的这个字段的值。例如:要在每个月的8号触发一个操作,但不关心星期几,我们可以设置0008*?/在某个域上周期性触发,这个符号就是其域中的表达式分为两部分,第一部分为初始值,除秒外会减少一个单位,例如定义5/10在“second”上,表示从开始每10秒执行一次fifth秒,而on"minute"表示从第5秒开始,每10分钟执行一次。L在英文中是LAST的意思,只能??用在“day”和“week”中。在“日”中设置表示该月的最后一天(根据当前月份,如果是二月,还会根据是否是农历年),在“周”中设置表示星期六,即相当于“7”或“SAT”。如果在“L”前加一个数字,则表示该数据的最后一个。例如在“week”上设置格式“7L”表示“本月的最后一个星期六”W表示在距离指定日期最近的工作日(周一至周五)触发,只能在“day”中使用和只能在特定数字之后使用。如果在“日”设置“15W”,则表示触发器在最接近每月15日的工作日触发。如果15号刚好是星期六,则找最近的星期五(14号)触发,如果15号是周末,则找下一个星期一(16号)触发。如果15号刚好是工作日(周一到第5周),就会在当天触发。如果是“1W”,则只能推送到本月最近的下一个工作日,不能跨月推送到上个月。#表示该月的前几周,只能用在“周”上。例如,“2#3”表示每个月的第三个星期二。学完通配符,我们来看几个例子:每天10点执行一次:0010***每10分钟执行一次:0*/10***凌晨3点执行一次每个月的第一天:0031*?每月最后一天的23:30执行一次:03023L*?每周六凌晨3点执行一次:003?*L在30和50执行一次:030,50***?在go中使用cron前面我们已经了解了基础知识,现在想在go项目中使用定时任务,应该怎么做呢?github上有个star比较高的cron库。我们可以使用robfig/cron库来开发我们的定时任务。学习之前先安装cron$goget-ugithub.com/robfig/cron/v3这是目前比较稳定的版本。现在这个版本采用标准规格。默认是没有秒。如果要在上面的字段中,我们需要创建一个cron对象来指定。稍后会显示。先来看一个简单的用法:packagemainimport("fmt""time""github.com/robfig/cron/v3")funcmain(){c:=cron.New()c.AddFunc("@every1s",func(){fmt.Println("任务在1秒后开始")})c.Start()select{}}这里我们使用cron.New创建一个cron对象来管理定时任务。调用cron对象的AddFunc()方法向管理器添加计划任务。AddFunc()接受两个参数。参数1以字符串的形式指定了触发时间规则,参数2是一个不带参数的函数,每次触发都会调用该函数。@every1s表示每秒触发一次,在@every后面加一个时间间隔,表示每隔多久触发一次。例如@every1h表示每小时触发一次,@every1m2s表示每1分2秒触发一次。time.ParseDuration()支持的所有格式都可以在这里使用。调用c.Start()开始定时循环。请注意,因为c.Start()启动了一个新的goroutine用于循环检测,所以我们在代码末尾添加了一行select{}以防止主goroutine退出。上面我们定义时间的时候,用到了cron的预定义时间规则,那么我们来学习一下他有的一些预定义时间规则:@yearly:也可以写成@annually,表示第一天0点每年。相当于0011*;@monthly:表示每月第一天0点。相当于001**;@weekly:表示一周第一天的0点,注意第一天是星期天,也就是星期六结束,星期天开始的0点。相当于00**0;@daily:也可以写成@midnight,表示每天0点。相当于00***;@hourly:表示每小时的开始。相当于0****。Cron也支持固定的时间间隔,格式如下:@every
