Servlet
Servlet各版本特性
Servlet 2.x
- 支持Filter
- 新增对Context的Listener监听
Servlet 3.x
- 异步处理,开启asyncSupported异步支持
- 支持注解配置Servlet、Filter、Listener,无需web.xml中配置
- Web模块,web-fragment.xml
并作为Java EE 6规范 一起发布。
异步是如何实现的
Servlet 3.x 中的异步处理是从Tomcat中的Connector组件开始的。
Servlet会开启一个单独的线程来执行业务,当前Servlet线程会立即返回给Servlet容器以便响应其他的请求。
异步结果是如何主动推送到客户端的?
AsyncContext是如何将结果推送给客户端的?
其实Tomcat并未关闭响应流,直到异步处理完成。
异步应用
- 开启asyncSupported
- Servlet的service方法中,获取异步上下文
AsyncContext ac = request.startAsync();
- 创建一个线程(或使用线程池)
thread
,将ac作为参数传入 - 开启异步上下文执行
ac.start(thread)
任务 - Servlet的service方法直接结束,返回客户端。
Servlet 4.x
- 支持HTTP/2
- 服务器推送,PushBuilder
Servlet Container
一个Web工程对应一个Context容器,管理Servlet容器的是Context容器。
Servlet容器的启动过程
Servlet容器的启动就是Context容器的启动。
Tomcat 7.x 开始支持嵌入式功能,新增了一个
org.apache.catalina.startup.Tomcat
启动类。创建一个实例对象并调用start
方法就可以启动一个Tomcat。
public Context addWebapp(Host host, String url, String path) {
silence(url);
Context ctx = new StandardContext();
ctx.setPath( url );
ctx.setDocBase(path);
if (defaultRealm == null) {
initSimpleAuth();
}
ctx.setRealm(defaultRealm);
ctx.addLifecycleListener(new DefaultWebXmlListener());
ContextConfig ctxCfg = new ContextConfig();
ctx.addLifecycleListener(ctxCfg);
ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
if (host == null) {
getHost().addChild(ctx);
} else {
host.addChild(ctx);
}
return ctx;
}
一个Web应用对应一个Context容器,所以添加一个Web应用将会创建一个StandardContext容器。
Context容器启动,会加载解析context.xml配置文件,创建ClassLoader对象,获取ServletContext,初始化Servlet。
Web应用的初始化工作
解析Servlet
加载解析web.xml文件,将xml中对应的listener、filter、servlet属性设置到Context容器中。
Context容器里的Servlet对象被包装成StandardWrapper。
创建Servlet实例
创建Servlet实例
如果Servlet的load-on-startup配置项大于 0,那么在Context容器启动的时候就会被实例化,前面提到在解析配置文件时会读取默认的 globalWebXml,在 conf 下的 web.xml 文件中定义了一些默认的配置项,其定义了两个 Servlet,分别是:org.apache.catalina.servlets.DefaultServlet 和 org.apache.jasper.servlet.JspServlet 它们的 load-on-startup 分别是 1 和 3,也就是当 Tomcat 启动时这两个 Servlet 就会被启动。
Wrapper.loadServlet
创建 Servlet 实例的方法是从Wrapper.loadServlet开始的。loadServlet 方法要完成的就是获取servletClass然后把它交给InstanceManager去创建一个基于servletClass.class的对象。
初始化Servlet
Wrapper.initServlet
初始化 Servlet 在 StandardWrapper 的 initServlet 方法中,这个方法很简单就是调用 Servlet 的 init 的方法
Servlet体系结构
Java Web 应用是基于Servlet规范运转,Servlet运转主要关联三个类:
- ServletConfig
- ServletRequest
- ServletResponse
这三个类都是通过容器(Servlet Container创建的)传给Servlet的。
ServletConfig在Servlet初始化时传递的
实际是一个StandardWrapperFacade对象,实现了ServletConfig接口,是StandardWrapper的门面类,隐藏一些不必要的数据给Servlet。
ServletRequest和ServletResponse是在请求到达调用Servlet时传递的。
- 原始请求,封装成 apache Request/Response 对象
- 交给用户线程处理,封装成 catalina Request/Response 对象
- 最终传给Servlet,封装成 RequestFacade/ResponseFacade 对象,门面模式。
ServletContext
实际是一个ApplicationContextFacade对象,同样保证ServletContex只能从容器中拿到它该拿的数据。它们都起到对数据的封装作用,它们使用的都是门面设计模式。
Servlet如何工作的
Servlet单进程多线程,每个请求分配一个线程。
请求到达时如何确定要进入哪个容器
mapper类记录了所有容器(父、子容器)的映射关系,请求达到时,去mapper查找,确定进入哪个engine容器,进入哪个host容器,进入哪个context容器,进入哪个wrapper容器。
所有容器都实现了监听器,只要任何容器发送变化时,mapper都会给通知。