Go语言包的加载顺序如图

可以通过如下实例详细了解

代码来源于《Go实战》

代码地址:https://github.com/goinaction/code

项目代码结构

程序架构

首先分析main文件

main.main.go

package main

import (
"log"
"os" _ "github.com/goinaction/code/chapter2/sample/matchers" // 对包做初始化操作,但是不使用包里的标志符。调用包里所有的init函数
"github.com/goinaction/code/chapter2/sample/search"
) // init is called prior to main.
func init() {
// Change the device for logging to stdout.
log.SetOutput(os.Stdout)
} // main is the entry point for the program.
func main() {
// Perform the search for the specified term.
search.Run("president")
}

在执行main文件之前,会首先加载matchers、search包里的init函数和包级别的常量变量定义

加载matchers包的变量常量定义和init函数

type (
item struct {
XMLName xml.Name `xml:"item"`
PubDate string `xml:"pubDate"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
GUID string `xml:"guid"`
GeoRssPoint string `xml:"georss:point"`
} image struct {
XMLName xml.Name `xml:"image"`
URL string `xml:"url"`
Title string `xml:"title"`
Link string `xml:"link"`
} channel struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
PubDate string `xml:"pubDate"`
LastBuildDate string `xml:"lasteBuildDate"`
TTL string `xml:"ttl"`
Language string `xml:"language"`
ManagingEditor string `xml:"managingEditor"`
WebMaster string `xml:"webMaster"`
Image image `xml:"image"`
Item []item `xml:"item"`
} rssDocument struct {
XMLName xml.Name `xml:"rss"`
Channel channel `xml:"channel"`
}
) type rssMatcher struct{} func init() {
var matcher rssMatcher
search.Register("rss", matcher)
}

加载search包的常量变量定义和init函数

const dataFile = "data/data.json"

type Feed struct {
Name string `json:"site"`
URI string `json:"link"`
Type string `json:"type"`
}
type Result struct {
Field string
Content string
} type Matcher interface {
Search(feed *Feed, searchTerm string) ([]*Result, error)
}
var matchers = make(map[string]Matcher) type defaultMatcher struct{} func init() {
var matcher defaultMatcher
Register("default", matcher)
}

在回到main函数,首先执行search.Run()的函数,

func Run(searchTerm string) {
feeds, err := RetrieveFeeds()
if err != nil {
log.Fatal(err)
} results := make(chan *Result)
//添加线程等待
var waitGroup sync.WaitGroup
waitGroup.Add(len(feeds)) for _, feed := range feeds {
log.Printf("%v", *feed)
matcher, exists := matchers[feed.Type]
if !exists {
matcher = matchers["default"]
} //起协程是执行匹配
go func(matcher Matcher, feed *Feed) {
Match(matcher, feed, searchTerm, results)
waitGroup.Done()
}(matcher, feed)
} go func() {
waitGroup.Wait()
close(results)
}()
Display(results)
}

在Run函数中,执行RetrieveFeeds()函数

func RetrieveFeeds() ([]*Feed, error) {
//打开配置文件
file, err := os.Open(dataFile)
if err != nil {
return nil, err
} defer file.Close() var feeds []*Feed
//解析json格式到Feed结构体
err = json.NewDecoder(file).Decode(&feeds)
return feeds, err
}

执行匹配

func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) {
searchResults, err := matcher.Search(feed, searchTerm)
if err != nil {
log.Println(err)
return
} //将匹配到的结果写入chan
for _, result := range searchResults {
results <- result
}
}

执行搜索

func (m rssMatcher) Search(feed *search.Feed, searchTerm string) ([]*search.Result, error) {
var results []*search.Result
log.Printf("search Feed Type[%s] Site[%s] For URI[%s]\n", feed.Type, feed.Name, feed.URI) document, err := m.retrieve(feed)
if err != nil {
return nil, err
}
for _, channelItem := range document.Channel.Item {
matched, err := regexp.MatchString(searchTerm, channelItem.Title)
if err != nil {
return nil, err
} if matched {
results = append(results, &search.Result{
Field: "Title",
Content: channelItem.Title,
})
} matched, err = regexp.MatchString(searchTerm, channelItem.Description)
if err != nil {
return nil, err
}
if matched {
results = append(results, &search.Result{
Field: "Description",
Content: channelItem.Description,
})
}
}
return results, nil
}
func (m rssMatcher) retrieve(feed *search.Feed) (*rssDocument, error) {
if feed.URI == "" {
return nil, errors.New("NO rss feed uri provided")
} //http获取uri返回
resp, err := http.Get(feed.URI)
if err != nil {
return nil, err
} defer resp.Body.Close() if resp.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Response Error %d\n", resp.StatusCode)
} //将结果解析到xml格式的struct
var document rssDocument
err = xml.NewDecoder(resp.Body).Decode(&document)
return &document, err
}

最后展示结构

func Display(results chan *Result) {
//从chan中读取,会阻塞直到chan close
for result := range results {
log.Printf("%s:\n%s\n\n", result.Field, result.Content)
}
}

Go实例解析的更多相关文章

  1. exec函数族实例解析

    exec函数族实例解析 fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间.堆.栈等资源的副本.注意,子进程持有的是上述存储空间的 ...

  2. [Reprint] C++函数模板与类模板实例解析

    这篇文章主要介绍了C++函数模板与类模板,需要的朋友可以参考下   本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程( ...

  3. [Reprint]C++普通函数指针与成员函数指针实例解析

    这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下   C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...

  4. JavaWeb实现文件上传下载功能实例解析

    转:http://www.cnblogs.com/xdp-gacl/p/4200090.html JavaWeb实现文件上传下载功能实例解析 在Web应用系统开发中,文件上传和下载功能是非常常用的功能 ...

  5. Android实例-Delphi开发蓝牙官方实例解析(XE10+小米2+小米5)

    相关资料:1.http://blog.csdn.net/laorenshen/article/details/411498032.http://www.cnblogs.com/findumars/p/ ...

  6. Android开发之IPC进程间通信-AIDL介绍及实例解析

    一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用 ...

  7. easyUI:ComboTree and comselector使用实例解析

    ComboTree 使用场景:故名思意,ComboTree是combox和Tree的结合体,在需要通过选择得到某一个node值的时候触发. 栗子: 定义: 使用标签创建树形下拉框. Comselect ...

  8. Maven--多模块依赖实例解析(五)

    <Maven--搭建开发环境(一)> <Maven--构建企业级仓库(二)> <Maven—几个需要补充的问题(三)> <Maven—生命周期和插件(四)&g ...

  9. SoapUI简介和入门实例解析

    SoapUI简介 SoapUI是一个开源测试工具,通过soap/http来检查.调用.实现Web Service的功能/负载/符合性测试.该工具既可作为一个单独的测试软件使用,也可利用插件集成到Ecl ...

  10. shell test和find命令实例解析

    shell test和find命令实例解析 下面以\build\core\product.mk相关部分来学习 define _find-android-products-files $(shell t ...

随机推荐

  1. uboot使用笔记

    一.从最基础的uboot开始 首先是打算直接用程序把已有的镜像烧写进去sd卡里面,但是问题来了,烧写不进去 原因: 可能是windows 7的某些版本和win⑩的系统为了保护磁盘,设定了保护等级,直接 ...

  2. Linux Network Command

    查看 内外网访问ipnetstat -an download file from server scp -r root@139.xxx.xxx.82:~/virtualbox.box /tmp/

  3. 【转载】IL指令集

    转载自:http://www.cnblogs.com/knowledgesea/p/5461040.html 名称 说明 Add 将两个值相加并将结果推送到计算堆栈上. Add.Ovf 将两个整数相加 ...

  4. PTA --- 时间复杂度 选择题

    1-1 2​N​​和N​N​​具有相同的增长速度. (2分) T         F 作者: DS课程组 单位: 浙江大学 1-2 (NlogN)/1000是O(N)的. (1分) T         ...

  5. [Kafka] [All about it]

    Overview 设计目标: 以O(1) 常数级时间复杂度的访问性能,提供消息持久化能力. 高吞吐率. 支持 kafka server 间的消息分区,及分布式消费,同时保证每个partition内部的 ...

  6. 2018-2019-2 网络对抗技术 20165326 Exp3 免杀原理与实践

    免杀原理与实践 目录 知识点问答 实践内容 遇到的问题 心得体会 知识点 meterpreter免杀 基础问题回答 杀软是如何检测出恶意代码的? 特征码(基于签名):模式匹配,比对特征码库 启发式:通 ...

  7. waitGroup的使用

    package main import ( "fmt" "sync" "time" ) func main() { var wg sync. ...

  8. 2019-04-23-day038-数据库的语句

    昨日回顾 补充的知识点 server端肯定是确定下来的 mysql的客户端 mysql.exe 直接在命令行就可以运行的 (学习阶段用) navicat等可视化的客户端,是第三方开发的客户端 (开发辅 ...

  9. 记reinforcement learning double DQNS

    传统的DQN算法会导致overestimate.因为在训练开始时,最大的Q值并不一定是最好的行为. 也就是说较差的行为Q值相对较大,较好的行为Q值相对较小.这时我们在更新Q值时用最大期望来计算我们作为 ...

  10. 2019/02/09 对于KinectFusion 的理解

    网上有很多关于Kinect Fusion 的详细介绍,包括各个部分的算法,思路,以及应用上的限制和优化. 在此就不多介绍了. KinectFusion 提供了非常基础的用RGB-D 相机实现的 Den ...