servlet的解析分为两步实现,第一个是匹配到对应的Wrapper,第二个是加载对应的servlet并进行数据,这些数据是怎么到界面的,response.getWrite()获取对应的流,然后写入这个流中,这个流中就有上文的outputBuffer。
匹配到对应Wrapper
在上文中我们曾经走到过了doRun方法,现在就直接从这里开始
执行顺序如下:
NioEndpoint(run)==>下步调用doRun
NioEndpoint(doRun)==>下步调用state = handler.process(ka,status);
handler实例对象Http11ConnectionHandler其继承AbstractConnectionHandler
AbstractConnectionHandler(process) ==》下步调用 state = processor.process(wrapper);
processor实例对象Http11NioProcessor 其继承AbstractHttp11Processor
AbstractHttp11Processor(process) ==》下步调用getAdapter().service(request, response);
CoyoteAdapter.service(request,response)这个方法就已经接近核心处理了,代码如下:
在第一处标红的地方,对请求进行了解析,并且匹配到对应的主机和context和wrapper
在第二处标红的地方是加载servlet并进行调用处理
在第三处标红的地方是刷新流,响应到界面
@SuppressWarnings("deprecation")
@Override
publicvoid service(org.apache.coyote.Request req,
org.apache.coyote.Responseres)
throws Exception {
Request request = (Request)req.getNote(ADAPTER_NOTES);
Response response =(Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
//创建一个request对象
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES,request);
res.setNote(ADAPTER_NOTES,response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
if (connector.getXpoweredBy()){
response.addHeader("X-Powered-By",POWERED_BY);
}
boolean comet =false;
boolean async = false;
boolean postParseSuccess = false;
try {
//设置执行线程线程名
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
//对uri进行解码,主要是解析报文,如果不合法返回响应码400
postParseSuccess = postParseRequest(req,request, res, response);
if (postParseSuccess) {
//设置是否支持异步
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
//不断调用管道加载对应的servlet进行调用,其中传递了response参数,所以可以放入流数据
connector.getService().getContainer().getPipeline().getFirst().invoke(request,response);
if (request.isComet()) {
if (!response.isClosed()&& !response.isError()) {
comet = true;
res.action(ActionCode.COMET_BEGIN, null);
if (request.getAvailable()|| (request.getContentLength() >0 &&(!request.isParametersParsed()))) {
event(req, res,SocketStatus.OPEN_READ);
}
} else {
request.setFilterChain(null);
}
}
}
//如果是异步请求
if (request.isAsync()){
async = true;
ReadListener readListener= req.getReadListener();
if (readListener != null&&request.isFinished()) {
ClassLoader oldCL =null;
try {
oldCL =request.getContext().bind(false, null);
if (req.sendAllDataReadEvent()){
req.getReadListener().onAllDataRead();
}
} finally {
request.getContext().unbind(false,oldCL);
}
}
Throwable throwable =
(Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (!request.isAsyncCompleting()&& throwable !=null) {
request.getAsyncContextInternal().setErrorState(throwable, true);
}
} else if (!comet) {
//如果为同步请求,Flush并关闭输入输出流
request.finishRequest();
response.finishResponse();
}
} catch (IOExceptione) {
// Ignore
} finally{
AtomicBoolean error = new AtomicBoolean(false);
res.action(ActionCode.IS_ERROR,error);
if (request.isAsyncCompleting()&& error.get()) {
res.action(ActionCode.ASYNC_POST_PROCESS, null);
async = false;
}
if (!async&& !comet) {
if (postParseSuccess){
request.getMappingData().context.logAccess(
request, response,
System.currentTimeMillis()- req.getStartTime(),
false);
}
}
req.getRequestProcessor().setWorkerThreadName(null);
if (!comet &&!async) {
request.recycle();
response.recycle();
} else{
request.clearEncoders();
response.clearEncoders();
}
}
}
postParseRequest:CoyoteAdapter(org.apache.catalina.connector)
connector.getService().getMapper().map(serverName,decodedURI,version, request.getMappingData());
下面是request部分构造代码
protected final MappingDatamappingData =new MappingData();
public MappingDatagetMappingData() {
return mappingData;
}
根据调用方法,我们可以知道其传入的参数有request实例成员对象mappingData的引用类型,所以下面的匹配的Context以及Wrapper所到的mappingData都是当前request的属性
============================================
map: Mapper (org.apache.catalina.mapper)
internalMap(host.getCharChunk(),uri.getCharChunk(), version, mappingData);
internalMap:758,Mapper (org.apache.catalina.mapper)
在这里面匹配到了对应的虚拟主机,存放到了mappingData中去,以及Context主要采用了二分查找获取游标进行匹配
然后其调用internalMapWrapper(contextVersion,uri, mappingData);匹配到了Wrapper存放到mappingData,其匹配规则如下
/**
* a: 对全新的路径进行精准匹配
* b: 对全新的路径进行通配符匹配
* c: 根据全新的路径,进行查找是否存在相应的文件,如果存在相应的文件,则需要将该文件返回。在回前我们需要进一步确认,这个文件是不是讲文件内容源码返回,还是像jsp文件一样,进行一定的处理然后再返回,所以又要确认下文件的扩展名是怎样的
* c1: 尝试寻找能够处理该文件扩展名的servlet,即进行扩展名匹配,如果找到,则使用对应的servlet
* c2: 如果没找到,则默认使用defaultWrapper,即DefaultServlet(它只会将文件内容源码返回,不做任何处理)
* d: 对全新的路径进行扩展名匹配(与c的目的不同,c的主要目的是想返回一个文件的内容,在返回内容前涉及到扩展名匹配,所以4c的前提是存在对应路径的文件)
* 案例1: a.html,a、b没有匹配到,到c的时候,找到了该文件,然后又尝试扩展名匹配,来决定是走c1还是c2,由于.html还没有对应的servlet来处理,就使用了默认的DefaultServlet
* 案例2: a.jsp,同上,在走到c的时候,找到了处理.jsp对应的servlet,所以走了c1
* 案例3: a.action,如果根目录下有a.action文件,则走到c1的时候,进行扩展名匹配,匹配到了SecondServlet,即走了c1,使用SecondServlet来处理请求;如果根目录下没有a.action文件,则走到了d,进行扩展名匹配,同样匹配到了SecondServlet,即走了d,同样使用SecondServlet来处理请求
* 案例4: first/abc,执行b的时候,就匹配到了FirstServlet,所以使用FirstServlet来处理请求
* */
private final void internalMapWrapper(ContextVersioncontextVersion,
CharChunkpath,
MappingDatamappingData)throws IOException {
。。。。。。。
if (found) {
mappingData.wrapperPath.setString(wrappers[pos].name);
if (path.getLength() >length) {
mappingData.pathInfo.setChars
(path.getBuffer(),
path.getOffset()+ length,
path.getLength()- length);
}
mappingData.requestPath.setChars
(path.getBuffer(), path.getOffset(),path.getLength());
mappingData.wrapper=wrappers[pos].object;
mappingData.jspWildCard=wrappers[pos].jspWildCard;
}
}
}
一起执行顺序来看一下一个servlet如何进行加载
invoke:98,StandardEngineValve (org.apache.catalina.core)
代码如下:
/**
* 基于请求的服务名选择合适的虚拟主机进行请求处理
*
* 如果不能匹配到对应主机,返回对应的http错误
*
* @param request 执行请求
* @param response Response tobe produced
*
*/
@Override
publicfinal void invoke(Request request,Response response)
throws IOException,ServletException{
//根据请求找到对应的host
Host host =request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
//设置当前请求是否支持异步
if (request.isAsyncSupported()){
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
//org.apache.catalina.valves.AccessLogValve[localhost]
//org.apache.catalina.valves.ErrorReportValve[localhost]
//org.apache.catalina.core.StandardHostValve[localhost]
/**
* 调用host的第一个valve
*
* 其执行原理是获取根据管道获取第一个阀门AccessLogValve调用其invoke方法
*
* AccessLogValve的invoke第一行调用getNext().invoke()调用了ErrorReportValve
*
* 同理调用了StandardHostValve的invoke方法所以实际调用的最先的是invoke方法
* */
host.getPipeline().getFirst().invoke(request,response);
}
invoke:143,StandardHostValve (org.apache.catalina.core)
下步调用context.getPipeline().getFirst().invoke(request,response);
invoke:504,AuthenticatorBase(org.apache.catalina.authenticator)
下步调用getNext().invoke(request, response);
/**
*
* 基于URI的request获取对应的Wrapper如果没有匹配到返回一个HTTP错误
*
* 在这个方法中做的事情主要是获取wrapper然后进行对应管道的阀门进行调用
* @param request Request tobe processed
* @param response Response tobe produced
*
* @exception IOException if aninput/output error occurred
* @exception ServletException ifa servlet error occurred
*/
@Override
publicfinal void invoke(Request request,Response response)
throws IOException,ServletException{
// Disallow any directaccess to resources under WEB-INF or META-INF
MessageBytesrequestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/",0))
||(requestPathMB.equalsIgnoreCase("/META-INF"))
||(requestPathMB.startsWithIgnoreCase("/WEB-INF/",0))
||(requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
//获取当前request利用的wrapper
Wrapper wrapper =request.getWrapper();
if (wrapper == null||wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Acknowledge the request
try {
response.sendAcknowledgement();
} catch(IOExceptionioe) {
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"),ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,ioe);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
if (request.isAsyncSupported()){
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request,response);
}
invoke:98,StandardWrapperValve (org.apache.catalina.core)
其主要操作如下:获取到对应的StandardWrapper,然后分配一个servlet,具体在loadServlet中进行实例话,再分配由于是成员变量,只有第一次调用的时候才会进行分配,之后直接返回第一次的实例化对一下对象,具体看allocate方法
public final void invoke(Request request,Responseresponse)
throws IOException,ServletException{
//获取到对应的StandardWrapper
StandardWrapper wrapper= (StandardWrapper) getContainer();
//每个请求开始servlet都是为空
Servlet servlet = null;
Context context =(Context) wrapper.getParent();
try {
if (!unavailable){
servlet = wrapper.allocate();
}
}
MessageBytes requestPathMB =request.getRequestPathMB();
DispatcherTypedispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC)dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// 创建过滤器实例
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request,wrapper, servlet);
if ((servlet !=null) &&(filterChain !=null)) {
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()){
request.getAsyncContextInternal().doInternalDispatch();
} else if(comet) {
filterChain.doFilterEvent(request.getEvent());
} else{
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log =SystemLogHandler.stopCapture();
if (log != null&&log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()){
request.getAsyncContextInternal().doInternalDispatch();
} else if(comet) {
filterChain.doFilterEvent(request.getEvent());
} else{
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
}
//释放过滤器
if (filterChain!=null) {
if (request.isComet()){
// If this is a Cometrequest, then the same chain will be used for the
// processing of allsubsequent events.
filterChain.reuse();
} else{
filterChain.release();
}
}
// Deallocate theallocated servlet instance
try {
if (servlet !=null) {
wrapper.deallocate(servlet);
}
} catch (Throwablee) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.deallocateException",
wrapper.getName()),e);
if (throwable == null) {
throwable = e;
exception(request,response, e);
}
}
try {
if ((servlet !=null) &&
(wrapper.getAvailable() ==Long.MAX_VALUE)) {
wrapper.unload();
}
} catch (Throwablee) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()),e);
if (throwable == null) {
throwable = e;
exception(request,response, e);
}
}
long t2=System.currentTimeMillis();
long time=t2-t1;
processingTime += time;
if( time > maxTime)maxTime=time;
if( time < minTime)minTime=time;
}
servlet的调用
按照这个顺序执行完所有过滤器就会执行对应的servlet,这是因为在创建过滤器
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request,wrapper, servlet); 的时候,将servlet给注入进去了,当过滤器执行完了,会执行调用servlet的service, 由于自己写的servlet是会继承HttpServlet的,所以将调用其service方法
调用如下:
internalDoFilter:,ApplicationFilterChain
方法如下:下面展示了两个service ,同在HttpServlet只是方法的参数有所不同,加载过程先调用一个,然后第一个再调用第二个,根据请求方法调用自己对应的Servlet中的doGet等一些列方法
protected void service(HttpServletRequest req,HttpServletResponseresp)
throws ServletException,IOException{
//获取对应的方法
String method = req.getMethod();
/**
* 根据请求method调用对应方法
* GET ==>doGet(req, resp)
* head ==> doHead(req, resp)
* post ==>doPost(req,resp)
*
* */
if (method.equals(METHOD_GET)) {
long lastModified= getLastModified(req);
if (lastModified == -1) {
// servlet doesn't supportif-modified-since, no reason
// to go through furtherexpensive logic
doGet(req,resp);
} else{
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch(IllegalArgumentExceptioniae) {
// Invalid date header -proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince< (lastModified /1000 * 1000)) {
// If the servlet mod timeis later, call doGet()
// Round down to thenearest second for a proper compare
// A ifModifiedSince of-1 will always be less
maybeSetLastModified(resp,lastModified);
doGet(req,resp);
} else{
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified =getLastModified(req);
maybeSetLastModified(resp,lastModified);
doHead(req,resp);
} else if(method.equals(METHOD_POST)) {
doPost(req, resp);
} else if(method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if(method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if(method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if(method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NOservlet supports whatever
// method was requested, anywhereon this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = newObject[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg,errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,errMsg);
}
}
上面已经讲述了一个servlet调用的过程,他的信息是如何返回掉流中,我们的看一下response,getWrite方法
可以看出这个流最终将outputBuffer给封装,其write方法
所以是写到上文封装的流中,最后一并解析到页面,可以参照请求到响应流。