StandardWrapperValve的invoke就三步:
-
创建Servlet实例
-
给当前请求创建一个Filter链
-
调用Filter链
[](()为何要给每个请求创建Filter链
每个请求的请求路径不同,而Filter都有相应路径映射,因此不是所有Filter都需要处理当前请求,要根据请求路径选择特定的一些Filter。
[](()为何没调用Servlet#service
Filter链的最后一个Filter会负责调用Servlet。
[](()Filter管理
=======================================================================
跟Servlet一样,Filter也可在web.xml
配置。
但Filter的作用域是整个Web应用,因此Filter的实例维护在Context容器:Map里存的是filterDef(filter定义),而非filter类实例
Filter链存活期很短,它跟每个请求对应。一个新请求来了,就动态创建一个Filter链,请求处理完,Filter链就被回收。
public final class ApplicationFilterChain implements FilterChain {
// Filter链的Filter数组
private Appl 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 icationFilterConfig[] filters = new ApplicationFilterConfig[0];
// Filter链的当前的调用位置
private int pos = 0;
// Filter总数
private int n = 0;
// 每个Filter链最终要调用的Servlet
private Servlet servlet = null;
public void doFilter(ServletRequest req, ServletResponse res) {
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest req,
ServletResponse res){
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
return;
}
servlet.service(request, response);
}
internalDoFilter里会做个判断:
-
若当前Filter位置 < Filter数组长度,即Filter还没调完,就从Filter数组取下一个Filter,调用其doFilter
-
否则,说明已调用完所有Filter,该调用Servlet#service了。service方法是留给程序员实现业务逻辑的,比如CRUD
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain){
…
//调用Filter的方法
chain.doFilter(request, response);
}
Filter#doFilter的FilterChain参数,就是Filter链。每个Filter#doFilter里必须调用Filter链的doFilter,而Filter链中保存当前Filter位置,会调用下一个Filter的doFilter方法,这样就能完成链式调用。
[](()对应的filter是怎么注册到Servlet的呢?
filter是注册到Servlet容器中,Tomcat的StandardContext类中维护了一个Filter列表,所谓注册就是把你写的filter类实例加到这个列表。
[](()Listener管理
=========================================================================
Listener可以监听容器内部发生的事件:
- 生命状态的变化
比如Context容器启动和停止、Session的创建和销毁。
- 属性变化
比如Context容器某个属性值变了、Session的某个属性值变了以及新的请求来了
[](()怎么添加监听器
在web.xml配置或注解添加,在监听器里实现业务逻辑。
Tomcat需读取配置文件,拿到监听器的类名,将它们实例化,并适时调用这些监听器方法。
Tomcat是通过Context容器来管理这些监听器的。Context容器将两类事件分开来管理,分别用不同的集合来存放不同类型事件的监听器:
//监听属性值变化的监听器
private List applicationEventListenersList = new CopyOnWriteArrayList<>();
//监听生命事件的监听器
private Object applicationLifecycleListenersObjects[] = new Object[0];
剩下的事情就是触发监听器了,比如在Context容器的启动方法里,就触发了所有的ServletContextListener:
// 1 拿到所有生命周期监听器
Object instances[] = getApplicationLifecycleListeners();
for (int i = 0; i < instances.length; i++) {
// 2 判断Listener的类型是否为ServletContextListener
if (!(instances[i] instanceof ServletContextListener))
continue;
// 3 触发Listener方法
ServletContextListener lr = (ServletContextListener) instances[i];
lr.contextInitialized(event);