Note: This article source code analysis based on Tomcat 9.0.43, source gitee warehouse warehouse address: gitee.com/funcy/tomca… .
This is the sixth article in the tomcat source code analysis. In the previous article, we mentioned that in Poller threads, Tomcat will wrap the connection request as SocketProcessorBase and throw it into the thread pool to run. How does this work, and how does it end up in the servlet? This article will tell you all about it.
It is important to note that there are many links, from dropping requests to the thread pool to executing the servlet. The call links obtained through debugging are as follows:
This includes the parsing of the HTTP protocol and the implementation of the Servlet specification, which we will skip briefly and analyze only the key steps.
1. The parsinghttp
Protocol:Http11Processor#service
Tomcat uses Http11Processor#service to parse the HTTP protocol.
public SocketState service(SocketWrapperBase
socketWrapper).while (! getErrorState().isError(a) && keepAlive && !isAsync(a) && upgradeToken = =null&& sendfileState == SendfileState.DONE && ! protocol.isPaused()) {try {
/ / inputBuffer parseRequestLine () : line analytical processing requests
if(! inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(), protocol.getKeepAliveTimeout())) { ... }// prepareRequestProtocol() : Handles the HTTP protocol version
prepareRequestProtocol();
if (protocol.isPaused()) {
...
} else {
keptAlive = true; .// inputBuffer.parseheaders () : parses the request headers
if (!http09 && !inputBuffer.parseHeaders()) {
...
}
}
} catch(...). {... }...if (getErrorState().isIoAllowed()) {
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
// Prepare the request
prepareRequest();
} catch(Throwable t) { ... }}...if (getErrorState().isIoAllowed()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
// Handle read eventsgetAdapter().service(request, response); . }catch(...). {... }}... }... }Copy the code
This method is quite long, but I’ve streamlined it down to the key methods, which are listed below:
- parsing
http
Request:inputBuffer.parseRequestLine()
- To deal with
http
Protocol Version:prepareRequestProtocol()
- parsing
http
Request header:inputBuffer.parseHeaders()
- To prepare
http
Request data:prepareRequest()
- Continue processing requests:
CoyoteAdapter#service(request, response)
The above four methods make me sad, and without a deep understanding of every detail of HTTP, I would not recommend studying them.
After parsing the HTTP request, continue to call getAdapter().service(request, response); The logic after processing, which is the CoyoteAdapter#service(request, response) method.
2. GeneratehttpServletRequest/httpServletResponse
:CoyoteAdapter#service(request, response)
Is it/httpServletResponse in CoyoteAdapter# service (request, response) generated in the HTML code is as follows:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
// Get httpServletRequest with the value null
Request request = (Request) req.getNote(ADAPTER_NOTES);
// Get httpServletResponse with the value null
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create a request object
request = connector.createRequest();
request.setCoyoteRequest(req);
// Create a Response objectresponse = connector.createResponse(); response.setCoyoteResponse(res); . }...try {
// Process parameters, which refer to some parameters of the servlet specification, if the request method, sessionId
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Invoke the valve process of the containerconnector.getService().getContainer().getPipeline().getFirst().invoke( request, response); }... }catch (IOException e) {
// Ignore
} finally{... }}Copy the code
In Tomcat, there are two types of Request and Response:
org.apache.coyote.Request
与org.apache.coyote.Response
Tomcat is provided for storagehttp
Connected to dataorg.apache.catalina.connector.Request
withorg.apache.catalina.connector.Response
Tomcat is also provided by Tomcat, but it is implemented separatelyHttpServletRequest/HttpServletResponse
Here we take a look at org. Apache. Catalina. The creation process of the Request:
request = connector.createRequest();
Copy the code
Connect #createRequest method:
public Request createRequest(a) {
return new Request(this);
}
Copy the code
The constructor is called, so follow along:
/** * implements HttpServletRequest */
public class Request implements HttpServletRequest {
/** connector */
protected final Connector connector;
/ * * this is ` org. Apache. Coyote. Request `, used to store data of the HTTP Request connection * /
protectedorg.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(); }}/** * Part of the httpServletRequest method is as follows: * They end up calling coyoteRequest */
@Override
public String getMethod(a) {
return coyoteRequest.method().toString();
}
@Override
public String getRequestURI(a) {
returncoyoteRequest.requestURI().toString(); }... }Copy the code
At this point, the Request has just been created without doing any real work, so let’s move on.
3. reqeustpostParseRequest(...)
Let’s go back to the CoyoteAdapter#service() method and after creating Request/Response, it’s time to load this:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {...try {
// Process parameters, which refer to some parameters of the servlet specification, if the request method, sessionIdpostParseSuccess = postParseRequest(req, request, res, response); . }... }Copy the code
CoyoteAdapter#postParseRequest handles some of the servlet specification’s parameters, which is essentially an assignment to an httpServletRequest Request.
protected boolean postParseRequest(org.apache.coyote.Request req, Request request, org.apache.coyote.Response res, Response response) throws IOException, ServletException {
/ / resolution scheme
if (req.scheme().isNull()) {
// Resolve scheme to set httServletRequest request
req.scheme().setString(connector.getScheme());
request.setSecure(connector.getSecure());
} else {
request.setSecure(req.scheme().equals("https"));
}
// Handle the agent
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();
// Process the request method
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"); }}// Some other parsing operations will be ignored.while (mapRequired) {
// Parse servlet contents such as Host, Context, Wrapper, etcconnector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData()); . }...return true;
}
Copy the code
This method is still quite long and does nothing to parse the HTTP request data and convert it to the parameters required for HttpServletRequest.
This method calls the following code:
// Parse servlet contents such as Host, Context, Wrapper, etc
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
Copy the code
This code ends up calling Mapper#map(), which looks like this:
public void map(MessageBytes host, MessageBytes uri, String version, MappingData mappingData) throws IOException {
// Get the host and uri
if (host.isNull()) {
String defaultHostName = this.defaultHostName;
if (defaultHostName == null) {
return;
}
host.getCharChunk().append(defaultHostName);
}
host.toChars();
uri.toChars();
// Handle the mapping
internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}
Copy the code
This method, once it gets the host and uri, continues to call the Mapper#internalMap method, which handles the HTTP request path, which is to find the servlet that ultimately executes according to the request path. Let’s move on.
4. Handle mapping paths:Mapper#internalMap
Follow up with the Mapper#internalMap method:
private final void internalMap(CharChunk host, CharChunk uri, String version, MappingData mappingData) throws IOException {
// This step is equivalent to getting all the hosts in the project
MappedHost[] hosts = this.hosts;
/ / find the hostMappedHost mappedHost = exactFindIgnoreCase(hosts, host); . mappingData.host = mappedHost.object;if (uri.isNull()) {
return;
}
uri.setLimit(-1);
// Now that we have found the host, we can find all the contexts under that host
ContextList contextList = mappedHost.contextList;
MappedContext[] contexts = contextList.contexts;
/ / find the context
int pos = find(contexts, uri);
if (pos == -1) {
return; }... mappingData.contextPath.setString(context.name); ContextVersion contextVersion =null;
// Now that the context is found, the next step is to look for all contextVersions of the context
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) {
// Find contextVersions based on the version numbercontextVersion = exactFind(contextVersions, version); }}if (contextVersion == null) {
contextVersion = contextVersions[versionCount - 1];
}
mappingData.context = contextVersion.object;
mappingData.contextSlashCount = contextVersion.slashCount;
if(! contextVersion.isPaused()) {/ / processing wrapperinternalMapWrapper(contextVersion, uri, mappingData); }}Copy the code
And notice that instead of StandardHost and StandardContext, MappedHost and MappedContext, MappedHost, MappedContext, MappedHost, MappedContext, MappedContext holds each ContextVersion:
For example, MappedHost content is as follows:
protected static final class MappedHost extends MapElement<Host> {
/** Store the structure of each MappedContext, continue */
public volatileContextList contextList; . }protected static final class ContextList {
/** Use array to store MappedContext */
public finalMappedContext[] contexts; . }Copy the code
MappedContext contains the following contents:
protected static final class MappedContext extends MapElement<Void> {
/** store multiple ContextVersions, use arrays to store */
public volatileContextVersion[] versions; . }Copy the code
4.1 find the host
So how do they find hosts? When tomcat creates a host, it defaults to a hostname:
public class Tomcat {
protected String hostname = "localhost";
public Host getHost(a) {
// Get engine, created when it doesn't exist
Engine engine = getEngine();
if (engine.findChildren().length > 0) {
return (Host) engine.findChildren()[0];
}
/ / create the Host
Host host = new StandardHost();
host.setName(hostname);
// Add to engine
getEngine().addChild(host);
returnhost; }... }Copy the code
Tomcat will parse the host passed in during the HTTP request. When the two match, the corresponding host is found.
private static final <T, E extends MapElement<T>> E exactFindIgnoreCase( E[] map, CharChunk name) {
// Find the element equal to or closest to name in the map array, return the subscript
int pos = findIgnoreCase(map, name);
if (pos >= 0) {
// Again, because findIgnoreCase(...) Returns the closest or equal subscript to the given
E result = map[pos];
if (name.equalsIgnoreCase(result.name)) {
returnresult; }}return null;
}
Copy the code
4.2 findContext
How do we find the Context? Here’s what we do when we add Context:
/ / create the context
String docBase = System.getProperty("java.io.tmpdir");
Context context = tomcat.addContext("", docBase);
Copy the code
In tomcat. AddContext (…). Method, we can specify a request path to the Context, like this:
Context context = tomcat.addContext("/api", docBase);
Copy the code
This means that requests prefixed with API are processed using the Context. The Mapper#internalMap method looks for the matching Context based on the uri.
4.3 processingwrapper
Match:Mapper#internalMapWrapper
Once the Context is found, the Wrapper method is Mapper#internalMapWrapper:
private final void internalMapWrapper(ContextVersion contextVersion, CharChunk path, MappingData mappingData) throws IOException {
// Get all exactWrappers
MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
// Perform a lookup
internalMapExactWrapper(exactWrappers, path, mappingData);
// A lot of content is omitted. }Copy the code
This method is also very long and handles various matching rules. Let’s look at just one of them here. Let’s look at the matching process and enter the Mapper#internalMapExactWrapper method:
private final void internalMapExactWrapper(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
// The path matches, whether the requested URI matches the servlet path
MappedWrapper wrapper = exactFind(wrappers, path);
if(wrapper ! =null) {
mappingData.requestPath.setString(wrapper.name);
// Find it and assign it
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; }}}Copy the code
Here the path is the path of the URI. This step is based on the path to determine whether there is a match, that is, whether the uri passed in is Chelsea to the Servlet path. After the match is successful, the Wrapper property of mappingData is assigned.
At this point, the host, context, and wrapepr corresponding to the request are found.
5. CallXxxValve
Let’s return to the CoyoteAdapter#service method:
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {...try {
// Process parameters, which refer to some parameters of the servlet specification, if the request method, sessionId
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Invoke the valve process of the containerconnector.getService().getContainer().getPipeline().getFirst().invoke( request, response); }... }catch (IOException e) {
// Ignore
} finally{... }}Copy the code
After parsing the HTTP parameters and requesting the corresponding servlet, the container Pipeline is called:
// Invoke the valve process of the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
Copy the code
The entire pipeline structure is as follows:
One Pipeline call after another is executed. What is stored in Pipeline? There are Valve, StandardEngine, StandardHost, StandardContext, StandardWrapper, etc.
5.1 StandardEngineValve#invoke
We follow up with connector.getService().getContainer().getPipeline().getFirst().invoke(request, response), Since connection.getservice ().getcontainer () returns StandardEngine, the StandardEngineValve#invoke method is run:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// use the corresponding host to handleHost host = request.getHost(); .// Call host valve processing
host.getPipeline().getFirst().invoke(request, response);
}
Copy the code
The method has two key codes:
request.getHost()
: gets the host corresponding to the request, which is the previous oneMapper#internalMap
The one that corresponds to the requesthost
host.getPipeline().getFirst().invoke(request, response)
: Continue callingStandardHostValve#invoke
methods
5.2 StandardHostValve#invoke
Let’s enter the StandardHostValve#invoke method again:
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Get the context to use from requestContext context = request.getContext(); .try{...try {
if(! response.isErrorReportRequired()) {// Call valve of the contextcontext.getPipeline().getFirst().invoke(request, response); }}catch(Throwable t) { ... }}finally{... }Copy the code
This method is called the same way as StandardEngineValve#invoke:
request.getContext()
From:request
To get the corresponding of the requestcontext
This one is also inMapper#internalMap
How to findcontext.getPipeline().getFirst().invoke(request, response)
Continue to callStandardContextValve#invoke
To deal with
5.3 StandardContextValve#invoke
The StandardContextValve#invoke method looks like this:
public final void invoke(Request request, Response response)
throws IOException, ServletException {...// Retrieve the wrapper from requestWrapper wrapper = request.getWrapper(); .// Call the Valve handling of the wrapper
wrapper.getPipeline().getFirst().invoke(request, response);
}
Copy the code
Well, again the same routine as the StandardEngineValve#invoke method, we continue with the StandardWrapperValve#invoke method.
6. StandardWrapperValve#invoke
Let’s look at the Pipeline call diagram:
From the point of view of the call, StandardWrapperValve has been called to the end, and the servlet is called from here. This method code is as follows:
public final void invoke(Request request, Response response)
throws IOException, ServletException {...try {
if(! unavailable) {// Get the servlet, return it if the instance exists, otherwise create the instance and call servlet #initservlet = wrapper.allocate(); }}catch(...). {... }...// Create a filter chain
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if((servlet ! =null) && (filterChain ! =null)) {
// Call filterchain-dofilter here
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
/ / call the filterfilterChain.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 {
/ / call the filterfilterChain.doFilter (request.getRequest(), response.getResponse()); }}}}catch (...) {
...
} finally{... }}Copy the code
This method is still quite long, but I have removed all the non-critical points, leaving only three key actions:
servlet = wrapper.allocate()
: getservlet
If theservlet
theloadOnStart
Less than zero, that’s where it gets instantiated and calledServlet#init
methodsApplicationFilterFactory.createFilterChain(...)
: Creates a filter chainfilterChain.doFilter(...)
: Invokes the filter operation
Let’s take a look at these methods.
6.1 StandardWrapper#allocate
Servlet #init (loadOnStartup < 0); servlet#init (loadOnStartup < 0);
public Servlet allocate(a) throws ServletException {...boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if(! singleThreadModel) {// The instance does not exist and is not initialized
if (instance == null| |! instanceInitialized) {synchronized (this) {
if (instance == null) {
try{...// call the StandardWrapper#loadServlet method
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); }}}... }... }Copy the code
This determines whether the instance exists and has been initialized, and if so calls the StandardWrapper#loadServlet method, which was analyzed in the previous article. It does two main things:
- instantiation
servlet
- call
Servlet#init(...)
methods
6.2 ApplicationFilterFactory#createFilterChain
The ApplicationFilterFactory#createFilterChain method does this:
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
if (servlet == null)
return null;
ApplicationFilterChain filterChain = null;
// Create a filterChain object
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 = newApplicationFilterChain(); req.setFilterChain(filterChain); }}}else {
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Get the warpper Context, and then get filterMaps from the ContextStandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); .// Add a filter that matches the servlet path to the filter chain
for (FilterMap filterMap : filterMaps) {
if(! matchDispatcher(filterMap, dispatcher)) {continue;
}
// Determine which filters satisfy the servlet path
if(! matchFiltersURL(filterMap, requestPath))continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
}
// Add a filter that matches the servlet name to the filter chain
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;
}
Copy the code
The ApplicationFilterFactory#createFilterChain method is longer, but the logic is fairly clear. It is used to create a chain of filters.
- create
ApplicationFilterChain
object - Gets the current
wrapper
The correspondingContext
And then fromContext
getfilterMaps
In theContext
There is a structure for storagefilter
aboutfilter
Load, the previous article also analyzed, here will not analyze - Find what satisfies the criteria
filter
And then add tofilterChain
C, and notice that this satisfies the conditionfilter
It means to satisfy the presentservlet
thefilter
Is passed when processing matching conditionsservlet
The path andservlet
If the name is matched, either of the two can be satisfied.
6.3 ApplicationFilterChain#doFilter
After obtaining the chain, the next step is to execute it using 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 {
// Execute the filterinternalDoFilter(request,response); }}Copy the code
Call the internalDoFilter (…). Carry out, continue to follow:
/** The index of the filter currently in use */
private int pos = 0;
/** The number of filters in the current filter chain */
private int n = 0;
/** Where the filter is stored */
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
/** * Execute filter */
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// Check whether the execution is complete
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
// Get the filter to executeFilter filter = filterConfig.getFilter(); .// Execute doFilter of filter (...) methods
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{...// Call the servlet#service method
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 {
Execute the servlet#service methodservlet.service(request, response); }}catch(...). {... }}Copy the code
internalDoFilter(…) Divided into two parts:
- perform
filter.doFilter(...)
methods - perform
servlet.service(...)
methods
There are two member variables in ApplicationFilterChain:
n
: The number of filters in the current filter chainpost
: Indicates the index of the filter currently in use
The size of these two parameters is used to determine whether to enter the filter call:
// Check whether the execution is complete
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
// Get the filter to executeFilter filter = filterConfig.getFilter(); .// Execute doFilter of filter (...) methods
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;
}
Copy the code
This filter.dofilter (request, response, this) is the code that actually calls filter.
After executing the current filter, how does it proceed to the next filter? When we implement Filter, we always have this code:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// Your business logic.// call the FilterChain#doFilter method
chain.doFilter(request, response)
// Your business logic. }Copy the code
In tomcat, FilterChain#doFilter calls ApplicationFilterChain#doFilter, and then ApplicationFilterChain#internalDoFilter. This call increments pos and continues with the Filter#doFilter method, which then returns to ApplicationFilterChain#doFilter… This iterative approach has a technical name in design patterns: the chain of responsibility pattern.
Each time the ApplicationFilterChain#doFilter method is called, the value of pos is incremented by 1 until pos >= n completes the filter and the servlet is executed:
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// Check whether the execution is complete
if (pos < n) {
/ / filter. }try{...// Call the servlet#service method
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};
Execute the servlet#service method
SecurityUtil.doAsPrivilege("service", servlet,
classTypeUsedInService, args, principal);
} else {
Execute the servlet#service methodservlet.service(request, response); }}catch(...). {... }}Copy the code
When it comes to servlet execution, it’s pretty simple: you’ve got an instance of the servlet, so you just call its service() method.
7. To summarize
This article mainly analyzes the whole process of tomcat thread pool processing request, Including the HTTP protocol parsing, it/httpServletResponse create and preparation, analytical mapping path (find corresponding host, context, wrapper), execute the filter with a servlet, However, for HTTP parsing, servlet specification implementation, this paper only points out the processing method, and did not go into the details of these methods.
Limited to the author’s personal level, there are inevitable mistakes in the article, welcome to correct! Original is not easy, commercial reprint please contact the author to obtain authorization, non-commercial reprint please indicate the source.
In this paper, starting from WeChat number public road, Java technology link: mp.weixin.qq.com/s/o6ZKNqdcd… If you like this article, welcome to pay attention to the public account, let’s explore the world of technology together!