综合教程
tomcat源码分析 (九)-凯发ag旗舰厅登录网址下载
我们接着上一篇文章的容器处理来讲,当postparserequest方法返回true时,则由容器继续处理,在service方法中有connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response)这一行:
- connector调用getservice()返回standardservice;
- standardservice调用getcontainer返回standardengine;
- standardengine调用getpipeline返回与其关联的standardpipeline;
engine处理请求
我们在前面的文章中讲过standardengine
的构造函数为自己的pipeline添加了基本阀standardenginevalve
,代码如下:
public standardengine() {
super();
pipeline.setbasic(new standardenginevalve());
try {
setjvmroute(system.getproperty("jvmroute"));
} catch(exception ex) {
log.warn(sm.getstring("standardengine.jvmroutefail"));
}
}
接下来我们看看standardenginevalve
的invoke()
方法。该方法主要是选择合适的host,然后调用host中pipeline的第一个valve的invoke()
方法。
public final void invoke(request request, response response)
throws ioexception, servletexception { // select the host to be used for this request
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());
} // ask this host to process this request
host.getpipeline().getfirst().invoke(request, response);
}
host.getpipeline().getfirst().invoke(request, response)
,可以看到 host 容器先获取自己的管道,再获取第一个阀门,我们再看看该阀门的 invoke 方法。host处理请求
分析host的时候,我们从host的构造函数入手,该方法主要是设置基础阀门。
public standardhost() {
super();
pipeline.setbasic(new standardhostvalve());
}
standardpipeline调用getfirst得到第一个阀去处理请求,由于基本阀是最后一个,所以最后会由基本阀去处理请求。
standardhost的pipeline里面一定有 errorreportvalve 与 standardhostvalve两个valve,errorreportvalve主要是检测 http 请求过程中是否出现过什么异常, 有异常的话, 直接拼装 html 页面, 输出到客户端。
我们看看errorreportvalve的invoke方法:
public void invoke(request request, response response)
throws ioexception, servletexception {
// perform the request
// 1. 先将 请求转发给下一个 valve
getnext().invoke(request, response);
// 2. 这里的 iscommitted 表明, 请求是正常处理结束
if (response.iscommitted()) {
return;
}
// 3. 判断请求过程中是否有异常发生
throwable throwable = (throwable) request.getattribute(requestdispatcher.error_exception);
if (request.isasyncstarted() && ((response.getstatus() < 400 &&
throwable == null) || request.isasyncdispatching())) {
return;
}
if (throwable != null) {
// the response is an error
response.seterror();
// reset the response (if possible)
try {
// 4. 重置 response 里面的数据(此时 response 里面可能有些数据)
response.reset();
} catch (illegalstateexception e) {
// ignore
}
// 5. 这就是我们常看到的 500 错误码
response.senderror(httpservletresponse.sc_internal_server_error);
}
response.setsuspended(false);
try {
// 6. 这里就是将 异常的堆栈信息组合成 html 页面, 输出到前台
report(request, response, throwable);
} catch (throwable tt) {
exceptionutils.handlethrowable(tt);
}
if (request.isasyncstarted()) {
// 7. 若是异步请求的话, 设置对应的 complete (对应的是 异步 servlet)
request.getasynccontext().complete();
}
}
该方法首先执行了下个阀门的 invoke 方法。然后根据返回的request 属性设置一些错误信息。那么下个阀门是谁呢?其实就是基础阀门了:standardhostvalve,该阀门的 invoke 的方法是如何实现的呢?
@override
public final void invoke(request request, response response)
throws ioexception, servletexception { // select the context to be used for this request
context context = request.getcontext();
if (context == null) {
response.senderror
(httpservletresponse.sc_internal_server_error,
sm.getstring("standardhost.nocontext"));
return;
} // bind the context cl to the current thread
if( context.getloader() != null ) {
// not started - it should check for availability first
// this should eventually move to engine, it's generic.
if (globals.is_security_enabled) {
privilegedactionpa = new privilegedsettccl(
context.getloader().getclassloader());
accesscontroller.doprivileged(pa);
} else {
thread.currentthread().setcontextclassloader
(context.getloader().getclassloader());
}
}
if (request.isasyncsupported()) {
request.setasyncsupported(context.getpipeline().isasyncsupported());
} // don't fire listeners during async processing
// if a request init listener throws an exception, the request is
// aborted
boolean asyncatstart = request.isasync();
// an async error page may dispatch to another resource. this flag helps
// ensure an infinite error handling loop is not entered
boolean erroratstart = response.iserror();
if (asyncatstart || context.firerequestinitevent(request)) { // ask this context to process this request
try {
context.getpipeline().getfirst().invoke(request, response);
} catch (throwable t) {
exceptionutils.handlethrowable(t);
if (erroratstart) {
container.getlogger().error("exception processing "
request.getrequesturi(), t);
} else {
request.setattribute(requestdispatcher.error_exception, t);
throwable(request, response, t);
}
} // if the request was async at the start and an error occurred then
// the async error handling will kick-in and that will fire the
// request destroyed event *after* the error handling has taken
// place
if (!(request.isasync() || (asyncatstart &&
request.getattribute(
requestdispatcher.error_exception) != null))) {
// protect against npes if context was destroyed during a
// long running request.
if (context.getstate().isavailable()) {
if (!erroratstart) {
// error page processing
response.setsuspended(false); throwable t = (throwable) request.getattribute(
requestdispatcher.error_exception); if (t != null) {
throwable(request, response, t);
} else {
status(request, response);
}
} context.firerequestdestroyevent(request);
}
}
} // access a session (if present) to update last accessed time, based on a
// strict interpretation of the specification
if (access_session) {
request.getsession(false);
} // restore the context classloader
if (globals.is_security_enabled) {
privilegedactionpa = new privilegedsettccl(
standardhostvalve.class.getclassloader());
accesscontroller.doprivileged(pa);
} else {
thread.currentthread().setcontextclassloader
(standardhostvalve.class.getclassloader());
}
}
context处理请求
接着context会去处理请求,同理,standardcontextvalve的invoke方法会被调用:
@override
public final void invoke(request request, response response)
throws ioexception, servletexception {
// disallow any direct access to resources under web-inf or meta-inf
messagebytes requestpathmb = 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;
} // select the wrapper to be used for this request
wrapper wrapper = request.getwrapper();
if (wrapper == null || wrapper.isunavailable()) {
response.senderror(httpservletresponse.sc_not_found);
return;
} // acknowledge the request
try {
response.sendacknowledgement();
} catch (ioexception ioe) {
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);
}
wrapper处理请求
wrapper是一个servlet的包装,我们先来看看构造方法。主要作用就是设置基础阀门standardwrappervalve
。
public standardwrapper() {
super();
swvalve=new standardwrappervalve();
pipeline.setbasic(swvalve);
broadcaster = new notificationbroadcastersupport();
}
接下来我们看看standardwrappervalve
的invoke()
方法。
@override
public final void invoke(request request, response response)
throws ioexception, servletexception { // initialize local variables we may need
boolean unavailable = false;
throwable throwable = null;
// this should be a request attribute...
long t1=system.currenttimemillis();
requestcount.incrementandget();
standardwrapper wrapper = (standardwrapper) getcontainer();
servlet servlet = null;
context context = (context) wrapper.getparent(); // check for the application being marked unavailable
if (!context.getstate().isavailable()) {
response.senderror(httpservletresponse.sc_service_unavailable,
sm.getstring("standardcontext.isunavailable"));
unavailable = true;
} // check for the servlet being marked unavailable
if (!unavailable && wrapper.isunavailable()) {
container.getlogger().info(sm.getstring("standardwrapper.isunavailable",
wrapper.getname()));
long available = wrapper.getavailable();
if ((available > 0l) && (available < long.max_value)) {
response.setdateheader("retry-after", available);
response.senderror(httpservletresponse.sc_service_unavailable,
sm.getstring("standardwrapper.isunavailable",
wrapper.getname()));
} else if (available == long.max_value) {
response.senderror(httpservletresponse.sc_not_found,
sm.getstring("standardwrapper.notfound",
wrapper.getname()));
}
unavailable = true;
} // allocate a servlet instance to process this request
try {
// 关键点1:这儿调用wrapper的allocate()方法分配一个servlet实例
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (unavailableexception e) {
container.getlogger().error(
sm.getstring("standardwrapper.allocateexception",
wrapper.getname()), e);
long available = wrapper.getavailable();
if ((available > 0l) && (available < long.max_value)) {
response.setdateheader("retry-after", available);
response.senderror(httpservletresponse.sc_service_unavailable,
sm.getstring("standardwrapper.isunavailable",
wrapper.getname()));
} else if (available == long.max_value) {
response.senderror(httpservletresponse.sc_not_found,
sm.getstring("standardwrapper.notfound",
wrapper.getname()));
}
} catch (servletexception e) {
container.getlogger().error(sm.getstring("standardwrapper.allocateexception",
wrapper.getname()), standardwrapper.getrootcause(e));
throwable = e;
exception(request, response, e);
} catch (throwable e) {
exceptionutils.handlethrowable(e);
container.getlogger().error(sm.getstring("standardwrapper.allocateexception",
wrapper.getname()), e);
throwable = e;
exception(request, response, e);
servlet = null;
} messagebytes requestpathmb = request.getrequestpathmb();
dispatchertype dispatchertype = 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);
// create the filter chain for this request
// 关键点2,创建过滤器链,类似于pipeline的功能
applicationfilterchain filterchain =
applicationfilterfactory.createfilterchain(request, wrapper, servlet); // call the filter chain for this request
// note: this also calls the servlet's service() method
try {
if ((servlet != null) && (filterchain != null)) {
// swallow output if needed
if (context.getswallowoutput()) {
try {
systemloghandler.startcapture();
if (request.isasyncdispatching()) {
request.getasynccontextinternal().dointernaldispatch();
} else {
// 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
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 {
// 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
filterchain.dofilter
(request.getrequest(), response.getresponse());
}
} }
} catch (clientabortexception e) {
throwable = e;
exception(request, response, e);
} catch (ioexception e) {
container.getlogger().error(sm.getstring(
"standardwrapper.serviceexception", wrapper.getname(),
context.getname()), e);
throwable = e;
exception(request, response, e);
} catch (unavailableexception e) {
container.getlogger().error(sm.getstring(
"standardwrapper.serviceexception", wrapper.getname(),
context.getname()), e);
// throwable = e;
// exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getavailable();
if ((available > 0l) && (available < long.max_value)) {
response.setdateheader("retry-after", available);
response.senderror(httpservletresponse.sc_service_unavailable,
sm.getstring("standardwrapper.isunavailable",
wrapper.getname()));
} else if (available == long.max_value) {
response.senderror(httpservletresponse.sc_not_found,
sm.getstring("standardwrapper.notfound",
wrapper.getname()));
}
// do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (servletexception e) {
throwable rootcause = standardwrapper.getrootcause(e);
if (!(rootcause instanceof clientabortexception)) {
container.getlogger().error(sm.getstring(
"standardwrapper.serviceexceptionroot",
wrapper.getname(), context.getname(), e.getmessage()),
rootcause);
}
throwable = e;
exception(request, response, e);
} catch (throwable e) {
exceptionutils.handlethrowable(e);
container.getlogger().error(sm.getstring(
"standardwrapper.serviceexception", wrapper.getname(),
context.getname()), e);
throwable = e;
exception(request, response, e);
} // release the filter chain (if any) for this request
// 关键点4,释放掉过滤器链及其相关资源
if (filterchain != null) {
filterchain.release();
} // 关键点5,释放掉servlet及相关资源
// deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (throwable e) {
exceptionutils.handlethrowable(e);
container.getlogger().error(sm.getstring("standardwrapper.deallocateexception",
wrapper.getname()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
} // if this servlet has been marked permanently unavailable,
// unload it and release this instance
// 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例
try {
if ((servlet != null) &&
(wrapper.getavailable() == long.max_value)) {
wrapper.unload();
}
} catch (throwable e) {
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;
}
通过阅读源码,我们发现了几个关键点。现罗列如下,后面我们会逐一分析这些关键点相关的源码。
- 关键点1:这儿调用wrapper的allocate()方法分配一个servlet实例
- 关键点2,创建过滤器链,类似于pipeline的功能
- 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
- 关键点4,释放掉过滤器链及其相关资源
- 关键点5,释放掉servlet及相关资源
- 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例
关键点1 - wrapper分配servlet实例
我们来分析一下wrapper.allocate()方法
@override
public servlet allocate() throws servletexception { // if we are currently unloading this servlet, throw an exception
// 卸载过程中,不能分配servlet
if (unloading) {
throw new servletexception(sm.getstring("standardwrapper.unloading", getname()));
} boolean newinstance = false; // if not singlethreadedmodel, return the same instance every time
// 如果wrapper没有实现singlethreadedmodel,则每次都会返回同一个servlet
if (!singlethreadmodel) {
// load and initialize our instance if necessary
// 实例为null或者实例还未初始化,使用synchronized来保证并发时的原子性
if (instance == null || !instanceinitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isdebugenabled()) {
log.debug("allocating non-stm instance");
} // note: we don't know if the servlet implements
// singlethreadmodel until we have loaded it.
// 加载servlet
instance = loadservlet();
newinstance = true;
if (!singlethreadmodel) {
// for non-stm, increment here to prevent a race
// condition with unload. bug 43683, test case
// #3
countallocated.incrementandget();
}
} catch (servletexception e) {
throw e;
} catch (throwable e) {
exceptionutils.handlethrowable(e);
throw new servletexception(sm.getstring("standardwrapper.allocate"), e);
}
}
// 初始化servlet
if (!instanceinitialized) {
initservlet(instance);
}
}
} if (singlethreadmodel) {
if (newinstance) {
// have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancepool) {
instancepool.push(instance);
ninstances ;
}
}
}
// 非单线程模型,直接返回已经创建的servlet,也就是说,这种情况下只会创建一个servlet
else {
if (log.istraceenabled()) {
log.trace(" returning non-stm instance");
}
// for new instances, count will have been incremented at the
// time of creation
if (!newinstance) {
countallocated.incrementandget();
}
return instance;
}
} // 如果是单线程模式,则使用servlet对象池技术来加载多个servlet
synchronized (instancepool) {
while (countallocated.get() >= ninstances) {
// allocate a new instance if possible, or else wait
if (ninstances < maxinstances) {
try {
instancepool.push(loadservlet());
ninstances ;
} catch (servletexception e) {
throw e;
} catch (throwable e) {
exceptionutils.handlethrowable(e);
throw new servletexception(sm.getstring("standardwrapper.allocate"), e);
}
} else {
try {
instancepool.wait();
} catch (interruptedexception e) {
// ignore
}
}
}
if (log.istraceenabled()) {
log.trace(" returning allocated stm instance");
}
countallocated.incrementandget();
return instancepool.pop();
}
}
总结下来,注意以下几点即可:
- 卸载过程中,不能分配servlet
- 如果不是单线程模式,则每次都会返回同一个servlet(默认servlet实现方式)
servlet
实例为null
或者servlet
实例还未初始化
,使用synchronized来保证并发时的原子性- 如果是单线程模式,则使用servlet对象池技术来加载多个servlet
接下来我们看看loadservlet()
方法
public synchronized servlet loadservlet() throws servletexception { // nothing to do if we already have an instance or an instance pool
if (!singlethreadmodel && (instance != null))
return instance; printstream out = system.out;
if (swallowoutput) {
systemloghandler.startcapture();
} servlet servlet;
try {
long t1=system.currenttimemillis();
// complain if no servlet class has been specified
if (servletclass == null) {
unavailable(null);
throw new servletexception
(sm.getstring("standardwrapper.notclass", getname()));
} // 关键的地方,就是通过实例管理器,创建servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
instancemanager instancemanager = ((standardcontext)getparent()).getinstancemanager();
try {
servlet = (servlet) instancemanager.newinstance(servletclass);
} catch (classcastexception e) {
unavailable(null);
// restore the context classloader
throw new servletexception
(sm.getstring("standardwrapper.notservlet", servletclass), e);
} catch (throwable e) {
e = exceptionutils.unwrapinvocationtargetexception(e);
exceptionutils.handlethrowable(e);
unavailable(null); // added extra log statement for bugzilla 36630:
// https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
if(log.isdebugenabled()) {
log.debug(sm.getstring("standardwrapper.instantiate", servletclass), e);
} // restore the context classloader
throw new servletexception
(sm.getstring("standardwrapper.instantiate", servletclass), e);
} if (multipartconfigelement == null) {
multipartconfig annotation =
servlet.getclass().getannotation(multipartconfig.class);
if (annotation != null) {
multipartconfigelement =
new multipartconfigelement(annotation);
}
} // special handling for containerservlet instances
// note: the instancemanager checks if the application is permitted
// to load containerservlets
if (servlet instanceof containerservlet) {
((containerservlet) servlet).setwrapper(this);
} classloadtime=(int) (system.currenttimemillis() -t1); if (servlet instanceof singlethreadmodel) {
if (instancepool == null) {
instancepool = new stack<>();
}
singlethreadmodel = true;
} // 调用servlet的init方法
initservlet(servlet); firecontainerevent("load", this); loadtime=system.currenttimemillis() -t1;
} finally {
if (swallowoutput) {
string log = systemloghandler.stopcapture();
if (log != null && log.length() > 0) {
if (getservletcontext() != null) {
getservletcontext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet;
}
关键的地方有两个:
- 通过实例管理器,创建servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
- 调用servlet的init方法
关键点2 - 创建过滤器链
创建过滤器链是调用的org.apache.catalina.core.applicationfilterfactory
的createfilterchain()
方法。我们来分析一下这个方法。该方法需要注意的地方已经在代码的comments里面说明了。
public static applicationfilterchain createfilterchain(servletrequest request,
wrapper wrapper, servlet servlet) { // if there is no servlet to execute, return null
if (servlet == null)
return null; // create and initialize a filter chain object
// 1. 如果加密打开了,则可能会多次调用这个方法
// 2. 为了避免重复生成filterchain对象,所以会将filterchain对象放在request里面进行缓存
applicationfilterchain filterchain = null;
if (request instanceof request) {
request req = (request) request;
if (globals.is_security_enabled) {
// security: do not recycle
filterchain = new applicationfilterchain();
} else {
filterchain = (applicationfilterchain) req.getfilterchain();
if (filterchain == null) {
filterchain = new applicationfilterchain();
req.setfilterchain(filterchain);
}
}
} else {
// request dispatcher in use
filterchain = new applicationfilterchain();
} filterchain.setservlet(servlet);
filterchain.setservletsupportsasync(wrapper.isasyncsupported()); // acquire the filter mappings for this context
standardcontext context = (standardcontext) wrapper.getparent();
// 从这儿看出过滤器链对象里面的元素是根据context里面的filtermaps来生成的
filtermap filtermaps[] = context.findfiltermaps(); // if there are no filter mappings, we are done
if ((filtermaps == null) || (filtermaps.length == 0))
return (filterchain); // acquire the information we will need to match filter mappings
dispatchertype dispatcher =
(dispatchertype) request.getattribute(globals.dispatcher_type_attr); string requestpath = null;
object attribute = request.getattribute(globals.dispatcher_request_path_attr);
if (attribute != null){
requestpath = attribute.tostring();
} string servletname = wrapper.getname(); // add the relevant path-mapped filters to this filter chain
// 类型和路径都匹配的情况下,将context.filterconfig放到过滤器链里面
for (int i = 0; i < filtermaps.length; i ) {
if (!matchdispatcher(filtermaps[i] ,dispatcher)) {
continue;
}
if (!matchfilters)
continue;
applicationfilterconfig filterconfig = (applicationfilterconfig)
context.findfilterconfig(filtermaps[i].getfiltername());
if (filterconfig == null) {
// fixme - log configuration problem
continue;
}
filterchain.addfilter(filterconfig);
} // add filters that match on servlet name second
// 类型和servlet名称都匹配的情况下,将context.filterconfig放到过滤器链里面
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) {
// fixme - log configuration problem
continue;
}
filterchain.addfilter(filterconfig);
} // return the completed filter chain
return filterchain;
}
关键点3 - 调用过滤器链的dofilter
applicationfilterchain类的dofilter函数代码如下,它会将处理委托给internaldofilter函数。
@override
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(
new java.security.privilegedexceptionaction() {
@override
public void run()
throws servletexception, ioexception {
internaldofilter(req,res);
return null;
}
}
);
} catch( privilegedactionexception pe) {
exception e = pe.getexception();
if (e instanceof servletexception)
throw (servletexception) e;
else if (e instanceof ioexception)
throw (ioexception) e;
else if (e instanceof runtimeexception)
throw (runtimeexception) e;
else
throw new servletexception(e.getmessage(), e);
}
} else {
internaldofilter(request,response);
}
}
applicationfilterchain类的internaldofilter函数代码如下:
// 1. `internaldofilter`方法通过pos和n来调用过滤器链里面的每个过滤器。pos表示当前的过滤器下标,n表示总的过滤器数量
// 2. `internaldofilter`方法最终会调用servlet.service()方法
private void internaldofilter(servletrequest request,
servletresponse response)
throws ioexception, servletexception { // call the next filter if there is one
// 1. 当pos小于n时, 则执行filter
if (pos < n) {
// 2. 得到 过滤器 filter,执行一次post
applicationfilterconfig filterconfig = filters[pos ];
try {
filter filter = filterconfig.getfilter(); if (request.isasyncsupported() && "false".equalsignorecase(
filterconfig.getfilterdef().getasyncsupported())) {
request.setattribute(globals.async_supported_attr, boolean.false);
}
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 {
// 4. 这里的 filter 的执行 有点递归的感觉, 通过 pos 来控制从 filterchain 里面拿出那个 filter 来进行操作
// 这里把this(filterchain)传到自定义filter里面,我们自定义的filter,会重写dofilter,在这里会被调用,dofilter里面会执行业务逻辑,如果执行业务逻辑成功,则会调用 filterchain.dofilter(servletrequest, servletresponse); ,filterchain就是这里传过去的this;如果业务逻辑执行失败,则return,filterchain终止,后面的servlet.service(request, response)也不会执行了
// 所以在 filter 里面所调用 return, 则会终止 filter 的调用, 而下面的 servlet.service 更本就没有调用到
filter.dofilter(request, response, this);
}
} catch (ioexception | servletexception | runtimeexception e) {
throw e;
} catch (throwable e) {
e = exceptionutils.unwrapinvocationtargetexception(e);
exceptionutils.handlethrowable(e);
throw new servletexception(sm.getstring("filterchain.filter"), e);
}
return;
} // we fell off the end of the chain -- call the servlet instance
try {
if (applicationdispatcher.wrap_same_object) {
lastservicedrequest.set(request);
lastservicedresponse.set(response);
} if (request.isasyncsupported() && !servletsupportsasync) {
request.setattribute(globals.async_supported_attr,
boolean.false);
}
// use potentially wrapped request from this point
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 {
//当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。
servlet.service(request, response);
}
} catch (ioexception | servletexception | runtimeexception e) {
throw e;
} catch (throwable e) {
e = exceptionutils.unwrapinvocationtargetexception(e);
exceptionutils.handlethrowable(e);
throw new servletexception(sm.getstring("filterchain.servlet"), e);
} finally {
if (applicationdispatcher.wrap_same_object) {
lastservicedrequest.set(null);
lastservicedresponse.set(null);
}
}
}
自定义filter
@webfilter(urlpatterns = "/*", filtername = "myfilter")
public class filetercontroller implements filter { @override
public void init(filterconfig filterconfig) throws servletexception {
system.out.println("filter初始化中");
} @override
public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception { system.out.println("登录逻辑");
if("登录失败"){
response.getwriter().write("登录失败");
//后面的拦截器和servlet都不会执行了
return;
}
//登录成功,执行下一个过滤器
filterchain.dofilter(servletrequest, servletresponse);
} @override
public void destroy() {
system.out.println("filter销毁中");
}
}
- pos和n是applicationfilterchain的成员变量,分别表示过滤器链的当前位置和过滤器总数,所以当pos小于n时,会不断执行applicationfilterchain的dofilter方法;
- 当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。
总结
以上是凯发ag旗舰厅登录网址下载为你收集整理的tomcat源码分析 (九)----- http请求处理过程(二)的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得凯发ag旗舰厅登录网址下载网站内容还不错,欢迎将凯发ag旗舰厅登录网址下载推荐给好友。
- 上一篇:
- 下一篇: