- 浏览: 263487 次
- 性别:
- 来自: 焦作
文章分类
最新评论
-
wangying641588:
Ext3.3.1 中的ext.jsb2, 在安装的时候看不见, ...
安装spket -
1927105:
总结的不错
PO VO BO DTO POJO DAO -
dongbiying:
好乱呀!弄不明白!
Java中如何打jar包 -
qinglintan:
如果是想在jsp页面写js,此插件就帮不了了,用aptana不 ...
安装spket -
zhuifengmzw:
某个组织、某个人是代表不了一个宗教的,
比如你要是个品德很恶劣 ...
伊斯兰教的悲哀 巴基斯坦男女因偷情被塔利班枪决
MS在发布ASP.NET时的一大功能特性是,与ASP和PHP等脚本语言不同,ASP.NET实际上是一种编译型的快速网页开发环境。这使得ASP.NET在具有开发和修改的简便性的同时,不会负担效率方面的损失。实现上ASP.NET与JSP的思路类似,引擎在第一次使用一个页面之前,会将之编译成一个类,自动生成Assembly并载入执行。
而通过《在WinForm程序中嵌入ASP.NET》一文中我们可以了解到,ASP.NET引擎实际上是可以无需通过IIS等Web服务器调用而被使用的,这就使得手工预编译ASP.NET页面成为可能。实际上这个需求是普遍存在的,早在ASP时代就层有第三方产品支持将ASP页面编译成二进制程序,以提高执行效率和保障代码安全性,而将伴随Whidbey发布的ASP.NET 2.0更是直接内置了预编译ASP.NET页面的功能。
实际上网上早就有人讨论过在ASP.NET 1.1中模拟预编译特性的实现方法,例如以下两篇文章
Pre-Compiling ASP.NET Web Pages
Pre-Compile ASPX pages in .NET 1.1
其思路基本上都是遍历所有需要预编译的页面文件,然后通过模拟Web页面请求的方式,触发ASP.NET引擎的自动预编译机制。这样做的好处是完全模拟真实情况,无需了解ASP.NET引擎的实现原理;但同时也会受到诸多限制,如预编译结果不透明,无法脱离原始ASP.NET页面文件使用等等,而且无法使我们从原理上理解预编译特性的实现。
下面我将分三到四个小节,简要讨论 ASP.NET 自动编译机制的实现、ASP.NET 页面文件编译的实现以及如何在ASP.NET 1.1中实现手动预编译页面和相应分发机制。
[1] 自动预编译机制浅析
本节我们将详细分析讨论.NET 1.1中,ASP.NET引擎内部实现自动页面预编译的原理。
首先,我们所说的ASP.NET页面实际上主要分为四类:
1.Web 应用程序文件 Global.asax
2.Web 页面文件 *.aspx
3.用户自定义控件文件 *.ascx
4.Web 服务程序文件 *.asmx
Web 应用程序文件对于每个Web 应用程序来说是可选唯一的,用来处理ASP.NET应用程序一级的事件,并将被预编译为一个System.Web.HttpApplication类的子类;
Web 页面文件是普通的ASP.NET页面,处理特定页面的事件,将被预编译为一个System.Web.UI.Page类的子类;
用户自定义控件文件是特殊的ASP.NET页面,处理控件自身的事件,将被预编译为一个System.Web.UI.UserControl类的子类;
Web 服务程序文件则是与前三者不太相同的一种特殊页面文件,暂时不予讨论。
然后,前三种ASP.NET文件的编译时机也不完全相同。Web 应用程序文件在此 Web 应用程序文件第一次被使用时自动编译;Web 页面文件在此Web页面第一次被使用时自动编译,实际上是调用 HttpRuntime.ProcessRequest 函数触发预编译;用户自定义控件文件则在其第一次被 Web 页面使用的时候自动编译,实际上是调用 Page.LoadControl 函数触发预编译。
在了解了以上这些基本知识后,我们来详细分析一下自动预编译的实现机制。
HttpRuntime.ProcessRequest 函数是处理Web页面请求的调用发起者,伪代码如下:
以下为引用:
public static void HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
{
// 检查当前调用者有没有作为ASP.NET宿主(Host)的权限
InternalSecurityPermissions.AspNetHostingPermissionLevelMedium.Demand();
if(wr == null)
{
throw new ArgumentNullException("custom");
}
RequestQueue queue = HttpRuntime._theRuntime._requestQueue;
if(queue != null)
{
// 将参数中的Web页面请求放入请求队列中
// 并从队列中使用FIFO策略获取一个页面请求
wr = queue.GetRequestToExecute(wr);
}
if(wr != null)
{
// 更新性能计数器
HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);
// 实际完成页面请求工作
HttpRuntime.ProcessRequestNow(wr);
}
}
HttpRuntime.ProcessRequestNow函数则直接调用缺省HttpRuntime实例的ProcessRequestInternal函数完成实际页面请求工作,伪代码如下:
以下为引用:
internal static void HttpRuntime.ProcessRequestNow(HttpWorkerRequest wr)
{
HttpRuntime._theRuntime.ProcessRequestInternal(wr);
}
HttpRuntime.ProcessRequestInternal函数逻辑稍微复杂一些,大致可分为四个部分。
首先检查当前HttpRuntime实例是否第一次被调用,如果是第一次调用则通过FirstRequestInit函数初始化;
接着调用HttpResponse.InitResponseWriter函数初始化页面请求的返回对象HttpWorkerRequest.Response;
然后调用HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例;
最后使用Web应用程序实例完成实际的页面请求工作。
伪代码如下:
以下为引用:
private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
{
// 构造 HTTP 调用上下文对象
HttpContext ctxt = new HttpContext(wr, 0);
// 设置发送结束异步回调函数
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, ctxt);
// 更新请求计数器
Interlocked.Increment(&;amp;(this._activeRequestCount));
try
{
// 检查当前HttpRuntime实例是否第一次被调用
if(this._beforeFirstRequest)
{
lock(this)
{
// 使用 Double-Checked 模式 避免冗余锁定
if(this._beforeFirstRequest)
{
this._firstRequestStartTime = DateTime.UtcNow;
this.FirstRequestInit(ctxt); // 初始化当前 HttpRuntime 运行时环境
this._beforeFirstRequest = false;
}
}
}
// 根据配置文件设置,扮演具有较高特权的角色
ctxt.Impersonation.Start(true, false);
try
{
// 初始化页面请求的返回对象
ctxt.Response.InitResponseWriter();
}
finally
{
ctxt.Impersonation.Stop();
}
// 获取当前 Web 应用程序实例
IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);
if (handler == null)
{
throw new HttpException(HttpRuntime.FormatResourceString("Unable_create_app_object"));
}
// 使用Web应用程序实例完成实际的页面请求工作
if((handler as IHttpAsyncHandler) != null)
{
IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler) handler);
ctxt.AsyncAppHandler = asyncHandler;
// 使用异步处理机制
asyncHandler.BeginProcessRequest(ctxt, this._handlerCompletionCallback, ctxt);
}
else
{
handler.ProcessRequest(ctxt);
this.FinishRequest(ctxt.WorkerRequest, ctxt, null);
}
}
catch(Exception E)
{
ctxt.Response.InitResponseWriter();
this.FinishRequest(wr, ctxt, E);
}
}
HttpRuntime.ProcessRequestInternal函数中,涉及到文件预编译的有两部分:一是获取当前 Web 应用程序实例时,会根据情况自动判断是否预编译Web 应用程序文件;二是在完成实际页面请求时,会在第一次使用某个页面时触发预编译行为。
首先来看看对 Web 应用程序文件的处理。
HttpRuntime.ProcessRequestInternal函数中调用了HttpApplicationFactory.GetApplicationInstance函数获取当前 Web 应用程序实例。System.Web.HttpApplicationFactory是一个内部类,用以实现对多个Web应用程序实例的管理和缓存。GetApplicationInstance函数返回的是一个IHttpHandler接口,提供IHttpHandler.ProcessRequest函数用于其后对Web页面文件的处理。伪代码如下:
以下为引用:
internal static IHttpHandler HttpApplicationFactory.GetApplicationInstance(HttpContext ctxt)
{
// 定制应用程序
if(HttpApplicationFactory._customApplication != null)
{
return HttpApplicationFactory._customApplication;
}
// 调试请求
if(HttpDebugHandler.IsDebuggingRequest(ctxt))
{
return new HttpDebugHandler();
}
// 判断是否需要初始化当前 HttpApplicationFactory 实例
if(!HttpApplicationFactory._theApplicationFactory._inited)
{
HttpApplicationFactory factory = HttpApplicationFactory._theApplicationFactory;
lock(HttpApplicationFactory._theApplicationFactory);
{
// 使用 Double-Checked 模式 避免冗余锁定
if(!HttpApplicationFactory._theApplicationFactory._inited)
{
// 初始化当前 HttpApplicationFactory 实例
HttpApplicationFactory._theApplicationFactory.Init(ctxt);
HttpApplicationFactory._theApplicationFactory._inited = true;
}
}
}
// 获取 Web 应用程序实例
return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(ctxt);
}
在处理特殊情况和可能的实例初始化之后,调用HttpApplicationFactory.GetNormalApplicationInstance函数完成获取Web应用程序实例的实际功能,伪代码如下:
以下为引用:
private HttpApplication HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
{
HttpApplication app = null;
// 尝试从已施放的 Web 应用程序实例队列中获取
lock(this._freeList)
{
if(this._numFreeAppInstances > 0)
{
app = (HttpApplication)this._freeList.Pop();
this._numFreeAppInstances--;
}
}
if(app == null)
{
// 构造新的 Web 应用程序实例
app = (HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
// 初始化 Web 应用程序实例
app.InitInternal(context, this._state, this._eventHandlerMethods);
}
return app;
}
构造新的 Web 应用程序实例的代码很简单,实际上就是对Activator.CreateInstance函数的简单包装,伪代码如下:
以下为引用:
internal static object HttpRuntime.CreateNonPublicInstance(Type type, object[] args)
{
return Activator.CreateInstance(type, BindingFlags.CreateInstance | BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public, null, args, null);
}
internal static object HttpRuntime.CreateNonPublicInstance(Type type)
{
return HttpRuntime.CreateNonPublicInstance(type, null);
}
至此一个 Web 应用程序实例就被完整构造出来,再经过InitInternal函数的初始化,就可以开始实际页面处理工作了。而HttpApplicationFactory实例的_theApplicationType类型,则是结果预编译后的Global.asax类。实际的预编译工作在HttpApplicationFactory.Init函数中完成,伪代码如下:
以下为引用:
private void HttpApplicationFactory.Init(HttpContext ctxt)
{
if(HttpApplicationFactory._customApplication != null)
return;
using(HttpContextWrapper wrapper = new HttpContextWrapper(ctxt))
{
ctxt.Impersonation.Start(true, true);
try
{
try
{
this._appFilename = HttpApplicationFactory.GetApplicationFile(ctxt);
this.CompileApplication(ctxt);
this.SetupChangesMonitor();
}
finally
{
ctxt.Impersonation.Stop();
}
}
catch(Object)
{
}
this.FireApplicationOnStart(ctxt);
}
}
GetApplicationFile函数返回Web请求物理目录下的global.asax文件路径;CompileApplication函数则根据此文件是否存在,判断是预编译之并载入编译后类型,还是直接返回缺省的HttpApplication类型,伪代码如下:
以下为引用:
internal static string HttpApplicationFactory.GetApplicationFile(HttpContext ctxt)
{
return Path.Combine(ctxt.Request.PhysicalApplicationPath, "global.asax");
}
private void HttpApplicationFactory.CompileApplication(HttpContext ctxt)
{
if(FileUtil.FileExists(this._appFilename))
{
ApplicationFileParser parser;
// 获取编译后的 Web 应用程序类型
this._theApplicationType = ApplicationFileParser.GetCompiledApplicationType(this._appFilename, context, out parser);
this._state = new HttpApplicationState(parser1.ApplicationObjects, parser.SessionObjects);
this._fileDependencies = parser.SourceDependencies;
}
else
{
this._theApplicationType = typeof(HttpApplication);
this._state = new HttpApplicationState();
}
this.ReflectOnApplicationType();
}
分析到这里我们可以发现,内部类型System.Web.UI.ApplicationFileParser的GetCompiledApplicationType函数是实际上进行Web应用程序编译工作的地方。但现在我们暂且打住,等下一节分析编译过程时再详细解说。 :)
然后我们看看对 Web 页面文件的处理。
在前面分析HttpRuntime.ProcessRequestInternal函数时我们曾了解到,在获得了Web应用程序实例后,会使用此实例的IHttpAsyncHandler接口或IHttpHandler接口,完成实际的页面请求工作。而无论有否Global.asax文件,最终返回的Web应用程序实例都是一个HttpApplication类或其子类的实例,其实现了IHttpAsyncHandler接口,支持异步的Web页面请求工作。对此接口的处理伪代码如下:
以下为引用:
private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
{
...
// 使用Web应用程序实例完成实际的页面请求工作
if((handler as IHttpAsyncHandler) != null)
{
IHttpAsyncHandler asyncHandler = ((IHttpAsyncHandler) handler);
ctxt.AsyncAppHandler = asyncHandler;
// 使用异步处理机制
asyncHandler.BeginProcessRequest(ctxt, this._handlerCompletionCallback, ctxt);
}
else
{
handler.ProcessRequest(ctxt);
this.FinishRequest(ctxt.WorkerRequest, ctxt, null);
}
...
}
HttpRuntime.ProcessRequestInternal函数通过调用HttpApplication.IHttpAsyncHandler.BeginProcessRequest函数开始页面请求工作。而HttpApplication实际上根本不支持同步形式的IHttpHandler接口,伪代码如下:
以下为引用:
void HttpApplication.ProcessRequest(System.Web.HttpContext context)
{
throw new HttpException(HttpRuntime.FormatResourceString("Sync_not_supported"));
}
bool HttpApplication.get_IsReusable()
{
return true;
}
而在HttpApplication.IHttpAsyncHandler.BeginProcessRequest函数中,将完成非常复杂的异步调用后台处理操作,这儿就不多罗嗦了,等有机会写篇文章专门讨论一下ASP.NET中的异步操作再说。而其最终调用还是使用System.Web.UI.PageParser对需要处理的Web页面进行解析和编译。
最后我们看看对用户自定义控件文件的处理。
Page类的LoadControl函数实际上是在抽象类TemplateControl中实现的,伪代码如下:
以下为引用:
public Control LoadControl(string virtualPath)
{
virtualPath = UrlPath.Combine(base.TemplateSourceDirectory, virtualPath);
Type type = UserControlParser.GetCompiledUserControlType(virtualPath, null, base.Context);
return this.LoadControl(type1);
}
实际的用户自定义控件预编译操作还是在UserControlParser类中完成的。
至此,在这一节中我们已经大致了解了ASP.NET自动预编译的实现原理,以及在什么时候对页面文件进行预编译。下一节我们将详细分析ApplicationFileParser、PageParser和UserControlParser,了解ASP.NET是如何对页面文件进行预编译的。
发表评论
-
使用dsoframer.ocx编辑2007office要注册的dll
2009-03-25 17:19 3584客户端需要注册组件dsoframer.ocx,把dsofra ... -
Asp.net动态生成html页面
2009-03-20 12:05 1343适用于:Microsoft ASP.NET ... -
asp.net编程中常用的33段代码(二)
2009-03-20 12:03 111618.日期格式化 【aspx页面内:<%# DataBind ... -
asp.net编程中常用的33段代码(一)
2009-03-20 11:58 10151. 打开新的窗口并传送参数: 传送参数:response ... -
“aspnet_state.exe 应用程序错误” 的解决方法
2009-03-13 09:34 3489不过是英文的,大家就算练习下英语了。 A ... -
网站建设之用asp.net生成html静态页的多种方法
2009-02-23 15:04 5797第一种方法是对一个aspx页面生成html文件,先对服务器发 ... -
解决ASP.NET上传文件大小限制
2009-02-02 15:39 2842解决asp.net上传文件大小限制对于asp.net,默认只允 ... -
Asp.Net页面输出到EXCEL
2009-02-02 15:30 1182其实,利用ASP.NET输出指定内容的WORD、EXCE ... -
asp.net中使用线程
2009-02-02 12:52 2014不同于winform application,asp.net ... -
asp.net中的小技巧---比如代码片断管理器
2009-01-09 14:53 12241,代码片断 当我们在visual studio 2008环境 ... -
Asp.net下C#自动化调用Word控件的实例与总结(转摘)
2008-12-15 13:44 8339本文通过一个实用例子完整演示了如何使用C#在ASP.NET里调 ... -
动态生成DataGrid的模板列
2008-12-04 15:54 1531有的时候我们需要邦定很复杂的DataGrid,我们知道Dat ... -
GridView事件解释
2008-11-29 13:15 1793RowCommand 在 GridView 控件 ... -
VS2005中ajax安装指南
2008-11-19 16:14 2861web2.0横空出世,ajax首当其冲,今天做web的如果再不 ...
相关推荐
7. **预编译机制**:ASP.NET的预编译功能提高了应用程序的性能,将ASP.NET页面转换为中间语言(IL)代码,以便更高效地执行。在【文1】中提到的自动预编译机制,有助于在运行时快速响应请求。 会话的生命周期包括...
asp.net页面显示正在加载、提交表单显示进度条
ASP.NET页面代码实现是.NET框架下用于构建动态Web应用程序的关键技术。这个模块代码,正如描述中提到的,是一个专门处理抽油机系数计算的组件,它已经在实际环境中通过了测试,意味着它的稳定性和效率得到了验证。抽...
本文将深入探讨ASP.NET的工作原理,涵盖其核心组件如APPDOMAIN、ASP.NET生命周期、HTTPAPPLICATION以及IIS的角色。 首先,APPDOMAIN(应用程序域)是.NET Framework中的一个基本概念,它提供了一个安全的执行环境,...
本资料“深入剖析ASP.NET的编译原理”将帮助我们理解这个框架背后的核心机制,特别是其编译过程。编译原理是任何编程语言或框架的基础,对于优化应用性能和解决调试问题至关重要。 首先,我们需要了解ASP.NET的两种...
本文将深入探讨ASP.NET页面的编译流程,帮助读者更好地理解其工作原理。 #### 二、ASP.NET页面生命周期与编译流程 ##### 2.1 页面生命周期简介 ASP.NET 页面生命周期由多个阶段组成,包括初始化(Initialization...
本文就在上一篇文章的基础上,进一步介绍在ASP.NET页面中实现Bar图的具体方法。希望本篇文章不仅能够让您领会到ASP.NET中强大的绘图功能,更希望能够弥补上一篇文章的一个缺憾,就是上一篇实现的图表的数据来自...
9. **Ajax技术**:为了实现无刷新的用户体验,开发者可能使用ASP.NET AJAX库或jQuery AJAX,使得部分页面内容能在后台异步更新,如实时显示新帖或回帖。 10. **SEO优化**:对于提升论坛的搜索引擎可见性,系统可能...
如果你的应用是基于ASP.NET Core,那么路由机制有所不同,但原理类似,只是配置方式和API有所改变。 总结一下,通过以上步骤,我们可以实现ASP.NET中去除aspx后缀的路由,创建更优雅、更易读的URL。这种方法不仅有...
总的来说,通过实现九九乘法表,你可以深入学习ASP.NET的服务器控件、页面生命周期、数据绑定、状态管理,以及客户端脚本与服务器端代码的协同工作。这个项目对于初学者来说既实用又有趣,能够帮助他们快速掌握ASP...
【标题】:“迅捷 Asp.net技术实现的网上购物系统” 【内容】: Asp.net是一种基于微软.NET框架的服务器端编程模型,广泛应用于构建Web应用程序,尤其在电子商务领域,如网上购物系统的开发中表现突出。这个“迅捷...
本文将深入探讨如何利用C#语言和ASP.NET技术来实现这一功能。 首先,我们需要理解手写签名的基本原理。手写签名通常涉及到数字图像处理和用户交互,可以通过鼠标或者触屏设备模拟手写过程。在这个过程中,我们需要...
总的来说,ASP.NET的工作原理是基于IIS的,通过ISAPI扩展和辅助进程实现请求的转发和处理。在IIS的不同版本中,这一过程有所变化,但核心的管道处理模型保持一致,使得开发者能够构建安全、高效的Web应用程序。了解...
在这个案例中,"asp.net实现的简单留言板"是一个利用ASP.NET技术和SQL Server 2000数据库来构建的基本交互式功能,允许用户在网站上留言并查看其他用户的留言。 1. ASP.NET基础:ASP.NET提供了丰富的控件和类库,...
6. **AJAX更新**:为了实现无刷新的轮播,开发者可能会使用ASP.NET AJAX UpdatePanel或纯JavaScript的异步更新技术,以避免每次切换内容时整个页面都需要重新加载。 7. **事件处理**:在ASP.NET中,开发者需要编写...
在ASP.NET开发中,实现多页签页面切换是一种常见的用户界面设计,它可以提供更好的用户体验,让用户在一个页面上浏览和操作多个相关的内容区域。本篇将详细介绍如何在ASP.NET环境中实现这样的功能,主要涉及的知识点...
总之,使用ASP.NET和C#实现网页快照功能需要理解WebBrowser控件的工作原理,掌握HTML页面的加载机制,并熟悉图像处理和文件I/O的相关知识。这个过程涉及到的技术点广泛,包括Web开发、图形处理、多线程编程以及文件...
1、使用代码内嵌的方式,在asp.net页面上输出九九乘法表。 2、使用双重循环,外循环的for代码:for (int i = 1; i ; i++),内循环的for代码: for (int j = 1; j ; j++)。 3、使用外循环控制表格的行的输出,使用内...
### 在ASP.NET页面中插入视频播放器:详细指南与实现步骤 #### 一、概述 在Web开发中,向用户展示视频内容是非常常见的需求之一。ASP.NET作为一种流行的Web开发框架,支持多种方式来实现在页面中插入视频播放器。...
ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET页面加载提示ASP.NET...