前言
filter是什么?filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用 它是位于前端请求到servlet之间的一系列过滤器,也可以称之为中间件,它主要是对请求到达servlet之前做一些额外的动作:
- 1、权限控制
- 2、监控
- 3、日志管理
- 4、等等
这里涉及到两个接口:filter和filterchain
filter和filterchain密不可分, filter可以实现依次调用正是因为有了filterchain。
1、filter接口
public interface filter {
// 容器创建的时候调用, 即启动tomcat的时候调用
public void init(filterconfig filterconfig) throws servletexception;
// 由filterchain调用, 并且传入filterchain本身, 最后回调filterchain的dofilter()方法
public void dofilter(servletrequest request, servletresponse response,
filterchain chain) throws ioexception, servletexception;
// 容器销毁的时候调用, 即关闭tomcat的时候调用
public void destroy();
}2、filterchain接口
public interface filterchain {
// 由filter.dofilter()中的chain.dofilter调用
public void dofilter(servletrequest request, servletresponse response)
throws ioexception, servletexception;
}执行流程
在前面的文章中,我们知道,tomcat启动会执行standardwrappervalve.java类的invoke方法:
public final void invoke(request request, response response){
......
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
applicationfilterchain filterchain =
applicationfilterfactory.createfilterchain(request, wrapper, servlet);
// call the filter chain for this request
// note: this also calls the servlet's service() method
container container = this.container;
try {
if ((servlet != null) && (filterchain != null)) {
// swallow output if needed
if (context.getswallowoutput()) {
try {
systemloghandler.startcapture();
if (request.isasyncdispatching()) {
request.getasynccontextinternal().dointernaldispatch();
} 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 {
filterchain.dofilter
(request.getrequest(), response.getresponse());
}
}
}
} catch (clientabortexception | closenowexception e) {
}
......
}上面的代码做了如下一些动作:
- 1、每次请求过来都会创建一个过滤器链(filterchain),并把待执行的servlet对象存放到过滤器链中。对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter。
- 2、创建了filterchain之后,就开始执行dofilter进行请求的链式处理。
1、创建filterchain
下面我们具体来看看filterchain是怎么创建的
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
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();
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
for (filtermap filtermap : filtermaps) {
if (!matchdispatcher(filtermap, dispatcher)) {
continue;
}
if (!matchfiltersurl(filtermap, requestpath))
continue;
applicationfilterconfig filterconfig = (applicationfilterconfig)
context.findfilterconfig(filtermap.getfiltername());
if (filterconfig == null) {
// fixme - log configuration problem
continue;
}
filterchain.addfilter(filterconfig);
}
// add filters that match on servlet name second
for (filtermap filtermap : filtermaps) {
if (!matchdispatcher(filtermap, dispatcher)) {
continue;
}
if (!matchfiltersservlet(filtermap, servletname))
continue;
applicationfilterconfig filterconfig = (applicationfilterconfig)
context.findfilterconfig(filtermap.getfiltername());
if (filterconfig == null) {
// fixme - log configuration problem
continue;
}
filterchain.addfilter(filterconfig);
}
// return the completed filter chain
return filterchain;
}上面的代码做了一下几件事:
- 1、把要执行的servlet存放到过滤器链中。
- 2、如果没有配置过滤器则return一个空的过滤器链(只包含上面设置的servlet)。
- 3、如果配置url-pattern过滤器,则把匹配的过滤器加入到过滤器链中
- 4、如果配置servlet-name过滤器,则把匹配的过滤器加入到过滤器链中
注意: filterchain.addfilter()顺序与web.xml中定义的filter顺序一致,所以过滤器的执行顺序是按定义的上下顺序决定的。
2、执行dofilter
创建了chain之后,就开始执行链式请求了,具体的逻辑如下:
private void internaldofilter(servletrequest request,
servletresponse response)
throws ioexception, servletexception {
// call the next filter if there is one
if (pos < n) {
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 {
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 {
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);
}
}
}上面的代码逻辑如下:
- 1、通过position索引判断是否执行完了所有的filter
- 2、如果没有,取出当前待执行的索引filter,调用其dofilter方法,在上面的接口说明中,我们看到,所有的filter类都继承了filter接口,都实现了dofilter方法;我们也注意到,该方法接收一个filterchain对象。在这段代码中,
filter.dofilter(request, response, this);可以看到,将自身引用传递进去了,那么各个filter在dofilter的方法中,可以根据自身业务需要,来判断是否需要继续进行下面的filter链式执行,如果需要,就执行filterchain.dofilter方法,此时就又回到了此代码中。如果反复 - 3、如果执行完了所有的filter,则开始执行servlet业务模块
servlet.service(request, response);
以上就是详解tomcat中filter是怎样执行的的详细内容,更多关于tomcat filter执行的资料请关注代码网其它相关文章!
发表评论