在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
原文:http://www.cnblogs.com/artech/archive/2007/05/21/753620.html Microsoft 的Visual Studio为我们在应用开发中提供的强大功能,我们是有目共睹。借助该工具,是我们的开发 显得更加高效而轻松。从Microsoft把这个IDE的名字从VS.NET 该为VS(比如原来的Visual Studio.NET 2003,现在的版本叫VS2005),可以MS对该IDE的期望和野心:MS要把它改造成一个万能的IDE。不过任何都有其两面性,对于我们广大的开发者来说,VS是我们的各种行为简单化,傻瓜化;但是在另一方面,他也会蒙蔽我们的眼睛,使我们对它背后做的事情视而不见。以我们的ASP.NET Website开发为例,编程、编译、部署都可以借助VS,有了VS一切显得如此简单,每个人都会做,但是我想很多一部分人对一个ASP.NET Website如何进行编译不会很了解。这篇文章就来谈谈背后的故事——ASP.NET是如何进行编译的。由于篇幅的问题整篇文章分两个部分:动态编译Dynamical Compilation和预编译(Precompilation)。 1.动态编译的过程 我们先来介绍在动态编译下的大体的执行流程:当ASP.NET收到一个基于某个page的request的时候,先判断该Page和相关的Source code是否编译过,如果没有就将其编译,如果已经编译,就是用已经Load的Assembly直接生成Page对象。 在这里有下面几点需要注意: 1). 动态编译是按需编译的,ASP.NET只会编译和当前Request相关的aspx和code。 2). 动态编译是基于某个目录的,也就是说ASP.NET会把被请求的page所在的目录的所有需要编译的文件进行编译,并生成一个Assembly。 3). 除了编译生成的Assembly外,动态编译还会生成一系列的辅助文件。 4). 对相关文件的修改,会导致重新编译,但是修改对当前的Request不起作用。也就是说如果你对某个aspx进行修改,那么对于修改后抵达的Request,会导致重新编译,但是对于之前的Request使用的依然是原来编译好的Assembly。 5). 编译生成的文件被放在一个临时目录中,这个目录的地址为Windows Directory\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files。其具体的目录结构如下图所示:
Directory\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files只是一个默认的临时目录,你可以在web config中的compilation section中设置你需要的临时目录。
<compilation tempDirectory="d:\MyTempFiles" />
2.Sample 现在我用一个Sample来一探ASP.NET是如何进行动态编译的。
在两个子目录Part I和Part II下分别创建了两个Web page:Page1和Page2。在App_Code目录中创建了一个Utility的static class。下面是它的定义:
public static class Utility
} 内容很简单,对当前加载的所有相关的Assembly(这些Assembly的Fullname以App_Web打头)进行Reflection,列出所有的Type。这个ReflectAllAssmebly将在5个Web page(Default Page和两队Page1&Page2)的Page_Load事件中被调用。
protected void Page_Load(object sender, EventArgs e)
} Default是列出所有4Page对应的Link以便我们访问它们,在我们再进行编译的情况下在IE中输入对应的URL来访问Default Page。(其他Page的Html中不具有真正的内容,是一个空的page.)
通过上面的显示,我们可以看到现在有一个Assembly:App_Web_wh7-uda5。该Asssembly定一个的Type有5个, _Default和 default_aspx分别对应Default Page,而Default2和 default2_aspxDefault2 Page的。FastObjectFactory_app_web_wh7_uda5是很重要的Type,我将会在后面对其进行深入介绍。正如我们在上面说过的,动态编译是按需编译,现在我们对Default Page进行访问,由于这次对该Website的第一次访问,所有需要的Source Code,包括aspx,code behind都要进行编译。在这个Sample中,虽然我们并没有访问Default2 page,但是我们说过,动态编译是基于目录的,由于Default Page和Default2 Page都直接置于根目录下,所以ASP.NET会把根目录下的所有文件编译到一个Assembly中。由于Page1和Page2位于子目录Part I和Part II之下,所以不会参与编译。除非我们下载对它进行Request。 我们现在来访问Part I下的Page1和Page2看看会有什么结果。我们会发现,两次Request获得的输出是一样的: 有了上面的理论和实验结果,我想这个时候,你肯定已经想到,如果我现在对Part II的Page1和Page2进行访问,输出结果会是什么样子了。 3.Page最终被转化成什么? 我们现在来看看通过编译,一个Page到底转换成什么。我们以Part I/Page1为例。通过上面的Sampe,我们知道它最终变成了两个Type:Page1和part_i_page1_aspx。 下面是Page1的定义:
下面是part_i_page1_aspx的定义
[CompilerGlobalScope]
public class part_i_page1_aspx : Page1, IHttpHandler 在part_i_page1_aspx中定义了一个基于aspx的HttpHandler所需的所有操作,并且它继承了Page1。所以我们可以说Part I/Page1这个page 最终的体现为part_i_page1_aspx。进一步说,对Part I/Page1的Http Request,ASP.NET所要做的就是生成一个part_i_page1_aspx来Handle这个request就可以了。 4.FastObjectFactory 通过上面的一个简单的Sample,你已经看到每个Assembly中都会生成一个以FastObjectFactory作为前缀的Type。这是一个很重要的Type,从它的名称我们不难猜出它的作用:高效的生成对象。而生成的是什么样的对象呢?实际上就是基于每个aspx的Http request的Http handler,对于Part I/Page1就是我们上面一节分析的part_i_page1_aspx对象。 我们现在通过Reflector查看我们生成的App_Web_n1mhegpg中的FastObjectFactory_app_web_n1mhegpg是如何定义的。
internal class FastObjectFactory_app_web_n1mhegpg
通过上面的Code,我们可以看到在FastObjectFactory中定义一系列的Create_ASP_XXX(后缀就是Page 编译生成的Type的名称)。通过这些方法,可以快速生成对一个的Page。至于为什么会叫作FastObjectFactory,我想是因为直接通过调用这个静态的方法快速地创建Page对象,从而避免使用Reflection的late binding带来的性能的影响吧。 5.Preservation Files 进行每一次编译,ASP.NET会生成一系列具有.compiled扩展名的保留文件(Preservation File)。这个文件非常重要,我们现在来深入介绍这个样一个文件。 Preservation File这个文件本质上是一个XML。它是基于每个Page的,也就是每个Page都会有一个这样的Preservation File. 无论Page对应的Directory是怎样的,与之对应的Preservation File总会保存在根目录下,所以必须有一种机制保持为处于不同Directory的Page生成的Preservation File具有不同的文件名,不管Page的名称是否一样。所以Preservation File采用下面的命名机制: [page].aspx.[folder-hash].compiled 其中[page]是Page的名称,[folder-hash]是对page所在路径的Hash Value。这样做有两个好处。
下面这个Preservation File就是上面Sample中Part II/Page1.aspx对应的Preservation File,名称为default2.aspx.cdcab7d2.compiled。我们来看看XML每个Item各代表什么意思。
<?xml version="1.0" encoding="utf-8"?>
<preserve resultType="3" virtualPath="/Artech.ASPNETDeployment/Part II/Page1.aspx" hash="fffffff75090c769" filehash="5f27fa390c45c52a" flags="110000" assembly="App_Web_hitlo3dt" type="ASP.part_ii_page1_aspx"> <filedeps> <filedep name="/Artech.ASPNETDeployment/Part II/Page1.aspx" /> <filedep name="/Artech.ASPNETDeployment/Part II/Page1.aspx.cs" /> </filedeps> </preserve> 有这段XML我们可以看到,所有的内容都包含在preserve XML element中,在他中间定义了几个重要的attribute.
通过hash和filehash的缓存,ASP.NET可以判断自上一次使用以来,Preservation File和它所依赖的Dependent File是否被改动,如果真的被改动,将会重新编译。因为对于文件的任何改动都会导致该Hash value的改变。 此外,Preservation File的<filedeps>列出了所有依赖的文件,对于Page,一般是aspx和code behind。 6. 进一步剖析整个动态编译过程 现在我们来总结整个动态编译的过程: Step1:当ASP.NET收到对于某个Page的Request,根据这个request对应的Url试着找到该page对应的Preservation File,如果没有找到,重新编译Page所在目录下的所有需要编译的文件,同时生成一些额外的文件,包括Preservation File。 Step2:然后解析这个Preservation File,通过hash和filehash判断文件自身或者是Dependent File是否在上一次编译之后进行过任何的修改,如果是的,则重新编译。然后重新执行Step2。 Step3:通过Preservation File 的assembly attribute Load对应的assembly(如果Assembly还没有被Load的话),通过type获知对应的aspx type,然后借助FastObjectFactory的对应的Create_ASP_XXX创建这个type。这个新创建的对象就是我们需要的Http Handler,在之上执行相应的操作把结果Response到客户端。 对动态编译的讨论就到这里,在本篇文章下半部分将会讨论另一种更加有用的编译方式:[原创] 深入剖析ASP.NET的编译原理之二:预编译(Precompilation)。 |
请发表评论