注:本文源码分析基于 tomcat 9.0.43,源码的gitee仓库仓库地址:gitee.com/funcy/tomca….
本文是tomcat源码分析的第六篇,上一篇文章中我们提前,在Poller线程中,tomcat的会把连接请求会包装为SocketProcessorBase,然后丢到线程池中运行。这其中的运行过程是怎么样的呢,最终又是怎么执行到servlet的?本文将为你一一揭晓。
需要注意的是,从把请求丢到线程池到servlet的执行,其中包含的链路非常多,通过调试的方式得到的调用链路如下:
这其中包含了http协议的解析、servlet规范的实现,对于这些我们就简单略过了,仅分析关键步骤。
1. 解析http协议:Http11Processor#service
tomcat解析http协议的方法为Http11Processor#service,方法如下:
public SocketState service(SocketWrapperBase<?> socketWrapper)
...
while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
sendfileState == SendfileState.DONE && !protocol.isPaused()) {
try {
// inputBuffer.parseRequestLine():解析处理请求行
if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
protocol.getKeepAliveTimeout())) {
...
}
// prepareRequestProtocol():处理 http 协议版本
prepareRequestProtocol();
if (protocol.isPaused()) {
...
} else {
keptAlive = true;
...
// inputBuffer.parseHeaders():解析请求头
if (!http09 && !inputBuffer.parseHeaders()) {
...
}
}
} catch (...) {
...
}
...
if (getErrorState().isIoAllowed()) {
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
// 准备request请求
prepareRequest();
} catch (Throwable t) {
...
}
}
...
if (getErrorState().isIoAllowed()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
// 处理读事件
getAdapter().service(request, response);
...
} catch (...) {
...
}
}
...
}
...
}
这个方法非常长,不过我已经精简了许多,仅留下了关键方法,列举如下:
- 解析
http请求行:inputBuffer.parseRequestLine() - 处理
http协议版本:prepareRequestProtocol() - 解析
http请求头:inputBuffer.parseHeaders() - 准备
http请求数据:prepareRequest() - 继续处理请求:
CoyoteAdapter#service(request, response)
上面4个方法看得让我十分惆怅,如果不是对http的每个细节有深入了解,不建议研究那几个方法,真的让人头大。
解析完http请求后,继续调用getAdapter().service(request, response);处理之后的逻辑,也就是 CoyoteAdapter#service(request, response) 方法.
2. 生成httpServletRequest/httpServletResponse:CoyoteAdapter#service(request, response)
httpServletRequest/httpServletResponse是在CoyoteAdapter#service(request, response)中生成的,代码如下:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// 获取 httpServletRequest,得到的值为null
Request request = (Request) req.getNote(ADAPTER_NOTES);
// 获取 httpServletResponse,得到的值为null
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// 创建 request 对象
request = connector.createRequest();
request.setCoyoteRequest(req);
// 创建 response 对象
response = connector.createResponse();
response.setCoyoteResponse(res);
...
}
...
try {
// 处理参数,指 servlet 规范的一些参数,如果请求方法,sessionId
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// 调用 container 的 valve 处理
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
...
} catch (IOException e) {
// Ignore
} finally {
...
}
}
在tomcat中,Request与Response有两种类型:
org.apache.coyote.Request与org.apache.coyote.Response,tomcat 提供的,用来存放的http连接数据的org.apache.catalina.connector.Request与org.apache.catalina.connector.Response,也是tomcat提供的,不过它分分别实现了HttpServletRequest/HttpServletResponse
这里我们来看看org.apache.catalina.connector.Request 的创建过程:
request = connector.createRequest();
再进入Connector#createRequest方法:
public Request createRequest() {
return new Request(this);
}
调用的是构造方法,继续跟进去:
/**
* 实现了 HttpServletRequest
*/
public class Request implements HttpServletRequest {
/** 连接器 */
protected final Connector connector;
/** 这是 `org.apache.coyote.Request`, 用来存放 http 请求连接的数据 */
protected org.apache.coyote.Request coyoteRequest;
...
public Request(Connector connector) {
this.connector = connector;
formats = new SimpleDateFormat[formatsTemplate.length];
for(int i = 0; i < formats.length; i++) {
formats[i] = (SimpleDateFormat) formatsTemplate[i].clone();
}
}
/**
* 部分 httpServletRequest 的方法如下:
* 他们最终调用的是 coyoteRequest
*/
@Override
public String getMethod() {
return coyoteRequest.method().toString();
}
@Override
public String getRequestURI() {
return coyoteRequest.requestURI().toString();
}
...
}
到这里,Request 只是创建了出来,并没有做什么实质性的工作,我们继续看下去。
3. 解析reqeust:postParseRequest(...)
让我们回到CoyoteAdapter#service()方法,创建完Request/Response,接下来就是把这个里面装东西了:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
...
try {
// 处理参数,指 servlet 规范的一些参数,如果请求方法,sessionId
postParseSuccess = postParseRequest(req, request, res, response);
...
}
...
}
处理参数解析的方法为CoyoteAdapter#postParseRequest,里面处理了servlet规范的一些参数,其实就是给httpServletRequest类型的Request进行赋值,它的代码如下:
protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
org.apache.coyote.Response res, Response response) throws IOException, ServletException {
// 解析 scheme
if (req.scheme().isNull()) {
// 解析 scheme,设置 httServletRequest 类型的 request
req.scheme().setString(connector.getScheme());
request.setSecure(connector.getSecure());
} else {
request.setSecure(req.scheme().equals("https"));
}
// 处理代理
String proxyName = connector.getProxyName();
int proxyPort = connector.getProxyPort();
if (proxyPort != 0) {
req.setServerPort(proxyPort);
} else if (req.getServerPort() == -1) {
if (req.scheme().equals("https")) {
req.setServerPort(443);
} else {
req.setServerPort(80);
}
}
if (proxyName != null) {
req.serverName().setString(proxyName);
}
MessageBytes undecodedURI = req.requestURI();
// 处理请求方法
if (undecodedURI.equals("*")) {
if (req.method().equalsIgnoreCase("OPTIONS")) {
StringBuilder allow = new StringBuilder();
allow.append("GET, HEAD, POST, PUT, DELETE, OPTIONS");
// Trace if allowed
if (connector.getAllowTrace()) {
allow.append(", TRACE");
}
res.setHeader("Allow", allow.toString());
connector.getService().getContainer().logAccess(request, response, 0, true);
return false;
} else {
response.sendError(400, "Invalid URI");
}
}
// 其他的一些解析操作就不看了
...
while (mapRequired) {
// 解析servlet相关内容,如 Host, Context,Wrapper 等
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
...
}
...
return true;
}
这个方法依旧十分长,所做的工作为解析http请求的数据,将其转换为HttpServletRequest所需要的参数,对于里面的一些细节就不分析了。
这个方法中调用了这样一段代码:
// 解析servlet相关内容,如 Host, Context,Wrapper 等
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
这段代码最终调用的是Mapper#map(),内容如下:
public void map(MessageBytes host, MessageBytes uri, String version,
MappingData mappingData) throws IOException {
// 拿到了 host 与 uri
if (host.isNull()) {
String defaultHostName = this.defaultHostName;
if (defaultHostName == null) {
return;
}
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
// 处理映射
internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}
这个方法拿到host与uri后,继续请求Mapper#internalMap方法,在这个方法里会处理http的请求路径,也就是根据请求路径找到最终执行的servlet,我们继续。
4. 处理映射路径:Mapper#internalMap
继续跟进Mapper#internalMap方法:
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
// 这一步相当于获取到项目中所有的 Host
MappedHost[] hosts = this.hosts;
// 查找 host
MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
...
mappingData.host = mappedHost.object;
if (uri.isNull()) {
return;
}
uri.setLimit(-1);
// 上面已经查找到了host,接下来就从该host下的所有context中查找
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
// 查找 context
int pos = find(contexts, uri);
if (pos == -1) {
return;
}
...
mappingData.contextPath.setString(context.name);
ContextVersion contextVersion = null;
// 前面已经找到了 context,接下来就是从该context下所有的contextVersions中查找
ContextVersion[] contextVersions = context.versions;
final int versionCount = contextVersions.length;
if (versionCount > 1) {
Context[] contextObjects = new Context[contextVersions.length];
for (int i = 0; i < contextObjects.length; i++) {
contextObjects[i] = contextVersions[i].object;
}
mappingData.contexts = contextObjects;
if (version != null) {
// 根据版本号查找contextVersions
contextVersion = exactFind(contextVersions, version);
}
}
if (contextVersion == null) {
contextVersion = contextVersions[versionCount - 1];
}
mappingData.context = contextVersion.object;
mappingData.contextSlashCount = contextVersion.slashCount;
if (!contextVersion.isPaused()) {
// 处理 wrapper
internalMapWrapper(contextVersion, uri, mappingData);
}
}
需要注意的是,这里的Host、Context并不是前面提到的StandardHost、StandardContext,而是MappedHost、MappedContext,MappedHost中存放的是一个个的MappedContext,MappedContext中存放的是一个个ContextVersion:
如MappedHost内容如下::
protected static final class MappedHost extends MapElement<Host> {
/** 存放一个个MappedContext的结构,继续看下去 */
public volatile ContextList contextList;
...
}
protected static final class ContextList {
/** 用数组存放 MappedContext */
public final MappedContext[] contexts;
...
}
MappedContext内容如下:
protected static final class MappedContext extends MapElement<Void> {
/** 存放多个ContextVersion,使用数组存放 */
public volatile ContextVersion[] versions;
...
}
4.1 查找 host
那么他们是如何查找host的呢?在tomcat创建host时,默认为设置一个hostname:
public class Tomcat {
protected String hostname = "localhost";
public Host getHost() {
// 获取 engine,不存在时会创建
Engine engine = getEngine();
if (engine.findChildren().length > 0) {
return (Host) engine.findChildren()[0];
}
// 创建 Host
Host host = new StandardHost();
host.setName(hostname);
// 添加到 engine
getEngine().addChild(host);
return host;
}
...
}
在http请求时,tomcat会解析传入的host,当这两者匹配时,就表示找到了对应的host,看下Mapper#exactFindIgnoreCase就明白了:
private static final <T, E extends MapElement<T>> E exactFindIgnoreCase(
E[] map, CharChunk name) {
// 查找map数组中与name相等或最接近的元素,返回下标
int pos = findIgnoreCase(map, name);
if (pos >= 0) {
// 再一次判断,因为 findIgnoreCase(...) 返回的是与给定最接近或相等的下标
E result = map[pos];
if (name.equalsIgnoreCase(result.name)) {
return result;
}
}
return null;
}
4.2 查找 Context
Context又是如何查找的呢?在添加Context时,我们是这么做的:
// 创建 context
String docBase = System.getProperty("java.io.tmpdir");
Context context = tomcat.addContext("", docBase);
在tomcat.addContext(...)方法中,我们可以给Context指定一个请求路径,像这样:
Context context = tomcat.addContext("/api", docBase);
这样就表示以api为前缀的请求使用该Context处理,Mapper#internalMap方法在查找Context时,就是根据uri查找对匹配的Context的。
4.3 处理wrapper匹配:Mapper#internalMapWrapper
查找到Context后,接着就是处理Wrapper了,方法为Mapper#internalMapWrapper:
private final void internalMapWrapper(ContextVersion contextVersion, CharChunk path,
MappingData mappingData) throws IOException {
// 获取所有的 exactWrappers
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
// 进行查找操作
internalMapExactWrapper(exactWrappers, path, mappingData);
// 省略了很多的内容
...
}
这个方法也是非常长,处理了各种匹配规则,这里我们仅看其中一个,了解下吧匹配流程吧,进入Mapper#internalMapExactWrapper方法:
private final void internalMapExactWrapper(MappedWrapper[] wrappers,
CharChunk path, MappingData mappingData) {
// 路径匹配,请求的uri是否匹配servlet path
MappedWrapper wrapper = exactFind(wrappers, path);
if (wrapper != null) {
mappingData.requestPath.setString(wrapper.name);
// 找到后赋值
mappingData.wrapper = wrapper.object;
if (path.equals("/")) {
mappingData.pathInfo.setString("/");
mappingData.wrapperPath.setString("");
mappingData.contextPath.setString("");
mappingData.matchType = MappingMatch.CONTEXT_ROOT;
} else {
mappingData.wrapperPath.setString(wrapper.name);
mappingData.matchType = MappingMatch.EXACT;
}
}
}
这里的path就是uri的路径,这一步是根据路径来判断是否匹配的,即判断传入的uri与servlet path是否切尔西,匹配成功后,就赋值给mappingData的wrapper属性了。
至些,请求对应的host、context、wrapepr就都找到了。
5. 调用XxxValve
让我们回到CoyoteAdapter#service方法:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
...
try {
// 处理参数,指 servlet 规范的一些参数,如果请求方法,sessionId
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// 调用 container 的 valve 处理
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
...
} catch (IOException e) {
// Ignore
} finally {
...
}
}
在解析完http参数、请求对应的servlet后,接着就开始调用container的 Pipeline 处理了:
// 调用 container 的 valve 处理
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
整个pipeline结构如下:
执行时就是这样一个接一个的Pipeline调用。Pipeline里存放的是什么呢?这就是所谓有Valve,StandardEngine、StandardHost、StandardContext、StandardWrapper等都着对应的Valve:
5.1 StandardEngineValve#invoke
我们跟进connector.getService().getContainer().getPipeline().getFirst().invoke(request, response),由于connector.getService().getContainer()得到的是StandardEngine,因此运行的是StandardEngineValve#invoke方法:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 使用对应的host来处理
Host host = request.getHost();
...
// 调用 host 的 valve 处理
host.getPipeline().getFirst().invoke(request, response);
}
该方法关键代码有两个:
request.getHost():获取该请求对应的host,这个就是前面Mapper#internalMap千辛万苦找到的与该请求对应的hosthost.getPipeline().getFirst().invoke(request, response):继续调用StandardHostValve#invoke方法
5.2 StandardHostValve#invoke
我们再进入StandardHostValve#invoke方法:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// 从 request 中得到使用的 context
Context context = request.getContext();
...
try {
...
try {
if (!response.isErrorReportRequired()) {
// 调用 context 的 valve 处理
context.getPipeline().getFirst().invoke(request, response);
}
} catch (Throwable t) {
...
}
} finally {
...
}
这个方法的调用套路与StandardEngineValve#invoke是一样的:
request.getContext():从request中得到该请求对应的context,这个也是在Mapper#internalMap方法找到的context.getPipeline().getFirst().invoke(request, response)继续调用StandardContextValve#invoke处理
5.3 StandardContextValve#invoke
StandardContextValve#invoke方法如下:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
...
// 从 request 中拿到 wrapper
Wrapper wrapper = request.getWrapper();
...
// 调用 wrapper 的 valve 处理
wrapper.getPipeline().getFirst().invoke(request, response);
}
嗯,还是与StandardEngineValve#invoke方法一样的套路,我们继续看StandardWrapperValve#invoke方法。
6. StandardWrapperValve#invoke
我们再来看看Pipeline调用图:
从调用上来看,StandardWrapperValve已经调用到底了,servlet的调用就是在这里发生的,该方法代码如下:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
...
try {
if (!unavailable) {
// 获取 servlet,如果实例存在,直接返回,否则就创建实例并调用 Servlet#init 方法
servlet = wrapper.allocate();
}
} catch (...) {
...
}
...
// 创建过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// 在这里调用 filterChain.doFilter
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 调用 filter
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 {
// 调用 filter
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch (...) {
...
} finally {
...
}
}
这个方法依旧比较长,不过非关键点我都已经取消了,只留下了三个关键操作:
servlet = wrapper.allocate():获取servlet,如果servlet的loadOnStart小于0,那么在这里才会实例化并调用Servlet#init方法ApplicationFilterFactory.createFilterChain(...):创建过滤器链filterChain.doFilter(...):调用过滤器操作
接下来我们就来分析这几个方法。
6.1 StandardWrapper#allocate
在前面分析servlet的加载时,对于loadOnStartup小于0的servlet并没有处理(没有实例化,没有执行servlet#init方法),这个方法里会实例化loadOnStartup小于0的servlet的,代码如下:
public Servlet allocate() throws ServletException {
...
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// 实例不存在且未初始化
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
...
// 调用 StandardWrapper#loadServlet 方法
instance = loadServlet();
newInstance = true;
...
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
if (!instanceInitialized) {
initServlet(instance);
}
}
}
...
}
...
}
这个先判断实例是否存在,是否进行进行过初始化操作,如果满足条件则调用StandardWrapper#loadServlet方法,关于这个方法前面的文章中已经分析过了,它所做的主要工作就两个:
- 实例化
servlet - 调用
Servlet#init(...)方法
6.2 ApplicationFilterFactory#createFilterChain
ApplicationFilterFactory#createFilterChain方法所做的是创建过滤器链,代码如下:
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
if (servlet == null)
return null;
ApplicationFilterChain filterChain = null;
// 创建 filterChain 对象
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);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 拿到 warpper 对应的 Context,然后从 context中拿到 filterMaps
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
...
// 添加满足servlet路径的filter到过滤器链
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
// 判断哪些filter满足servlet的路径
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
// 添加满足servlet名称的filter到过滤器链
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMap, servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
return filterChain;
}
ApplicationFilterFactory#createFilterChain方法所略长,不过逻辑相当清晰,它主要是用来创建过滤器链的,流程如下:
- 创建
ApplicationFilterChain对象 - 获取当前
wrapper对应的Context,然后从Context拿到filterMaps,在Context中有一个结构专门用来存放filter的,关于filter的加载,前面的文章也分析了,这里就不再分析了 - 查找满足条件的
filter,然后添加到filterChain中,注意,这里满足条件的filter是指满足当前servlet的filter,处理匹配条件时,是通过servlet路径与servlet名称进行匹配的,两者满足其一即可。
6.3 ApplicationFilterChain#doFilter
获取到过滤器链后,接下来就是执行了,方法为ApplicationFilterChain#doFilter:
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
(java.security.PrivilegedExceptionAction<Void>) () -> {
internalDoFilter(req,res);
return null;
}
);
} catch( PrivilegedActionException pe) {
...
}
} else {
// 执行过滤器
internalDoFilter(request,response);
}
}
调用了internalDoFilter(...)执行,继续跟进去:
/** 当前正在使用的过滤器的下标索引 */
private int pos = 0;
/** 当前过滤器链中过滤器的数量 */
private int n = 0;
/** 存放过滤器的地方 */
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
/**
* 执行过滤器
*/
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 判断是否执行完了
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
// 获取要执行的 filter
Filter filter = filterConfig.getFilter();
...
// 执行 filter的doFilter(...) 方法
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (...) {
...
}
return;
}
try {
...
// 调用 servlet#service 方法
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)
&& Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service", servlet,
classTypeUsedInService, args, principal);
} else {
// 执行 servlet#service 方法
servlet.service(request, response);
}
} catch (...) {
...
}
}
internalDoFilter(...)分为两个部分:
- 执行
filter.doFilter(...)方法 - 执行
servlet.service(...)方法
在ApplicationFilterChain中有两个成员变量:
n:当前过滤器链中过滤器的数量post:当前使用的过滤器的下标索引
在过滤器的调用时,会根据这两个参数的大小判断要不要进入过滤器的调用:
// 判断是否执行完了
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
// 获取要执行的 filter
Filter filter = filterConfig.getFilter();
...
// 执行 filter的doFilter(...) 方法
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (...) {
...
}
return;
}
这个filter.doFilter(request, response, this)就是我们真正调用Filter的代码了。
执行完当前的filter后,是怎么执行到下一个的呢?我们在实现Filter时,都会有这么一句代码:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 你的业务逻辑
...
// 调用 FilterChain#doFilter 方法
chain.doFilter(request, response)
// 你的业务逻辑
...
}
在tomcat中,FilterChain#doFilter调用的就是ApplicationFilterChain#doFilter方法,接着又到了ApplicationFilterChain#internalDoFilter方法,这次调用时,pos的值会加1,然后继续调用Filter#doFilter方法,之后又调回ApplicationFilterChain#doFilter方法...如此循环往复的方法,在设计模式中,有一个专业的名字:责任链模式。
每一次调用ApplicationFilterChain#doFilter方法,pos的值就会加1,直到pos >= n后,filter就全部执行完了,接着就开始执行servlet了:
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// 判断是否执行完了
if (pos < n) {
// 执行 filter
...
}
try {
...
// 调用 servlet#service 方法
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)
&& Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
// 执行 servlet#service 方法
SecurityUtil.doAsPrivilege("service", servlet,
classTypeUsedInService, args, principal);
} else {
// 执行 servlet#service 方法
servlet.service(request, response);
}
} catch (...) {
...
}
}
关于servlet的执行,其实非常简单:前面已经获取到了servlet的实例,这里就直接调用其service()方法即可。
7. 总结
本文主要分析了tomcat线程池处理请求的整个过程,包括http协议的解析、httpServletRequest/httpServletResponse的创建及准备、解析映射路径(查找对应的host、context、wrapper)、执行filter与servlet,不过对于http的解析、servlet规范的实现,本文只是点出了处理的方法,并没有深入到这些方法的细节。
限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本文首发于微信公众号 Java技术探秘,链接:mp.weixin.qq.com/s/o6ZKNqdcd…. 如果您喜欢本文,欢迎关注该公众号,让我们一起在技术的世界里探秘吧!