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<Object> 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); }
这里的ServletContextListener接口是留给用户的扩展机制,用户可以实现该接口定义自己的监听器,监听Context容器的启停事件。
ServletContextListener跟Tomcat自己的生命周期事件LifecycleListener是不同的。LifecycleListener定义在生命周期管理组件中,由基类LifecycleBase统一管理。
可定制监听器监听Tomcat内部发生的各种事件:比如Web应用、Session级别或请求级别的。Tomcat中的Context容器统一维护了这些监听器,并负责触发。
Context组件通过自定义类加载器来加载Web应用,并实现了Servlet规范,直接跟Web应用打交道。
FAQ
Context容器分别用了CopyOnWriteArrayList和对象数组来存储两种不同的监听器,为什么要这样设计呢?
因为:
- 属性值变化listener能动态配置,所以用CopyOnWriteArray
写不会那么频繁,读取比较频繁 - 生命周期事件listener,不能动态改变,无线程安全问题
生命周期相关的类比如session一个用户分配一个,用完了就会销毁,用对象数组,可以适应增删改操作