好了接着上篇分析,tomcat分析完请求头之后,是怎么把请求发送给对应的servlet的呢?
看到org.apache.coyote.http11.Http11Processor类的service方法中,有这么一句:
getAdapter().service(request, response);
这里的request会把浏览器的发过来的信息,解析之后放在这里.
跟进,service方法,部分代码如下:
@Override
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
......
// 这部分把org.apache.coyote.Request和org.apache.coyote.Response
// 包装成平时我们写servlet需要的request 和response
if (request == null) {
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
request.setResponse(response);
response.setRequest(request);
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
......
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
postParseSuccess = postParseRequest(req, request, res, response);// 在这里匹配路径
if (postParseSuccess) {
......
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);// 这里传递请求
}
......
}
看到postParseRequest,会在该方法里面索引到我们之前放在mapper里面的host的信息
postParseSuccess = postParseRequest(req, request, res, response);
postParseRequest的部分代码如下:
protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
org.apache.coyote.Response res, Response response) throws IOException, ServletException {
......
MessageBytes serverName; // 获取服务器的名字,比如localhost
......
String version = null;
Context versionContext = null;
boolean mapRequired = true;
while (mapRequired) {
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData()); //在这里匹配路径---
// 找不到context,就是我们发布的应用
if (request.getContext() == null) {
res.setStatus(404);
res.setMessage("Not found");
......
return false;
}
......
}
......
return false;
}
......
return true;
}
根据之前的分析知道,生成的host容器会存放在service类的mapper属性里面,所以,getMapper就会获取到service里面的mapper的.
看到connector.getService().getMapper().map的方法,该方法传入的参数分别为:
serverName就是之前host的名字,如
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
Host的标签name,就是localhost. decodedURI请求路径如:/index,version版本号,
request.getMappingData(),还有的就是mappingData用来储存host和host下所有的webapp的类.等下索引到的host信息就放在这个属性下了.Mapper类的map方法里面会调用internalMap方法,部分代码如下:
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
// 通过主机名,如localhost找到host容器
MappedHost[] hosts = this.hosts;
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
if (mappedHost == null) {
if (defaultHostName == null) {
return;
}
mappedHost = exactFind(hosts, defaultHostName);
if (mappedHost == null) {
return;
}
}
mappingData.host = mappedHost.object;
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, uri);
......
int lastSlash = -1;
int uriEnd = uri.getEnd();
int length = -1;
boolean found = false;
MappedContext context = null;
......
while (pos >= 0) {
context = contexts[pos];
if (uri.startsWith(context.name)) { // 在这里匹配webapp的
length = context.name.length();
if (uri.getLength() == length) {
found = true;
break;
} else if (uri.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = nthSlash(uri, contextList.nesting + 1);
} else {
lastSlash = lastSlash(uri);
}
uri.setEnd(lastSlash);
pos = find(contexts, uri);
}
......
// 在这里匹配filter和servlet的
if (!contextVersion.isPaused()) {
internalMapWrapper(contextVersion, uri, mappingData);
}
}
该方法主要以下工作:
1,通过servername在mapper里面找到host容器.
2,然后通过请求路径在找到的host里面找到context容器.
3,接着通过请求路径,知道wrapper,也就保存了servelet的包装类.
好了匹配host,context,和wrapper的结束之后,继续看到,org.apache.coyote.http11.Http11Processor类的service方法中的
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
这个invoke方法会在一次调用放在pipeline类里面的invoke方法,在这里我们只需要看到org.apache.catalina.core.StandardWrapperValve里面的invoke方法即可,部分代码如下:
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
......
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
......
if (!unavailable) {
servlet = wrapper.allocate(); // 在这里创建servlet的实例,有的话就重用,没有的话就加载
}
......
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
if ((servlet != null) && (filterChain != null)) {
if (context.getSwallowOutput()) {
......
filterChain.doFilter(request.getRequest(),
response.getResponse());
......
} else {
......
}
}
......
if (servlet != null) {
wrapper.deallocate(servlet);
}
......
}
该方法主要做了以下几个工作:
1,索引到servlet
2,创建过滤器链,也就是filterChain
因为之前在分析web.xml的时候就分析过servlet的创建了,在这里就不再分析.现在主要分析的是filterChain的创建,
在分析web.xml的时候知道filter的信息是放在FilterDef里面的,而FilterDef又保存在standardContext里面,代码如下:
for (FilterDef filter : webxml.getFilters().values()) {
......
context.addFilterDef(filter); // 添加过滤器
}
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
看到
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
createFilterChain的部分代码如下:
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
......
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
......
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
......
String servletName = wrapper.getName();
// 根据路径匹配的过滤器
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
// 根据servlet匹配的过滤器
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
return filterChain;
}
该方法主要是创建filterChain,还有选择匹配路径的过滤器.
接着看回invoke方法中有:
filterChain.doFilter(request.getRequest(),
response.getResponse());
看到这里相信大家都很熟悉了,就不用详细讲了.
分析到这里tomcat的整个请求过程基本分析完了.后面有时间的话会详细分析,tomcat是如何解析客户端发过来的信息的,比如:如何处理get和post发送过来的数据,以及如何处理servlet3.0支持的异步发送信息的方.
在分析源码的过程中,难免有错漏和不足之处,望各位海涵吧.如果大家在发现有错漏之处,可以给我留言.