一. Servlet简介
基于Java技术的Web组件,运行在服务器端,由Servlet容器管理,用于生成动态内容。
Servlet容器管理Servlet,负责Servlet和客户的通信以及调用Servlet的方法。
Servlet功能:
- 接收用户请求的http协议
- 返回http响应协议
Servlet优势:
- Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
- Servlet 是独立于平台的,因为它是用 Java 编写的。
- 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
- Java 类库的全部功能对 Servlet 来说都是可用的。它可通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
Servlet框架:
Servlet实现方式:
- 实现javax.servlet.Servlet接口 (对应三)
- 继承javax.servlet.GenericServlet类 (对应六)
- 继承javax.servlet.http.HttpServlet类(常用方式) (对应七)
Servlet核心jar包:
tomcat/lib目录下的servlet-api.jar
响应客户请求的过程:
二. 在web.xml中配置Servlet
在新建项目的WebContent\WEB-INF\下,有web.xml配置文件。如果没有,新建项目时,第三步需要勾选
在根标签<web-app></webapp>中配置Servlet的相关信息,如下:
<servlet>
<!-- 声明名字,可以任意 -->
<servlet-name>firstServlet</servlet-name>
<!-- 声明类名字 -->
<servlet-class>com.qibao.servlet.firstServlet</servlet-class>
</servlet>
<!-- 配置如何访问这个servlet -->
<servlet-mapping>
<!-- 必须和要和 某个声明的servlet名字一样 -->
<servlet-name>oneServlet</servlet-name>
<!-- 声明访问的url路径 -->
<url-pattern>/first</url-pattern>
</servlet-mapping>
- Servlet程序必须通过Servlet容器来启动运行,并且储存目录有特殊要求,需要存储在<WEB应用程序目录>\WEB-INF\classes\目录中。
- Servlet程序必须在WEB应用程序的web.xml文件中进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。
- 一个<servlet>元素用于注册一个Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
- 一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。
- 一个Servlet可映射多个URL,即多个<servlet-mapping>元素的<servlet-name>设置的值可以是同一个Servlet的注册名。
- Servlet映射中可使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”;另一种格式是以正斜杠/开头并以“/*”结尾,例如/action/*。/代表当前web应用的路径,即http://localhost:8080/webapp/。
- * 两边不能同时出现数据 /action/*do
<load-on-startup>1</load-on-startup>
在<servlet></servlet>标签中添加,容器加载web应用时,就创建此servlet实例,并调用init()方法.需要一个大于0的整数.值越小优先级越高.
不加这个选项只有当页面第一次发送请求时才去创建servlet,调用init().
通过注解的方式注册Servlet:
@WebServlet("/firstServlet")
- @WebServlet 用于将一个类声明为 Servlet
- @WebInitParam该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数
属性名 | 类型 | 描述 |
name | String | 指定 Servlet 的 name 属性。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式 |
loadOnStartup | int | 指定 Servlet 的加载顺序 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式 |
description | String | 该 Servlet 的描述信息 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用 |
Servlet的调用过程:
三. 实现Servlet接口
开发Servlet的第一种方式——实现Servlet接口。
Servlet接口中有六个方法需要实现:
public class FirstServlet implements Servlet {
//tomcat服务停止时,初始化Servlet的方法,由tomcat调用,只执行一次。
@Override
public void init(ServletConfig arg0) throws ServletException {}
//用户的每一次请求,都会执行这个方法,由用户发出请求,由tomcat调用这个方法
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {}
//tomcat服务停止时,服务器会去销毁Servlet,由tomcat调用,用于清空某些资源,只调用一次
@Override
public void destroy() {}
//获取这个servlet在web.xml中的配置信息
@Override
public ServletConfig getServletConfig() {return null;}
//获取这个serlvet的名称
@Override
public String getServletInfo() {return null;}
}
四. Servlet生命周期
指的就是Servlet的出生到结束。分别经历了加载、初始化、服务、销毁。
- 加载阶段:加载并实例化,创建servlet实例,只调用一次构造方法;
- 初始化阶段: 调用init()方法,只调用一次初始化方法;
- 响应客户请求阶段:调用service()方法,一般业务逻辑在这里处理;
- 终止阶段:调用destroy()方法。
当请求servlet的时候:
- Servlet引擎检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
- 装载并创建该Servlet的一个实例对象:调用该 Servlet 的构造器
- 调用Servlet实例对象的init()方法。
- 创建一个用于封装请求的ServletRequest对象和一个代表响应消息的ServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
- WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
五. ServletConfig接口
功能:读取web.xml中配置的serlvet信息。
Servlet容器将代表当前web应用的对象(ServletContext)和Servlet的配置参数信息一并封装到一个称为ServletConfig的对象中,并在初始化Servlet实例对象时调用init(ServletConfig config)方法将ServletConfig对象传递给Servlet。
常见方法(可在初始化时通过传入的参数ServletConfig config调用):
- getServletName() 获取当前Servlet在web.xml中配置的名字
- getServletContext() 获取代表当前web应用的ServletContext对象
- getInitParameter(String) 获取当前Servlet指定名称的初始化参数的值
- getInitParameterNames() 获取当前Servlet所有初始化参数的名字组成的枚举
配置web.xml
<servlet>
<servlet-name>secondServlet</servlet-name>
<servlet-class>com.qibao.servlet.SecondServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>secondServlet</servlet-name>
<url-pattern>/second.do</url-pattern>
</servlet-mapping>
在init(ServletConfig config)方法中调用:
@Override
public void init(ServletConfig config) throws ServletException {
System.err.println("在配置文件中的名称:" + config.getServletName());
//获取所有初始化参数的名字组成的枚举
Enumeration<String> en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String nm = (String) en.nextElement();
// 根据初始化的名字获取值
String value = config.getInitParameter(nm);
System.err.println(nm + ":" + value);
}
}
六. GenericServlet类
开发Servlet的第二种方式——继承GenericServlet类。
省略在web.xml中配置的环节,直接看代码
public class SecondServlet extends GenericServlet{
private static final long serialVersionUID = 1L;
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {}
}
- GenericServlet提供除service()方法外所有Servlet接口中方法的缺省实现,GenericServlet的方法,只有service(req,res)是抽象方法,只需要实现这个方法就好
- GenericServlet也实现了ServletConfig接口,处理初始化参数和servlet上下文,提供对授权传递到init()方法中的ServletConfig对象的获取方法getServletConfig()。
- 如果要做初始化操作,可重写GenericServlet中的init()方法 。而非init(servleconfig)带参数的方法。 init() --- 此方法不是serlvet的生命周期方法,只是被GenericSerlvet中的init(servleconfig)再次调用的方法 。
1:实现了javax.serlvet.Selvet接口,用户只需要重写service方法;
2:包装了SevletConfig用户可直接调用。
七. HttpServlet类
开发Servlet的第三种方式——继承HttpServlet类。
- HttpServlet是GenericServlet的子类
- HttpServlet 类通过调用指定到HTTP请求的方法实现service()
- 通过HttpServlet去开发servlet,需要重写doGet、doPost方法
@WebServlet("/thirdServlet")
public class ThirdServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public ThirdServlet() {super();}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 调用 MyServlet的service(ServletRequest, ServletResponse)方法,但MyServlet没有重写这个方法,所以到MyServlet的父类HttpServlet去找;
- 调用HttpServlet的service(ServletRequest, ServletResponse)方法,它又调用了service(HttpServletRequest, HttpServletResponse);
- HttpServlet的service(HttpServletRequest, HttpServletResponse)方法,它先对请的方法进行判断,如果是GET请求,则调用doGet()方法,如果是POST请求,则调用doPost()方法;
- 如果在MyServlet中重写了 doGet(),doPost()方法,则会调用MyServlet中的重写的响应的doGet(),doPost()方法。
注:必须要重写至少一个doXXX方法,如果没有重写doXXX方法,则会报405错误。
八. 线程安全问题
servlet不是线程安全的。Servlet体系结构建立在Java多线程机制之上,它的生命周期由Web容器负责。当客户端第一次请求某个Servlet时,Servlet容器将会根据配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。可参考售票系统.
解决方法:
- 使用 synchronized同步代码块。效率太低,不建议使用
- 不依赖实例全局成员变量,将接收参数的变量声明成局部变量。因为,局部变量默认就是线程安全的。局部变量在线程的栈中,先进后出。
九. ServletContext接口
ServletContext是一个全局的储存信息的空间,服务器开始就存在,服务器关闭才释放。
一个web项目,只存在一个ServletContext实例,每个Servlet都可以访问到它,用于共享数据。
获取方法:
ServletConfig和GenericServlet的getServletContext()方法
功能:
- 获取WEB应用程序的初始化参数。
- //配置全局初始化参数
<context-param>
<param-name>name</param-name>
<param-value>张三</param-value>
</context-param>
//获取全局初始化参数
ServletContext ctx = getServletContext();
Enumeration<String> en = ctx.getInitParameterNames();
while (en.hasMoreElements()) {
String nm = (String) en.nextElement();
//根据初始化的名字获取值
String value = ctx.getInitParameter(nm);
out.print(nm+":"+value+"<hr>");
}
- 获取项目的真实的路径。
//获取真实路径(在服务器上的绝对路径)
response.getWriter().print(ctx.getRealPath("/imgs")+"<hr>");
//获取项目应用上下文(/webapp名称)
response.getWriter().print(ctx.getContextPath()+"<hr>");
- 做为域对象,保存多个客户共享的数据。