servlet解析演进(3) -容器

摘要:上文说HttpProcessor在启动之后监听socket端口,如果socket端口有请求,该方法就调用方法connector.getContainer().invoke(request, response);去处理请求。接下来,我们来看看容器在整个请求处理过程中的作用。 本文介绍最重要的四个容器: 1、Engine:表示整个Catalina的servlet引擎 2、Host:表示一个拥

上文说HttpProcessor在启动之后监听socket端口,如果socket端口有请求,该方法就调用方法connector.getContainer().invoke(request,response);去处理请求。接下来,我们来看看容器在整个请求处理过程中的作用。

本文介绍最重要的四个容器:

1 (38).jpg

 

1、Engine:表示整个Catalina的servlet引擎

2、Host:表示一个拥有数个上下文的虚拟主机

3、Context:表示一个Web应用,一个context包含一个或多个wrapper

4、warpper:表示一个独立的servlet

这四个容器都实现了接口Container,所以他们都叫容器,他们之间有上下级别的关系,如图所示

1、简单容器

连接器管理socket监听,一旦页面有对应端口请求,那么容器就来处理。也就是上文中说的这个方法。

connector.getContainer().invoke(request,response);

我们在页面请求servlet是这个样子的:http://localhost:8080/Primitive。那么这个URI是如何解析的呢?

首先引入Maper接口,mapper接口和容器关联,负责具体处理我们的URI对应的servlet类。

publicinterfaceMapper{ /** *mapper关联的容器 */ publicContainergetContainer(); /** 设置关联容器 */ publicvoidsetContainer(Containercontainer); /**返回mapper对应的协议 *ReturntheprotocolforwhichthisMapperisresponsible. */ publicStringgetProtocol(); /** *设置mapper对应的协议 */ publicvoidsetProtocol(Stringprotocol); /**返回该请求的负责容器。*/ publicContainermap(Requestrequest,booleanupdate); } 简单的Mapper接口实现如下: publicclassSimpleContextMapperimplementsMapper{ /**mapper关联容器 */ privateSimpleContextcontext=null; publicContainergetContainer(){ return(context); } publicvoidsetContainer(Containercontainer){ if(!(containerinstanceofSimpleContext)) thrownewIllegalArgumentException ("Illegaltypeofcontainer"); context=(SimpleContext)container; } publicStringgetProtocol(){ returnnull; } publicvoidsetProtocol(Stringprotocol){ } /**返回处理这个请求的子容器 */ publicContainermap(Requestrequest,booleanupdate){ //获取URI StringcontextPath= ((HttpServletRequest)request.getRequest()).getContextPath(); StringrequestURI=((HttpRequest)request).getDecodedRequestURI(); StringrelativeURI=requestURI.substring(contextPath.length()); // Wrapperwrapper=null; StringservletPath=relativeURI; StringpathInfo=null; //从容器的servletMappings字段获得servlet名称。 Stringname=context.findServletMapping(relativeURI); if(name!=null) //从容器的children字段中获得该servlet名称对应的Wrapper类实例 wrapper=(Wrapper)context.findChild(name); return(wrapper); } }

代码中map方法返回了我们请求URI对应的处理容器。简单的处理容器主要就是处理我们请求的servlet。具体的他包含两个方面的内容:

1、生成servlet实例。

2、为管道添加处理逻辑。

生成servlet实例主要就是通过类加载器加载servlet类。然后实例化该servlet类并且调用init()方法执行实例初始化过程。为管道添加逻辑就涉及到了管道和Vavle的概念。

管道其实就是一个责任链,为servlet初始化后添加一些处理逻辑Vavle。看代码:

publicclassSimplePipelineimplementsPipeline{ publicSimplePipeline(Containercontainer){ setContainer(container); } //基础的Valve可有可无 protectedValvebasic=null; //该Pipleline关联的容器 protectedContainercontainer=null; //该pipeline包含的valves数组 protectedValvevalves[]=newValve[0]; //添加valve publicvoidaddValve(Valvevalve){ if(valveinstanceofContained) //为valve设置容器为当前类的容器。 ((Contained)valve).setContainer(this.container); //线程安全扩容放置新增vavles synchronized(valves){ Valveresults[]=newValve[valves.length+1]; System.arraycopy(valves,0,results,0,valves.length); results[valves.length]=valve; valves=results; } } publicvoidinvoke(Requestrequest,Responseresponse) throwsIOException,ServletException{ //生成内部类SimplePipelineValveContext调用责任链 (newSimplePipelineValveContext()).invokeNext(request,response); } publicvoidremoveValve(Valvevalve){ } //内部类 protectedclassSimplePipelineValveContextimplementsValveContext{ protectedintstage=0; publicStringgetInfo(){ returnnull; } //责任链调用 publicvoidinvokeNext(Requestrequest,Responseresponse) throwsIOException,ServletException{ intsubscript=stage; stage=stage+1; //InvoketherequestedValveforthecurrentrequestthread if(subscript<valves.length){ valves[subscript].invoke(request,response,this); } elseif((subscript==valves.length)&&(basic!=null)){ basic.invoke(request,response,this); } else{ thrownewServletException("Novalve"); } } }//endofinnerclass }

2、管道的原理。

管道其实就是一个责任链,他把需要处理的逻辑全部都组装成为Vavle然后一个一个的去执行,没执行一个首先是要去调下一个Vavle,如果调用到底部没有其他了那就执行逻辑,返回之后就继续执行它上面的Vavle。

代码实现:

从连接器过来的请求首先调用了容器的invoker()。invoker里面就是去执行管道里面的逻辑处理。

publicvoidinvoke(Requestrequest,Responseresponse) throwsIOException,ServletException{ pipeline.invoke(request,response); } 管道的invoker需要调用责任链,那么他首先生成了内部类,然后调用invokeNext()方法去执行业务逻辑。 publicvoidinvoke(Requestrequest,Responseresponse) throwsIOException,ServletException{ //InvokethefirstValveinthispipelineforthisrequest (newSimplePipelineValveContext()).invokeNext(request,response); } protectedclassSimplePipelineValveContextimplementsValveContext{ protectedintstage=0; publicStringgetInfo(){ returnnull; } publicvoidinvokeNext(Requestrequest,Responseresponse) throwsIOException,ServletException{ intsubscript=stage; stage=stage+1; //InvoketherequestedValveforthecurrentrequestthread if(subscript<valves.length){ valves[subscript].invoke(request,response,this); } elseif((subscript==valves.length)&&(basic!=null)){ basic.invoke(request,response,this); } else{ thrownewServletException("Novalve"); } } }//endofinnerclass