上一篇讲了从创建应用程序域到创建ISAPIRuntime实例的过程,本篇继续讲Asp.net处理第一次请求的必要的初始化过程。

ISAPIRuntime分析

ISAPIRuntime在System.Web.Hosting中实现,它的ProcessRequest是我们处理web请求的入口。

    public int ProcessRequest(IntPtr ecb, int iWRType) {
IntPtr pHttpCompletion = IntPtr.Zero;
if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
pHttpCompletion = ecb;
ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
}
ISAPIWorkerRequest wr = null;
try {
bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
wr.Initialize();
String wrPath = wr.GetAppPathTranslated();
String adPath = HttpRuntime.AppDomainAppPathInternal;
if (adPath == null ||StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
HttpRuntime.ProcessRequestNoDemand(wr);
return ;
}
else {
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath));
return ;
}
}
catch(Exception e) {
try {
WebBaseEvent.RaiseRuntimeError(e, this);
} catch {}
if (wr != null && wr.Ecb == IntPtr.Zero) {
if (pHttpCompletion != IntPtr.Zero) {
UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
}
if (e is ThreadAbortException) {
Thread.ResetAbort();
}
return ;
}
throw;
}
}

注意方法的IntPtr类型的参数ecb, 它是一个非托管的指针,用于传递一些必须的数据,以及最终将Response的内容返回给非托管环境ISAPI(异步方式),然后呈现给Client用户。方法中调用ISAPIWorkerRequest的静态方法CreateWorkerRequest而创建ISAPIWorkerRequest对象实例,参数分别为ecb和代表WorkerRequest类型的int参数iWRType,通过判断ecb和type类型的具体内容,来决定创建什么类型的WorkerRequest(上述类型的ISPAIWorkerRequest都继承于HttpWorkerRequest),上面的代码可以看出对不同版本的IIS进行了不同的包装,通过其Initialize方法来初始化一些基本的信息(比如:contentType, querystring的长度,filepath等相关信息)。然后调用HttpRuntime.ProcessRequestNoDemand(wr)转入HttpRuntime处理请求,最终体现在调用ProcessRequestInternal方法上。

HttpRuntime分析

Httpruntime在System.Web下实现,我们来看其处理请求的ProcessRequestInternal方法。

    private void ProcessRequestInternal(HttpWorkerRequest wr) {
Interlocked.Increment(ref _activeRequestCount);
if (_disposingHttpRuntime) {
try {
wr.SendStatus(, "Server Too Busy");
wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
wr.SendResponseFromMemory(body, body.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
} finally {
Interlocked.Decrement(ref _activeRequestCount);
}
return;
}
HttpContext context;
try {
context = new HttpContext(wr, false);
}
catch {
try {
wr.SendStatus(, "Bad Request");
wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
wr.SendResponseFromMemory(body, body.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
return;
} finally {
Interlocked.Decrement(ref _activeRequestCount);
}
}
wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context);
HostingEnvironment.IncrementBusyCount();
try {
try {
EnsureFirstRequestInit(context);
}
catch {
if (!context.Request.IsDebuggingRequest) {
throw;
}
}
context.Response.InitResponseWriter();
IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
if (app == null)
throw new HttpException(SR.GetString(SR.Unable_create_app_object));
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start");
if (app is IHttpAsyncHandler) {
IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
context.AsyncAppHandler = asyncHandler;
asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
}
else {
app.ProcessRequest(context);
FinishRequest(context.WorkerRequest, context, null);
}
}
catch (Exception e) {
context.Response.InitResponseWriter();
FinishRequest(wr, context, e);
}
}

该方法中创建了熟悉的HttpContext并同时创建了HttpRequest与HttpResponse

internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) {

        _wr = wr;

        Init(new HttpRequest(wr, this), new HttpResponse(wr, this));

if (initResponseWriter)

            _response.InitResponseWriter();

        PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);

    }

然后通过HttpApplicationFactory的GetApplicationInstance静态方法,获取我们熟悉的HttpApplication对象实例(注:HttpApplication对象是继承IHttpAsyncHandler,而IHttpAsyncHandler又继承于IHttpHandler),然后执行调用BeginProcessRequest方法。至此正式进入了HttpApplication对象的创建以及大家熟知的HttpApplication以后的生命周期了。

HttpApplicationFactory分析

HttpApplicationFactory在System.Web下实现。

查看HttpApplicationFactory用来创建Httpapplication的GetApplicationInstance方法。

    internal static IHttpHandler GetApplicationInstance(HttpContext context) {
if (_customApplication != null)
return _customApplication;
if (context.Request.IsDebuggingRequest)
return new HttpDebugHandler();
_theApplicationFactory.EnsureInited();
_theApplicationFactory.EnsureAppStartCalled(context);
return _theApplicationFactory.GetNormalApplicationInstance(context);
}

该方法有三个步骤:首先是EnsureInited,会检查是否已经初始化,如果没有会调用Init方法先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译,Init方法如下。

    private void Init() {
if (_customApplication != null)
return;
try {
try {
_appFilename = GetApplicationFile();
CompileApplication();
}
finally {
SetupChangesMonitor();
}
}
catch {
throw;
}
}

然后是EnsureAppStartCalled方法如果未开始启动会调用FireApplicationOnStart。

   private void EnsureAppStartCalled(HttpContext context) {
if (!_appOnStartCalled) {
lock (this) {
if (!_appOnStartCalled) {
using (new DisposableHttpContextWrapper(context)) {
WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
FireApplicationOnStart(context);
}
_appOnStartCalled = true;
}
}
}
} private void FireApplicationOnStart(HttpContext context) {
if (_onStartMethod != null) {
HttpApplication app = GetSpecialApplicationInstance();
app.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
RecycleSpecialApplicationInstance(app);
}
}

这里创建特定的HttpApplication实例,触发ApplicationOnStart事件(会执行global.asax中的Application_Start方法)。然后在处理完事件以后就立即被回收掉,因为系统初始化只需要一次。

最后是GetNormalApplicationInstance,如果在有空闲的HttpApplication实例,就直接用,如果没有就新创建,然后调用InitInternal方法进行初始化相关的内容,最后返回该HttpApplication实例。

    private HttpApplication GetNormalApplicationInstance(HttpContext context) {
HttpApplication app = null;
lock (_freeList) {
if (_numFreeAppInstances > ) {
app = (HttpApplication)_freeList.Pop();
_numFreeAppInstances--;
if (_numFreeAppInstances < _minFreeAppInstances) {
_minFreeAppInstances = _numFreeAppInstances;
}
}
}
if (app == null) {
app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
using (new ApplicationImpersonationContext()) {
app.InitInternal(context, _state, _eventHandlerMethods);
}
}
……
return app;
}

HttpApplication分析

HttpApplication在System.Web下实现,首先查看HttpApplication的InitInternal方法,该方法用于初始化。

    internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) {
Debug.Assert(context != null, "context != null");
_state = state;
PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
try {
try {
_initContext = context;
_initContext.ApplicationInstance = this;
context.ConfigurationPath = context.Request.ApplicationPathObject;
using (new DisposableHttpContextWrapper(context)) {
if (HttpRuntime.UseIntegratedPipeline) {
Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null");
Debug.Assert(_moduleConfigInfo.Count >= , "_moduleConfigInfo.Count >= 0");
try {
context.HideRequestResponse = true;
_hideRequestResponse = true;
InitIntegratedModules();
}
finally {
context.HideRequestResponse = false;
_hideRequestResponse = false;
}
}
else {
InitModules();
Debug.Assert(null == _moduleContainers, "null == _moduleContainers");
}
if (handlers != null)
HookupEventHandlersForApplicationAndModules(handlers);
_context = context;
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = true;
}
_hideRequestResponse = true;
try {
Init();
}
catch (Exception e) {
RecordError(e);
}
}
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = false;
}
_hideRequestResponse = false;
_context = null;
_resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback);
if (HttpRuntime.UseIntegratedPipeline) {
_stepManager = new PipelineStepManager(this);
}
else {
_stepManager = new ApplicationStepManager(this);
}
_stepManager.BuildSteps(_resumeStepsWaitCallback);
}
finally {
_initInternalCompleted = true;
context.ConfigurationPath = null;
_initContext.ApplicationInstance = null;
_initContext = null;
}
}
catch {
throw;
}
}

该代码主要有2个功能,一个是初始化大家熟悉的HttpModules,一个是通过BuildSteps执行多个生命周期事件的处理函数。通过上面的代码我们可以看出,每个功能都有一个特殊判断,判断IIS是否是IIS7的集成模式,如果是就有特殊的步骤,如果不是就走一般的步骤(两者直接的差异分别是:经典模式初始化HttpModules的时候会从网站配置的Modules里读取,集成模式会预加载CLR和大量Modules,比如加载服务器上设置的HttpModules;另外在BuildSteps的时候, IIS7集成模式走的是自己特殊的流程)。

总结一下,InitInternal方法的主要功能如下:

InitModules():根据Web.Config的设置,加载相应的HttpModules。

InitIntegratedModules():会加载IIS7集成模式下在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。

HookupEventHandlersForAppplicationAndModules:绑定HttpApplication实例中相应的事件处理函数(在Global.asax中定义的事件处理函数)。

创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中(包括HttpModules中定义的周期事件处理函数和查找匹配的HttpHandler、执行HttpHandler的方法以及过滤输出等特殊事件),等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。

在 HttpApplication的事件如下形式定义:

    public event EventHandler BeginRequest {
add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
}

所有的事件都是调用AddSyncEventHookup方法添加进去的,其中第一个参数是以Event+事件名称的值。

    internal void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification) {
AddSyncEventHookup(key, handler, notification, false);
}
private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) {
ThrowIfEventBindingDisallowed();
Events.AddHandler(key, handler);
if (IsContainerInitalizationAllowed) {
PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);
if (container != null) {
SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler);
container.AddEvent(notification, isPostNotification, step);
}
}
}

经典模式下在初始化HttpModlue的时候通过调用Events.AddHandler方法,将事件添加到Events集合里,同时这个key就是Event+事件名称,而集成模式下这些事件是添加到另外一个地方的(通过将事件hanlder包装成SyncEventExecutionStep类型,然后调用container.AddEvent方法将事件添加到另外一个地方),也就是说if上面的Events集合是给经典模式用的,下面的Container里的数据是给集成模式用的,这些事件是存放在HttpApplication的ModuleContainers属性里,这个属性的类型是PipelineModuleStepContainer[],个数就是HttpModules的个数,也就是说每个HttpModule在HttpApplication上添加的事件都放在各自的PipelineModuleStepContainer容器里。

    private PipelineModuleStepContainer[] ModuleContainers {
get {
if (_moduleContainers == null) {
Debug.Assert(_moduleIndexMap != null && _moduleIndexMap.Count > , "_moduleIndexMap != null && _moduleIndexMap.Count > 0");
_moduleContainers = new PipelineModuleStepContainer[_moduleIndexMap.Count];
for (int i = ; i < _moduleContainers.Length; i++) {
_moduleContainers[i] = new PipelineModuleStepContainer();
}
}
return _moduleContainers;
}
}

StepManager分析

集成模式和经典模式(或IIS6)使用的是不同的StepManager,这个类的BuildSteps方法就是为了创建有序的ExecutionStep,其中包括各种事件的事情以及其它在各时间周期之间穿插的操作,最主要的操作,大家以前就应该知道的,比如哪个周期可以判定使用哪个HttpHandler,以及在哪个周期内执行这个HttpHandler的BeginProcessRequest方法。StepManager的具体实现类(ApplicationStepManager、PipelineStepManager)和HttpApplication类在同一个文件中定义。

ApplicationStepManager的BuildSteps方法(用于经典模式)

    internal override void BuildSteps(WaitCallback stepCallback ) {
ArrayList steps = new ArrayList();
HttpApplication app = _application;
bool urlMappingsEnabled = false;
UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > );
steps.Add(new ValidateRequestExecutionStep(app));
steps.Add(new ValidatePathExecutionStep(app));
if (urlMappingsEnabled)
steps.Add(new UrlMappingsExecutionStep(app)); // url mappings
app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
steps.Add(new MapHandlerExecutionStep(app)); // map handler
app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
steps.Add(new CallHandlerExecutionStep(app)); // execute handler
app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
steps.Add(new CallFilterExecutionStep(app)); // filtering
app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
_endRequestStepIndex = steps.Count;
app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
steps.Add(new NoopExecutionStep()); // the last is always there
_execSteps = new IExecutionStep[steps.Count];
steps.CopyTo(_execSteps);
_resumeStepsWaitCallback = stepCallback;
} private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
if (asyncHandler != null) {
asyncHandler.CreateExecutionSteps(this, steps);
}
EventHandler handler = (EventHandler)Events[eventIndex];
if (handler != null) {
Delegate[] handlers = handler.GetInvocationList();
for (int i = ; i < handlers.Length; i++) {
steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
}
}
}

这个方法的完整功能归纳总结有以下几点:

对请求的Request进行验证,ValidateRequestExecutionStep。

对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。 

如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。

执行事件处理函数,比如将BeginRequest、AuthenticateRequest转化成可执行ExecutionStep在正式调用时候执行。

在这多个个事件操作处理期间,根据不同的时机加了4个特殊的ExecutionStep。

MapHandlerExecutionStep:查找匹配的HttpHandler

CallHandlerExecutionStep:执行HttpHandler的BeginProcessRequest

CallFilterExecutionStep:调用Response.FilterOutput方法过滤输出

NoopExecutionStep:空操作,留着以后扩展用

所有的ExecuteionStep都保存在ApplicationStepManager实例下的私有字段_execSteps里,而HttpApplication的BeginProcessRequest方法最终会通过该实例的ResumeSteps方法来执行这些操作。

PipelineStepManager的BuildSteps(用于集成模式)

    internal override void BuildSteps(WaitCallback stepCallback) {
Debug.Trace("PipelineRuntime", "BuildSteps");
HttpApplication app = _application;
IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
app.AddEventMapping(ttpApplication.IMPLICIT_HANDLER,
RequestNotification.MapRequestHandler,
false, materializeStep);
app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
RequestNotification.ExecuteRequestHandler,
false, app.CreateImplicitAsyncPreloadExecutionStep());
IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
RequestNotification.ExecuteRequestHandler,
false, handlerStep);
IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
RequestNotification.EndRequest,
true, webSocketsStep);
IExecutionStep filterStep = new CallFilterExecutionStep(app);
app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
RequestNotification.UpdateRequestCache,
false, filterStep);
app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
RequestNotification.LogRequest,
false, filterStep);
_resumeStepsWaitCallback = stepCallback;
} private void AddEventMapping(string moduleName,RequestNotification requestNotification, bool isPostNotification,IExecutionStep step) {
......
PipelineModuleStepContainer container = GetModuleContainer(moduleName);
container.AddEvent(requestNotification, isPostNotification, step);
}

以上代码有2个地方和经典模式不相同:

集成模式没有使用MapHandlerExecutionStep来装载ExecutionStep(也就是查找对应的HttpHandler),而是通过MaterializeHandlerExecutionStep类来获得HttpHandler,方式不一样。

集成模式是通过HttpApplication的AddEventMapping方法来添加事件的,从而将事件加入到前面所说的ModuleContainers容器。

总结一下,在经典模式下,是用 Event+事件名称做key将所有事件的保存在HttpApplication的Events属性对象里,然后在BuildSteps里统一按照顺序组装,中间加载4个特殊的ExecutionStep,最后在统一执行;在集成模式下,是通过HttpModule名称+RequestNotification枚举值作为key将所有的事件保存在HttpApplication的ModuleContainers属性对象里,然后也在BuildSteps里通过伪造HttpModule名称加载那4个特殊的ExecutionStep,最后按照枚举类型的顺序,遍历所有的HttpModule按顺序来执行这些事件,可以自行编写一个自定义的HttpModuel来执行这些事件看看效果如何。

下面是总结一下处理第一次请求的大体处理流程。

ASP.NET初始化流程分析2的更多相关文章

  1. MVC之前-ASP.NET初始化流程分析1

    Asp.net Mvc是当前使用比较多的web框架,也是比较先进的框架.我打算根据自己的实际项目经验以及相关的源码和一些使用Asp.net Mvc的优秀项目(主要是orchard)来说一说自己对于As ...

  2. u-boot中nandflash初始化流程分析(转)

    u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...

  3. Raid1源代码分析--初始化流程

    初始化流程代码量比较少,也比较简单.主要是run函数.(我阅读的代码的linux内核版本是2.6.32.61) 四.初始化流程分析 run函数顾名思义,很简单这就是在RAID1开始运行时调用,进行一些 ...

  4. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  5. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. struts2源码分析-初始化流程

    这一篇文章主要是记录struts.xml的初始化,还原struts2.xml的初始化流程.源码依据struts2-2.3.16.3版本. struts2初始化入口,位于web.xml中: <fi ...

  7. 8、Struts2 运行流程分析

    1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...

  8. freeswitch呼叫流程分析

    今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...

  9. 【开源】OSharp3.3框架解说系列(7.1):初始化流程概述

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

随机推荐

  1. hdu4614 Vases and Flowers 线段树+二分

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4614 题意: 给你N个花瓶,编号是0  到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花. ...

  2. 通过winform+模拟登录实现快速一键登录到人才招聘网站

    之前为了便于人事部门招聘登录网站更简洁高效,免去每天频繁输网址.用户名.密码等相关登录信息,特基于winform+HttpWebRequest实现模拟请求登录,最终达到一键登录到招聘网站后台的效果. ...

  3. MySql学习笔记(一) —— 数据的分组

    前面介绍的聚集函数只是用来计算行数,平均数,最大值,最小值而不用检索所有数据.通过count()函数,我们可以计算生产商1003提供的产品数目,但如果我要查询所有生产商提供的商品数,这就需要进行分组查 ...

  4. 开涛spring3(6.3) - AOP 之 6.3 基于Schema的AOP

    6.3  基于Schema的AOP 基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面.切入点及声明通知. 在Spring配置文件中,所以AOP相关定义必须放在<a ...

  5. jQuery中易混淆知识点总结(持续更新)

    find()与children() <body> <ul class="level-1"> <li class="item-i"& ...

  6. 阿里云服务器:IIS网站的架设(一、环境设置与安装IIS网站 二、网站的基本设置 三、建立新网站(未完待续))

    Windows Server 2012 R2的Internet Information Services (IIS)网站的模块化设计,可以减少被攻击面并减轻管理负担,让系统管理员更容易架设安全的具备高 ...

  7. click和blur事件冲突解决方案

    场景:例如做一个模仿百度搜索的搜索框,输入文字下面会有匹配项,当点击下拉项中的值时,就将值添加到搜索框中同时隐藏下拉框,点击其他地方就直接隐藏下拉框,这时所需要的事件分别为 下拉框事件onclick, ...

  8. 写markdown博客如何截图并快速上传到图床——记一个工具插件的实现

    1. 背景 写博客有一个自己的图床是不错的选择,如果不借助工具,在markdown博客中添加图片的步骤如下: 截取图片,保存到本地(得来回点对话框,选择保存路径,选择文件类型,输入文件名). 上传到图 ...

  9. Android WebView 不支持 H5 input type="file" 解决方法

    最近因为赶项目进度,因此将本来要用原生控件实现的界面,自己做了H5并嵌入webview中.发现点击H5中 标签 不能打开android资源管理器. 通过网络搜索发现是因为 android webvie ...

  10. 一天搞定CSS: 浮动(float)及文档流--10

    浮动(float),一个我们即爱又恨的属性.爱,因为通过浮动,我们能很方便地布局: 恨,浮动之后遗留下来太多的问题需要解决,特别是IE6-7(以下无特殊说明均指 windows 平台的 IE浏览器). ...