Martini源码剖析
- 格式:doc
- 大小:88.50 KB
- 文档页数:10
net core源码解读Net Core作为微软推出的跨平台开发框架,其源码的解读对于深入理解其内部机制和原理具有重要意义。
本文将对Net Core源码进行解读,帮助读者更好地理解这一框架。
一、源码概述Net Core源码主要包括.NET运行时、.NET标准库和 Core 等部分。
其中,.NET运行时是整个框架的核心,负责应用程序的加载、运行和管理。
.NET标准库是一组跨平台的类库,提供了丰富的API 用于开发各种应用程序。
Core则是基于.NET Core的Web开发框架,用于构建高效、可扩展的Web应用程序。
二、.NET运行时.NET运行时是Net Core框架的核心,它负责应用程序的加载、运行和管理。
在.NET运行时中,主要包括了垃圾回收器、JIT编译器、内存管理器、线程池等组件。
这些组件协同工作,为应用程序提供了稳定、高效的运行环境。
其中,垃圾回收器负责自动管理内存,防止内存泄漏和过度占用内存。
JIT编译器则负责将MSIL代码编译成本地代码,提高应用程序的运行效率。
内存管理器负责分配和释放内存,保证应用程序的内存安全。
线程池则提供了高效的线程复用机制,降低线程创建和销毁的开销。
三、.NET标准库.NET标准库是一组跨平台的类库,提供了丰富的API用于开发各种应用程序。
在.NET标准库中,主要包括了System命名空间下的众多类库,如System.Collections、System.IO、System.Threading等。
这些类库涵盖了常见的数据结构、文件操作、多线程编程等领域,为开发者提供了强大的支持。
四、 Core Core是基于.NET Core的Web开发框架,用于构建高效、可扩展的Web应用程序。
在 Core中,主要包括了Kestrel服务器、中间件、依赖注入等组件。
这些组件共同协作,实现了高效、安全的Web应用程序开发。
其中,Kestrel服务器是 Core内置的轻量级Web服务器,具有高效、可扩展的特点。
startactivityinner 源码解析摘要:1.Android 中的StartActivity 方法介绍2.StartActivity 的工作原理3.StartActivity 参数解析4.常见问题及解决方法5.实战案例分享正文:作为一名Android 开发者,熟练掌握StartActivity 方法对于实现应用程序的跳转功能至关重要。
本文将详细介绍Android 中的StartActivity 方法,解析其工作原理,并分析常见问题及解决方法。
最后,将通过实战案例分享,帮助大家更好地理解和应用StartActivity。
1.Android 中的StartActivity 方法介绍在Android 中,StartActivity 方法是Activity 类中的一个方法,用于启动另一个Activity。
通过调用这个方法,可以实现不同界面之间的跳转,使得应用程序的功能更加丰富。
StartActivity 方法的定义如下:```javapublic void startActivity(Intent intent)```参数:- Intent:表示要启动的Activity 的Intent 对象。
2.StartActivity 的工作原理StartActivity 方法启动另一个Activity 的过程可以分为以下几个步骤:- 创建一个Intent 对象,表示要启动的Activity。
- 调用startActivity 方法,将Intent 对象传递给目标Activity。
- 目标Activity 接收到Intent 对象,根据Intent 中的信息创建对应的实例。
- 目标Activity 实例启动,并设置界面布局。
- 源Activity 完成跳转,退出屏幕。
3.StartActivity 参数解析在StartActivity 方法中,Intent 对象包含了要启动的Activity 相关信息。
以下是Intent 对象中常用的参数:- action:表示Activity 启动的类型,例如ACTION_VIEW、ACTION_EDIT 等。
matter源码解析Matter是一个开源的、用于连接智能家居设备的协议。
它旨在提供一种可靠、安全的方式来连接和控制各种智能家居设备。
下面是对Matter源码的简要解析:Matter源码主要包含以下几个部分:1、协议定义:Matter使用Protocol Buffers(protobuf)定义了协议的数据结构和消息格式。
这部分代码定义了Matter协议的各种消息类型、字段和数据类型,以及它们之间的关系。
这些定义文件以.proto为扩展名,使用Protobuf 编译器可以生成对应的代码。
2、核心库:Matter的核心库提供了实现协议所需的各种功能和工具。
这些功能包括消息序列化、反序列化、加密解密、网络通信等。
核心库是用C语言编写的,它提供了跨平台的支持,可以在不同的操作系统和硬件架构上运行。
3、测试工具:Matter包含了一套完整的测试工具,用于测试协议的实现是否符合规范。
这些测试工具包括单元测试、集成测试和端到端测试等。
它们可以帮助开发者确保Matter协议的正确性和可靠性。
4、示例代码:Matter还提供了一些示例代码,展示了如何使用Matter协议进行设备连接和控制。
这些示例代码包括服务器端和客户端的实现,可以帮助开发者快速了解如何使用Matter协议。
从设计上看,Matter采用了一种基于消息传递的设计思路,它将所有控制指令和状态信息封装在消息中,通过消息的传递来实现设备之间的通信。
这种设计方式使得Matter具有很好的扩展性和灵活性,可以适应不同的设备和场景需求。
在实现上,Matter使用了一种基于事件驱动的模型,它将所有的操作都封装成事件,通过事件的处理来实现各种功能。
这种模型使得Matter具有很好的实时性和响应性,可以快速响应用户的操作和设备的状态变化。
总的来说,Matter的源码设计清晰、结构合理、易于理解和维护。
它的实现充分考虑了智能家居设备的特性和需求,具有很好的实用性和扩展性。
mondrian源码分析1.概述 (1)1.1.若干概念 (1)1.2.架构 (2)2.静态类包分析 (3)2.1.包解释 (3)2.2.Schema manger部分 (4)2.3.包mondrian.calc (4)2.4.包mondrian.olap—接口 (5)2.4.1.mdx函数包 (5)2.4.2.funCall (5)2.4.3.Query类 (6)2.5.包mondrian.rolap—计算层 (6)2.5.1.成员读取包MemberReader (6)2.5.2.单元格读取CellReader (8)2.5.3.RolapResult类 (11)2.5.4.RolapEvaluator类 (12)2.5.5.关于排序 (12)2.6.聚集层Star layer (13)2.6.1.概述 (13)2.6.2.聚合装载过程 (13)2.6.3.segment详解 (14)2.6.4.缓存失效控制 (17)3.交互管理层 (18)3.1.初始化MondrianModel (18)3.2.获取结果集 (19)4.修改点 (22)4.1.mondrian角色参数化 (22)4.2.缓存失效控制 (22)4.3.connection创建参数优化 (23)4.4.维度成员排序 (23)4.5.对mondrian bug的修正 (24)4.6.to-do List (24)1.概述1.1. 若干概念成员(member):成员是代表维度中一次或多次数据出现的项。
度量值也可以算作一个维度,因此一个具体度量值项也可以作为一个成员。
元组(tuple):是向量,用于定义来自多维数据集的数据切片;它由来自一个或多个维度的单个成员的有序集合组成。
元组用于标识来自多维数据集的特定多维数据块;由来自多维数据集中各个维度的一个成员组成的元组完全描述单元值。
换言之,元组是一种成员向量。
例如:(时间.[下半年], 路线.非陆地.航空),由单个成员组成的元组也可括在圆括号内,但这不是必需的。
Gohst源码分析完成端口CWinApp 中 InitApplication函数里面实现了窗口显示的一些功能,iocp也是在这里启动的。
只是贴上部分代码:// 启动IOCP服务器int nPort = m_IniFile.GetInt(_T("Settings"), _T("ListenPort"));int nMaxConnection = m_IniFile.GetInt(_T("Settings"), _T("MaxConnection"));if (nPort == 0)nPort = 81;if (nMaxConnection == 0)nMaxConnection = 10000;if (m_IniFile.GetInt(_T("Settings"), _T("MaxConnectionAuto")))nMaxConnection = 8000;((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection);其中主窗口的Activate函数就是启动IOCP 。
void CMainFrame::Activate(UINT nPort, UINT nMaxConnections){CString str,strLogText;if (m_iocpServer != NULL){m_iocpServer->Shutdown();delete m_iocpServer;}m_iocpServer = new CIOCPServer;// 开启IPCP服务器if (m_iocpServer->Initialize(NotifyProc, this, 100000, nPort)){str.Format(_T("监听端口: %d"), nPort);strLogText.Format( "Gh0st Rat 2013 监听端口: [%d]", nPort );g_pLogView->AddToLog(strLogText);}else{str.Format(_T("端口%d绑定失败"), nPort);strLogText.Format( "Gh0st Rat 2013 端口[%d]监听失败!", nPort );g_pLogView->AddToLog(strLogText);}m_wndStatusBar.SetPaneText(2, str);m_wndStatusBar.SetPaneText(3, _T("在线主机: 0"));}Initialize就是初始化IOCP函数。
mina源码解析Mina是一款基于Java的轻量级网络通讯框架,它支持TCP、UDP、SSL等协议。
Mina的核心是IoProcessor,它负责处理网络I/O事件,并将事件分发到IoHandler中处理。
本文将从源码的角度对Mina进行解析,探究其实现原理和内部机制。
一、IoServiceIoService是Mina的核心接口,它定义了启动、停止、监听和连接等方法。
Mina支持两种类型的IoService:IoAcceptor和IoConnector。
IoAcceptor用于监听并接受连接,而IoConnector用于主动连接其他服务端。
二、IoProcessorIoProcessor负责处理I/O事件,它是Mina的核心组件。
在Mina 中,每个IoService都有对应的IoProcessor。
当有新连接到来时,IoProcessor会创建一个IoSession并将其交给IoService处理。
三、IoFilterIoFilter是Mina的过滤器,可以用于对I/O事件进行拦截和处理。
Mina中的过滤器链是一种典型的责任链模式,每个过滤器都可以在I/O事件发生前、后或中间处理事件。
四、IoSessionIoSession是Mina的会话,它代表了一个TCP连接。
在Mina中,所有的I/O操作都是通过IoSession进行的。
IoSession中包含了该会话的所有状态信息,如连接状态、读写缓冲区等。
五、IoHandlerIoHandler是Mina的处理器,用于处理IoSession中的I/O事件。
当有新数据到达时,IoHandler会被调用,并处理该事件。
六、总结Mina是一款轻量级的网络通讯框架,其核心是IoProcessor、IoFilter、IoSession和IoHandler。
通过对Mina源码的分析,我们可以了解其内部机制和实现原理,从而更好地使用和理解Mina框架。
Omci,全称为Open Multi-Media Interface,是一种用于网络设备管理的协议。
在解读Omci源码之前,我们需要了解一些基础知识,例如网络协议栈、设备驱动程序以及多线程编程等。
Omci源码通常是用C语言编写的,这是因为C语言具有跨平台、可移植性好、高效等特点,适合用于编写底层协议栈。
Omci源码的主要组成部分包括:1. 协议栈:这部分代码实现了Omci协议的各种功能,包括数据包的封装、解封装、数据传输等。
2. 设备驱动程序:这部分代码用于与硬件设备进行通信,通常会根据具体的硬件平台进行编写。
3. 辅助功能:这部分代码提供了一些辅助功能,例如日志记录、内存管理等。
在解读Omci源码时,我们需要重点关注以下几个方面:1. 数据包结构:Omci协议中的数据包具有特定的格式,包括头部、负载等部分。
我们需要了解这些数据包的格式以及如何解析它们。
2. 状态机:Omci协议中的设备具有不同的状态,状态之间的转换需要遵循一定的规则。
我们需要了解这些状态以及它们之间的转换逻辑。
3. 任务调度:Omci协议需要在多线程环境下运行,因此需要进行任务调度。
我们需要了解如何使用多线程技术实现高效的任务调度。
4. 错误处理:网络通信中可能会出现各种错误,例如数据包丢失、数据包损坏等。
我们需要了解如何处理这些错误以及如何保证数据的可靠传输。
总之,通过深入解读Omci源码,我们可以更好地理解Omci协议的工作原理以及实现细节。
这有助于我们更好地使用Omci协议进行网络设备的管理和维护,提高网络管理的效率和可靠性。
同时,通过对Omci源码的学习和掌握,我们也可以提升自己的网络编程技能和多线程编程能力。
kityminder-editor 是一个基于kityminder 的思维导图编辑器。
如果你想深入了解其源码,以下是一些建议:
了解背景:首先,了解kityminder 的背景和功能。
kityminder 是一个基于kity 的思维导图工具,而kity 是一个由阿里巴巴开源的矢量图形库。
查看官方文档:kityminder-editor 的官方文档可能会提供源码结构和主要功能的概览。
直接阅读源码:如果官方文档不够详细,或者你想更深入地了解,直接阅读源码是最好的方式。
使用一个文本编辑器或IDE 来打开源码文件,并开始阅读。
关注关键部分:
数据模型:思维导图的数据结构是如何定义的?如何存储和读取?
视图渲染:如何将思维导图的数据结构渲染到屏幕上?
编辑功能:如何实现节点的添加、删除、移动和编辑?
事件处理:如何处理用户的输入事件,如点击、拖动等?
调试与测试:使用开发者工具来调试和测试kityminder-editor 的功能,这有助于理解其内部工作原理。
社区与论坛:如果遇到问题或困难,可以查看相关的社区和论坛,可能有其他开发者分享了他们的经验和见解。
持续学习:随着时间的推移,开源项目会不断更新和改进。
定期查看kityminder-editor 的更新日志和代码库,以了解最新的变化和改进。
贡献代码:如果你对kityminder-editor 有深入的了解,并且有新的想法或改进建议,可以考虑为其贡献代码或文档。
这不仅可以帮助项目的发展,还可以与其他贡献者交流和学习。
总之,深入理解一个开源项目的源码需要时间和耐心。
但通过逐步学习和实践,你可以逐渐掌握其核心原理和工作方式。
gin框架的路由源码解析前⾔本⽂转载⾄可以直接去原⽂看, ⽐我这⾥直观我这⾥只是略微的修改正⽂gin的路由实现使⽤ Radix Tree , 简洁版的前缀树前缀树别名: 字典树 / 单词查找树 / 键树为什么使⽤前缀树url是有限的,不可能⽆限长url是有规律的url是⼀级⼀级的, restful 更是如此⽐如博客有的是按年和⽉分割 /2020/3/aaaa.html /2020/3/bbbb.html 此时使⽤前缀树更合适gin的路由树基数树/PAT位树, 是⼀种更节省空间的前缀树, 对于基数树的每个节点,如果该节点是唯⼀的⼦树的话,就和⽗节点合并。
越常匹配的前缀, 权重越⼤因为前缀树的构建模式导致越长的路径定位的时间越长, gin在注册路由时越长的路由排的越前, 如果最长的节点能优先匹配, 那么路由匹配所花的时间不⼀定⽐短路由更长gin⾸先按照请求类型(POST/GET/...), 分为多个PAT树, 每个PAT树存储这个请求类型下⾯注册的路由, 路由⼜根据权重进⾏排序路由树节点路由树由⼀个个节点组成, gin的路由树节点由结构体 node 表⽰, 其构造结构如下// tree.gotype node struct {// 节点路径,⽐如上⾯的s,earch,和upportpath string// 和children字段对应, 保存的是分裂的分⽀的第⼀个字符// 例如search和support, 那么s节点的indices对应的"eu"// 代表有两个分⽀, 分⽀的⾸字母分别是e和uindices string// ⼉⼦节点children []*node// 处理函数链条(切⽚)handlers HandlersChain// 优先级,⼦节点、⼦⼦节点等注册的handler数量priority uint32// 节点类型,包括static, root, param, catchAll// static: 静态节点(默认),⽐如上⾯的s,earch等节点// root: 树的根节点// catchAll: 有*匹配的节点// param: 参数节点nType nodeType// 路径上最⼤参数个数maxParams uint8// 节点是否是参数节点,⽐如上⾯的:postwildChild bool// 完整路径fullPath string}请求的⽅法树在gin的路由中, 每⼀个 HTTP Method (GET/POST/PUT/....) 都对应了⼀棵PAT树, 在注册路由时会调⽤ addRoute 函数// gin.gofunc (engine *Engine) addRoute(method, path string, handlers HandlersChain) {// 获取请求⽅法对应的树root := engine.trees.get(method)if root == nil {// 如果没有就创建⼀个root = new(node)root.fullPath = "/"engine.trees = append(engine.trees, methodTree{method: method, root: root})}root.addRoute(path, handlers)}⽽在gin中, 每⼀个 Method 对应的树关系时是存放在⼀个切⽚中, engine.trees的类型是 methodTrees , 其定义如下type methodTree struct {method stringroot *node}type methodTrees []methodTree // slice⽽ engine.trees.get ⽅法如下,(就是for循环)func (trees methodTrees) get(method string) *node {for _, tree := range trees {if tree.method == method {return tree.root}}return nil}使⽤切⽚⽽不是使⽤map来存储, 可能是考虑到节省内存, ⽽且HTTP请求⼀共就9种, 使⽤切⽚也⽐较合适, 效率也⾼, 初始化在gin的 engine 中func New() *Engine {debugPrintWARNINGNew()engine := &Engine{RouterGroup: RouterGroup{Handlers: nil,basePath: "/",root: true,},// ...// 初始化容量为9的切⽚(HTTP1.1请求⽅法共9种)trees: make(methodTrees, 0, 9),// ...}engine.RouterGroup.engine = engineengine.pool.New = func() interface{} {return engine.allocateContext()}return engine}路由匹配当新的请求进⼊gin时, 会先经过函数 ServeHTTP// gin.gofunc (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {// 这⾥使⽤了对象池c := engine.pool.Get().(*Context)// 这⾥有⼀个细节就是Get对象后做初始化c.writermem.reset(w)c.Request = reqc.reset()engine.handleHTTPRequest(c) // 我们要找的处理HTTP请求的函数engine.pool.Put(c) // 处理完请求后将对象放回池⼦}ServeHTTP 调⽤ handleHTTPRequest 函数(节选)// gin.gofunc (engine *Engine) handleHTTPRequest(c *Context) {// 根据请求⽅法找到对应的路由树t := engine.treesfor i, tl := 0, len(t); i < tl; i++ {if t[i].method != httpMethod {continue}root := t[i].root// 在路由树中根据path查找value := root.getValue(rPath, c.Params, unescape)if value.handlers != nil {c.handlers = value.handlersc.Params = value.paramsc.fullPath = value.fullPathc.Next() // 执⾏函数链条c.writermem.WriteHeaderNow()return}c.handlers = engine.allNoRouteserveError(c, http.StatusNotFound, default404Body)}⼤致为先COPY⼀份路由的切⽚, 先找到与该请求对应的请求类型, 然后在这个请求类型的路由树种使⽤ getValue ⽅法查找对应的路由, 没有则返回404。
anki_vectorSDK源码解析(教程)⼀:最近anki vector robot开放了Python SDK,我听到的第⼀时间就赶快上⽹查了查,先抛⼏个官⽹重要链接吧:Python编程API⼿册及环境搭建等:anki公司github地址及anki_vectorSDK实现源码,⽤于理解电脑和vector的通信协议,包含教学例程:⼀些关于cozmo和vector编程使⽤的Web⼯具,可以在浏览器看到机器⼈看到的图像:anki编程交流社区:cozmo在线⼿册:cozmo编程SDK源码及教学例程源码:new:DDL官⽅SDK教学视频:(40分钟)(2020年新出的)⽬前没有相关书籍,anki不是像google⼀样的⼀流⼤⼚。
笔者从事C++开发,对python编程也是⼩⽩级别,对vector编程的学习也全是靠上⾯这⼏个链接。
⾸先,你得拥有⼀台vector和⼀台能⽤于编程的电脑(对操作系统没有要求,这点很不错),使它们处于同⼀个局域⽹内(都连着家⾥的wifi就⾏)。
具体安装python和搭建环境等细节不再展开,在线⽂档写的已经很详尽了。
转载请注明出处:https:///xjjsk/p/10159946.html⼆:接下来的部分介绍⼏个简单的官⽹demo: 源码⽬录:vector-python-sdk-master/examples/tutorials/1#01_hello_world.py23import anki_vector45def main():6 args = anki_vector.util.parse_command_args()7 with anki_vector.Robot(args.serial) as robot:8 print("Say 'Hello World'...")9 robot.say_text("Hello World")1011if__name__ == "__main__":12 main()⾸先,第3⾏引⼊anki_vector模块,其实就是⼀个叫做anki_vector的⽂件夹,⽬前所有的程序,只需要引⼊这个模块就拥有vector的所有控制功能了。
Martini源码剖析 martini是非常优雅的Go Web框架。他基于依赖注入的思想,仿照Sinatra的路由设计,参考Express的中间件设计,而且核心微小,扩展方便,非常值得学习。但是由于本身API设计简洁,使很多细节无法从代码理解。所以,我写一点笔记记录martini的工作方式。 Martini核心
我们从最简单的官方实例入手: package main
import "github.com/go-martini/martini" func main() { m := martini.Classic() m.Get("/", func() string { return "Hello world!" }) m.Run() }
martini.Martini是自带的核心结构,负责完成依赖注入和调用的过程。martini.ClassicMartini是路由martini.Router和martini.Martini的组合,实现路由分发和逻辑调用的过程。m := martini.Classic()返回的就是martini.ClassicMartini。具体在martini.go#L104: func Classic() *ClassicMartini { r := NewRouter() m := New() m.Use(Logger()) m.Use(Recovery()) m.Use(Static("public")) m.MapTo(r, (*Routes)(nil)) m.Action(r.Handle) return &ClassicMartini{m, r} }
里面的m := New()定义在martini.go#L38: func New() *Martini { m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
m.Map(m.logger) m.Map(defaultReturnHandler()) return m }
依赖注入 上面很明显的看到两个奇特方法:m.Map()、m.MapTo()。这里,需要注意martini的一个最重要原则,注入的任何类型的结构,都是唯一的。即如: type User struct{
Id int }
m.Map(&User{Id:1}) m.Map(&User{Id:2})
martini在寻找&User类型的时候,只能获取到&User{Id:2}结构(最后注册的)。Map的作用,就是向内部注册对应类型的具体对象或者值。类型索引是reflect.Type。从而我们可以理解到m.New()的代码中将m.Logger(*log.Logger)和defaultReturnHandler(martini.ReturnHandler)(括号中是类型索引)注入到内部。
这里出现了一个问题。接口这种类型,是无法用reflect.Type()直接获取的(因为传来的都是已经实现接口的具体结构)。解决方法就是m.MapTo()。 m.MapTo(r, (*Routes)(nil))
即将r(martini.router)按照martini.Router接口(注意大小写)类型注入到内部。 (*Routes)(nil)
也是高明的构造。接口的默认值不是nil,无法直接new。但是指针的默认值是nil,可以直接赋值,比如var user *User; user = nil。因此他注册一个接口指针类型的空指针,用reflect.Type.Elem()方法就可以获取到指针的内部类型,即接口类型,并以接口类型索引注入到内部。 路由过程 HTTP处理 martini.Martini实现了http.Handler方法,实际的HTTP执行过程在代码martini.go#L68: func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) { m.createContext(res, req).run() }
这里需要我们关注m.createContext,它返回*martini.context类型,代码martini.go#L87:
func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} c.SetParent(m) c.MapTo(c, (*Context)(nil)) c.MapTo(c.rw, (*http.ResponseWriter)(nil)) c.Map(req) return c }
创建martini.context类型;然后SetParent设置寻找注入对象的时候同时从m(martini.Martini)中寻找(martini.context和martini.Martini两个独立的inject),这样就可以获取m.Map注入的数据。 这里叉出来说:从代码看出实际上注入的数据有两层,分别在martini.context和martini.Martini。*martini.context中的是当前请求可以获取的(每个请求都会m.createContext(),都是新的对象);martini.Martini是全局的,任何请求都可以获
取到。 回到上一段,c.MapTo把martini.context按martini.Context接口,将martini.ResponseWriter按http.ResponseWriter接口,把req(http.Request)注入到当前上下文。
context.run方法定义在martini.go#L163: func (c *context) run() { for c.index <= len(c.handlers) { _, err := c.Invoke(c.handler()) if err != nil { panic(err) } c.index += 1
if c.Written() { return } } }
它在循环c.handlers(来自m.handlers,createContext代码中)。这里想解释三个细节。 c.Invoke是inject.Invoke方法,内部就是获取c.hanlder()返回的martini.Handler(func)类型的传入参数reflect.Type.In(),根据参数个数和类型去内部找对应的结构,然后拼装成[]reflect.Value给函数的reflect.Value(func).Call()。
c.handler()的返回来自两个方面,c.hanlders和c.action。c.handlers来自m.Use()添加,c.action来自r.Handle(*martini.router.Handle)(见上文martini.ClassicMartini.New中的m.Action(r.Handle))。因此,可以发现实际上handlers是有两个列表,一个是c.handlers([]martini.handler)和r.handlers(martini.routerContext.handlers)。而且前者先执行。也就是说无论m.Use写在哪儿,都要比router添加的func先执行。
c.Written判断请求是否已经发送。他实际上是判断martini.ResponseWriter.status是否大于0。因此只要发送了response status,handlers过程就会停止。
路由调用 从上面可以知道,路由调用过程有两个方面:一是m.Use()添加的handlers,二是路由添加比如m.Get("/",handlers...)中的handlers。m.Use的handlers调用就是上文的*martini.context.run方法,不再赘述。路由中的handlers执行是在router.go#L218: func (r *route) Handle(c Context, res http.ResponseWriter) { context := &routeContext{c, 0, r.handlers} c.MapTo(context, (*Context)(nil)) context.run() }
和router.go#L315: func (r *routeContext) run() { for r.index < len(r.handlers) { handler := r.handlers[r.index] vals, err := r.Invoke(handler) if err != nil { panic(err) } r.index += 1
// if the handler returned something, write it to the http response if len(vals) > 0 { ev := r.Get(reflect.TypeOf(ReturnHandler(nil))) handleReturn := ev.Interface().(ReturnHandler) handleReturn(r, vals) }
if r.Written() { return } } }
如果你已经理解上文中说明,这个过程和martini.context.run是一样的。唯一这里要解释的是martini.ReturnHandler。它与很上文中的m.Map(defaultReturnHandler())遥相呼应。