1、WEB服务器和Servlet容器
servlet容器的主要任务是管理servlet的生命周期。而你说的web容器更准确的说应该叫web服务器,它是来管理和部署web应用的。还有一种服务器叫做应用服务器,它的功能比web服务器要强大的多,因为它可以部署EJB应用,可以实现容器管理的事务,一般的应用服务器有weblogic和websphere等,它们都是商业服务器,功能强大但都是收费的。web容器最典型的就是tomcat了,Tomcat是web容器也是servlet容器。
web容器好比 电视机
servlet容器好比 VCD
没有VCD你可以看电视,对吧,但是有了VCD没有电视机,你从哪看起?
没有servlet容器,你也可以用web容器直接访问静态页面,比如安装一个apache等,但是如果要显示jsp/servlet,你就要安装一个servlet容器了,但是光有servlet容器是不够的,因为它要被解析成html输出,所以你仍需要一个web容器。
大多数servlet容器同时提供了web容器的功能,也就是说大多servlet容器可以独立运行你的web应用。
Apache HTTP服务器就是一个纯粹的Web服务器,要在其上运行servlet ,还需要一个servlet容器。
而Tomcat既是Web服务器,也是Servlet容器。
Apache和Tomcat整合使用:如果客户端请求的是静态页面,则只需要Apache服务器响应请求;如果客户端请求动态页面,则是Tomcat服务器响应请求;因为jsp是服务器端解释代码的,这样整合就可以减少Tomcat的服务开销。
2、Servlet接口
Servlet接口是Java Servlet API的核心抽象。所有Servlet类必须直接或间接的实现该接口,或者更通常做法是通过继承一个实现了该接口的类从而复用许多共性功能。目前有GenericServlet和HttpServlet这两个类实现了Servlet接口。大多数情况下,开发者只需要继承HttpServlet去实现自己的Servlet即可。HttpServlet继承了GenericServlet。
3、Servlet线程安全问题
Web应用程序的并发请求处理通常需要Web开发人员去设计适合多线程执行的Servlet,从而保证service方法能在一个特定时间点处理多线程并发执行。(注:即Servlet默认是线程不安全的,需要开发人员处理多线程问题)
通常Web容器对于并发请求将使用同一个servlet处理,并且在不同的线程中并发执行service方法。
4、Servlet实例数量
对于未托管在分布式环境中(默认)的servlet而言,servlet容器对于每一个Servlet声明必须且只能产生一个实例。不过,如果Servlet实现了SingleThreadModel接口,servlet容器可以选择实例化多个实例以便处理高负荷请求或者串行化请求到一个特定实例。
如果servlet以分布式方式进行部署,容器可以为每个虚拟机(JVM)的每个Servlet声明产生一个实例。但是,如果在分布式环境中servlet实现了SingleThreadModel接口,此时容器可以为每个容器的JVM实例化多个Servlet实例。
注:
SingleThreadModel已经被弃用
/** * @deprecated Interface SingleThreadModel is deprecated */
publicinterface SingleThreadModel { }
|
请不要实现 SingleThreadModel 接口。这种实践将导致 Web 容器创建多个 servlet 实例;即为每个用户创建一个实例。对于任何大小的应用程序,这种实践都将导致严重的性能问题。
5、Servlet3.0以上新特-异步Servlet
http://www.infoq.com/cn/news/2013/11/use-asynchronous-servlet-improve
Servlet 3.0标准已经发布了很长一段时间,相较于之前的2.5版的标准,新标准增加了很多特性,比如说以注解形式配置Servlet、web.xml片段、异步处理支持、文件上传支持等。虽然说现在的很多Java Web项目并不会直接使用Servlet进行开发,而是通过如Spring MVC、Struts2等框架来实现,不过这些Java Web框架本质上还是基于传统的JSP与Servlet进行设计的,因此Servlet依然是最基础、最重要的标准和组件。在Servlet 3.0标准新增的诸多特性中,异步处理支持是令开发者最为关注的一个特性,本文就将详细对比传统的Servlet与异步Servlet在开发上、使用上、以及最终实现上的差别,分析异步Servlet为何会提升Java Web应用的性能。
6、Servlet非阻塞I/O
Servlet 3.0 虽然支持异步请求处理,但却只允许使用传统 I/O,这会限制应用程序的可扩展性。在普通的应用程序中,ServletInputStream 是在 while 循环中进行读取:
|
Servlet 3.1 加入了非阻塞 I/O 并且引入了两个新的接口 ReadListener 和 WriteListener。这些接口有回调方法,当数据准备好的时候会被调用。 例如,在 Servlet 的 doGet 方法中可以编写下面的代码:
AsyncContext context = request.startAsync(); ServletInputStream input = request.getInputStream(); input.setReadListener(new MyReadListener(input, context)); |
7、HttpOnly Cookie
HttpOnly cookie暗示客户端它们不会暴露给客户端脚本代码(它没有被过滤掉,除非客户端知道如何查找此属性)。使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击。不过,能力有限,它实际是浏览器通过协议实现限制的。
8、Requst对象的生命周期
每个request对象只在servlet的service方法的作用域内,或过滤器的doFilter方法的作用域内有效,除非该组件启用了异步处理并且调用了request对象的startAsync方法。在发生异步处理的情况下,request对象一直有效,直到调用AsyncContext的complete方法。容器通常会重复利用request对象,以避免创建request对象的性能开销。开发人员必须注意的是,不建议在上述范围之外保持startAsync方法还没有被调用的请求对象的引用,因为这样可能产生不确定的结果。
9、ServletContext的生命周期
每一个部署到容器的Web应用都有一个Servlet接口的实例与之关联。在容器分布在多台虚拟机的情况下,每个JVM的每个Web应用将有一个ServletContext实例。
如果容器内的Servlet没有部署到Web应用中,则隐含的作为“默认”Web应用的一部分,并有一个默认的ServletContext。在分布式的容器中,默认的ServletContext是非分布式的且仅存在于一个JVM中。
10、过滤器的生命周期
在部署描述符中声明的每个<filter>在每个JVM的容器中仅实例化一个实例。容器提供了声明在过滤器的部署描述符的过滤器config(译者注:FilterConfig),对Web应用的ServletContext的引用,和一组初始化参数。
过滤器的核心概念是包装请求或响应,以便它可以覆盖行为执行过滤任务。
11、RequestDispatcher
RequestDispatcher接口中定义了二种方法用于请求转发:
forward(ServletRequest,ServletResponse)方法:
将请求转发给服务器上另外一个Servlet,JSP页面,或者HTML文件 这个方法必须在响应被提交给客户端之前调用,否则抛出异常。
方法调用后在响应中的没有提交的内容被自动消除。
include(ServletRequest,ServletResponse)方法 :
用于在响应中包含其他资源(Servlet,JSP页面或HTML文件)的内容。
即请求转发后,原先的Servlet还可以继续输出响应信息,转发到的Servlet对请求做出的响应将并入原先Servlet的响应对象中。
forward方法和include方法的区别:
forward方法调用后在响应中的没有提交的内容被自动消除。 include方法使原先的Servlet和转发到的Servlet都可以输出响应信息。
12、HttpSession
HttpSession session = request.getSession(boolean flag);当flag = true时,服务器会先查看请求中是否包含sessionId,如果有,则依据sessionId去查找对应的session对象,如果找到返回该对象。如果找不到,则会创建一个新的session对象。当flag = false时,服务器会先查看请求中是否包含sessionId,如果没有,返回null。如果有返回sessionId对应的session对象。
一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。 由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止(非持久session)
13、Web容器的类加载器
Servlet容器是一个Java EE产品的一部分,不应该允许应用程序重写Java SE或Java EE平台的类,比如那些在Java.*和javax.*命名空间中的类,Java SE或Java EE不允许被修改。
容器用于加载WAR文件中servlet的类加载器必须允许开发人员使用getResource加载遵循正常JavaSE语义的WAR文件的JAR包中包含的任何资源。和Java EE许可协议中描述的一样,不属于Java EE产品的servlet容器不应该允许应用程序覆盖Java SE平台中的类,如在java.*和javax.*命名空间中的类,Java SE不允许进行修改。容器不应该允许应用程序覆盖或访问容器的实现类。同时建议应用程序类加载器实现成WAR文件中的类和资源优先于属于容器范围内的JAR包中的类和资源加载。一个类加载器的实现必须保证对部署到容器的每个web应用,调用Thread.currentThread.getContextClassLoader()返回一个实现了本节规定的约定的ClassLoader实例。此外,部署的每个Web应用程序的ClassLoader实例必须是一个单独的实例。容器必须在任何回调(包括侦听器回调)到Web应用程序之前设置上面描述的线程上下文ClassLoader,一旦回调返回,需要把它设置成原来的ClassLoader。
注:容器类加载器是各个应用的类加载器的父类加载器,因此应用可以调用容器加载的类,符合双亲委派机制;但是,在容器回调应用的类的时候,如果按照双亲委派机制来看,容器是没法调用到应用的类的,因此有了线程上下文加载器,它来解决这种违背双亲委派机制的情况。
14、Web应用程序部署
当一个Web应用程序部署到容器中,在Web应用程序开始处理客户端请求之前,必须按照下述步骤顺序执行。
■ 实例化部署描述文件中<listener>元素标识的每个事件监听器的一个实例。
■ 对于已实例化的实现了ServletContextListener接口的监听器实例,调用contextInitialized()方法。
■ 实例化部署描述文件中<filter>元素标识的每个过滤器的一个实例,并调用每个过滤器实例的init()方法。
■ 包含<load-on-startup>元素的<servlet>元素,根据load-on-startup元素值定义的顺序为每个servlet实例化一个实例,并调用每个servlet实例的init()方法。
15、部署描述文件
如果Web应用不包含任何servlet、过滤器、或监听器组件或使用注解声明相同的,那么可以不需要web.xml文件。换句话说,只包含静态文件或JSP页面的应用程序并不需要一个web.xml的存在。
注:为什么jsp也可以不需要web.xml? 如果容器有一个内部的JSP容器,*.jsp扩展名映射到它,允许执行JSP页面的请求。该映射被称为隐式映射。如果Web应用定义了一个*.jsp映射,它的优先级高于隐式映射。 Servlet容器允许进行其他的隐式映射,只要显示映射的优先。例如,一个*.shtml隐式映射可以映射到包含在服务器上的功能。