Servlet

2019/01/03

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并未关闭响应流,直到异步处理完成。

异步应用

  1. 开启asyncSupported
  2. Servlet的service方法中,获取异步上下文 AsyncContext ac = request.startAsync();
  3. 创建一个线程(或使用线程池)thread,将ac作为参数传入
  4. 开启异步上下文执行 ac.start(thread)任务
  5. 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时传递的。

  1. 原始请求,封装成 apache Request/Response 对象
  2. 交给用户线程处理,封装成 catalina Request/Response 对象
  3. 最终传给Servlet,封装成 RequestFacade/ResponseFacade 对象,门面模式。

ServletContext

实际是一个ApplicationContextFacade对象,同样保证ServletContex只能从容器中拿到它该拿的数据。它们都起到对数据的封装作用,它们使用的都是门面设计模式。

Servlet如何工作的

Servlet单进程多线程,每个请求分配一个线程。

请求到达时如何确定要进入哪个容器

mapper类记录了所有容器(父、子容器)的映射关系,请求达到时,去mapper查找,确定进入哪个engine容器,进入哪个host容器,进入哪个context容器,进入哪个wrapper容器。

所有容器都实现了监听器,只要任何容器发送变化时,mapper都会给通知。

Post Directory