Servlet回顾总结 项目地址 http://git.oschina.net/68gkb/servlet-jsppro
1,包的问题:
maven搭建WEB项目时都需要依赖servlet-api、jsp-api、jstl三个基础包。(servlet-api与javax.servlet-api都是servlet-api包,版本不同3.0以下叫servlet-api,3.0以上叫javax.servlet-api。jsp-api,jstl同理,还有别的厂商的包,Tomcat,Jboss.)
2,Servlet包说明:
Servlet的框架是由两个Java包组成:javax.servlet和javax.servlet.http. 在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类.
在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类.
3,Servlet生命周期:
我们写Servlet项目是Controller都需要继承HttpServlet类,而HttpServlet中三个方法init(),service(),destory()是Servlet生命周期的三个重要方法;
初始化阶段(调用一次) 调用init()方法
响应客户请求阶段(每次请求都会调用) 调用service()方法
终止阶段(调用一次) 调用destroy()方法
3.1,Servlet何时被创建:
1,默认情况下,当WEB客户第一次请求访问某个Servlet的时候,WEB容器将创建这个Servlet的实例。
2,当web.xml文件中如果<servlet>元素中指定了<load-on-startup>子元素时,Servlet容器在启动web服务器时,将按照顺序创建并初始化Servlet对象
3.2,Servlet响应请求阶段:
对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的HttpServletRequest对象和HttpServletResponse对象,然后调用Servlet的service方法。
service方法从ServletRequest对象获得客户请求信息,处理该请求,并通过ServletResponse对象向客户返回响应信息.
(注意:1,HttpServlet类的service方法两个参数HttpServletRequest,HttpServletResponse是接口,而调用改方法时,传入的对象是什么类型的,如何实例化传入对象的这个得分析应用服务器源码(Tomcat).2,service方法就是根据方法类型(GET,POST,PUT...)调用不同处理方法)
3.3,Servlet终止阶段:
当WEB应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet新实例时,Servlet容器会先调用Servlet的destroy()方法,
在destroy()方法中可以释放掉Servlet所占用的资源。
3.4,Servlet单例:
Servlet本身是单实例的,这样当多个用户同时访问某个Servlet时,会访问该唯一的Servlet实例中的成员变量,
如果对成员变量进行写入工作,那就会导致Servlet的多线程问题,即数据不一致。
解决Servlet多线程同步问题的方案:
1.变量的线程安全; 多线程并不共享局部变量,所以我们要尽可能的在Servlet中使用局部变量;
2.代码块的线程安全; 使用同步块Synchronized,防止可能调用的代码块;但是要注意的是,要尽可能得缩小同步代码的方范围,不要在service方法和响应方法上直接使用同步,这会严重影响性能。
3.属性的线程安全; ServletContext,HttpSession,ServletRequest对象中属性;
4.使用同步集合; 使用Vector代替ArrayList,使用HashTable代替HashMap;
5.不要在Servlet中创建自己的线程来完成某个功能; Servlet本身就是多线程的,如果再创建新的线程,将会导致线程执行复杂化,出现线程安全问题;
6.在多个Servlet中,对外部对象,比如:文件;进行修改操作一定要加锁,做到互斥访问;
网上找到的servlet流程图:
4,重要的三个接口Servlet,ServletConfig,ServletContext
ServletContext对象:servlet容器在启动时会加载web应用,并为每个web应用创建唯一的servlet context对象,可以把ServletContext看成是一个Web应用的服务器端组件的共享内存,在ServletContext中可以存放共享数据。ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。
servletConfig对象:用于封装servlet的配置信息。从一个servlet被实例化后,对任何客户端在任何时候访问有效,但仅对servlet自身有效,一个servlet的ServletConfig对象不能被另一个servlet访问。
我们创建一个Servlet时会继承HttpServlet类,而HttpServlet类是继承GenericServlet类,而GenericServlet类实现了Servlet接口和ServletConfig接口。
从类的结构图和继承关系可以看出。我们创建的Servlet类具有Servlet的生命周期相关的方法,和ServletConfig的接口方法(得到servletName,初始化变量,和全局的ServletContext对象)
相关代码:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>webcore</display-name>
<!--ServletContext 全局 所有Servlet共享-->
<context-param>
<param-name>publicVal</param-name>
<param-value>publicVal</param-value>
</context-param>
<servlet>
<servlet-name>LoginController</servlet-name>
<servlet-class>com.lyyz.controller.LoginController</servlet-class>
<!--ServletConfig 私有-->
<init-param>
<param-name>privateVal</param-name>
<param-value>privateVal</param-value>
</init-param>
<!--servlet 初始化一般是访问时初始化的 如果配置load-on-startup 标签 则是容器启动时初始化servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoginController</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
</web-app>
package com.lyyz.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by lyyz on 2017/8/21.
*/
public class LoginController extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
System.out.println("LoginController init method");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
System.out.println("LoginController service method");
System.out.println("ServletConfig "+this.getInitParameter("privateVal"));
System.out.println("ServletContext "+this.getServletContext().getInitParameter("publicVal"));
//输出信息
// LoginController service method
// ServletConfig privateVal
// ServletContext publicVal
}
@Override
public void destroy() {
super.destroy();
System.out.println("LoginController destroy method");
}
}
4.1,ServletContext类:
ServletContext从上述中可知对象是全局公用的,从Servlet3.0开始支持注解(@Servlet,@Filter...)功能。而实现注解功能主要就是ServletContext接口中添加类addServlet,addFilter等等的方法。从servlet-api2,5和3.0两个包中的ServletContext接口的方法对比可以看出。
5,重定向和请求分派
5.1,重定向
HttpServletResponse接口提供的sendRedirect()方法用于生产302响应码和Location响应头,从而通知客户端去重新访问Location响应头中指定的URL。
5.2,请求分派
Servlet API中定义了一个RequestDispatcher接口,俗称请求分派器,有两个方法:forward()方法和include()方法。
forward()方法用于将请求转发到RequestDispatcher实例封装的资源;
include()方法用于将RequestDispatcher实例封装的资源作为当前响应内容的一部分包含进来。
5.3,重定向和请求分派的比较
虽然HttpServletResponse的sendRedirect()方法和RequestDispatcher的forward()方法都可以让浏览器获得另一个URL所指向的资源所作出的响应,但两者的内部运行机制有着很大的区别。
1)请求分派只能将请求转发给同一个Web应用中的其他组件;而重定向不仅可以定向到当前应用程序中的其他资源,也可以重定向到其他站点的资源上。
2)重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;而请求转发过程结束后,浏览器地址保持初始的URL地址不变。
3)请求分派的发起者和被调用者之间共享相同的request实例和response实例,它们属于同一个“请求/响应”过程;而重定向的发起者和被调用者使用各自的request实例和response,它们各自属于独立的“请求/响应”过程。
forward方法是把请求的内容转发到另外的一个servlet.而include是把另一个servlet处理过后的内容拿过来.
6,Servlet过滤器
Filter可以改变一个request和修改一个response。Filter不是一个Servlet,它不能产生一个response,它能够在一个request到达Servlet之前预处理request,也可以在离开Servlet时处理response。
Filter的使用案例:
1.统一全站的编码
2.禁止浏览器缓存动态页面
3.实现用户的自动登录
7,Servlet监听器
监听器可以使你的应用对某些事件作出反应。
· ServletContextListener:应用上下文生命周期监听器。用于监听Web应用的启动和销毁事件。
· ServletContextAttributeListener:应用上下文属性事件监听器。用于监听Web应用上下文中的属性改变的事件。
· ServletRequestListener:请求生命周期监听器。用于监听请求的创建和销毁事件。
· ServletRequestAttributeListener:请求属性事件监听器。用于监听请求中的属性改变的事件。
· HttpSessionListener:会话生命周期监听器。用于监听会话的创建和销毁事件。
· HttpSessionActivationListener:会话激活和钝化事件监听器。用于监听会话的激活和钝化的事件。
· HttpSessionAttributeListener:会话属性事件监听器。用于监听会话中的属性改变的事件。
· HttpSessionBindingListener:会话值绑定事件监听器。这是唯一不需要在web.xml中设定的Listener。
web.xml加载顺序 context-param > Listener > Filter > Servlet
8,Servlet3.0的新特性
8.1、新增标注支持
在Servlet3.0的部署描述文件web.xml的顶层标签<web-app>中有一个metadata-complete属性,如果把该属性的值设置为true,则容器在部署时只依赖于web.xml部署文件中的配置,
会忽略所以的标注(同时也会跳过web-fragment.xml的扫描,即禁用可插性支持);如果把该属性的值设置为false或者不配置该属性,则表示启用标注支持和可插性支持。
8.2、异步处理支持
Servlet3.0支持异步处理支持,Servlet接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时Servlet还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有ServletRequest和ServletResponse对象的引用),或者将请求继续转发给其他Servlet。
1)对于使用传统的部署描述文件web.xml配置Servlet和过滤器的情况,Servlet3.0为<servlet>和<filter>标签增加了<async-supported>子标签,该标签的默认取值为false,要启用异步处理支持,则将其设为true即可。
8.3、可插性支持
Servlet3.0新增的可插性(Pluggability)支持则将Servlet配置的灵活性提升到了新的高度。使用该特性,现在我们可以在不修改已有Web应用的前提下,
只需将按照一定格式打包成的JAR包放到WEB-INF/lib目录下,即可实现新的功能的扩充,不需要额外的配置。Servlet3.0引入了称为“Web模块部署描述文件片段”的web-fragment.xml来实现可插性的。web-fragment.xml部署描述文件可以定义一切可以在web.xml中定义的内容。