文章目录
Servlet简介
Servlet(Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。
- 狭义的Servlet是指Java语言实现的javax.servet.Servlet接口
- 广义的javax.servet.Servlet是指任何实现了这个Servlet接口的类
一般情况下,人们将Servlet理解为后者。
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
简单来说,Servlet就是一种动态网页技术, Servlet能够接收浏览器的请求(Request)并且给浏览器以响应(Response)。
新建Servlet的三种方式
- 实现Servlet接口
- 继承GenericServlet
- 继承HttpServlet
使用eclipse开发动态网页前要先完成eclipse和Tomcat的集成并且配置部署路径,这里有详细步骤,更改Tomcat端口号的方法也在这里:
我们现在分别用三种方式分别来新建一个Servlet,实现在网页上显示当前时间的功能。
1.实现Servlet接口
下面我们来用第一种方式,实现Servlet接口 来新建一个Servlet并用浏览器打开它。这里我们动态网页模块版本就先选择2.5版本,需要写配置文件,3.0以后的版本可以不用写配置文件。
- 新建 Dynamic Web Project
- 在打开窗口里输入项目名,选择动态网页版本,点击finish:
- 在
ShowTime/WebContent/WEB-INF/lib
目录下添加servlet-api.jar
。
- 依赖一下jar包:
- 在ShowTime/Java Resources/src下新建一个class,实现Servlet接口:
写好包名和类名,点击finish:
- 实现Servlet接口后重写service方法:
package com.xxx.xxx;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class Servlet_001 implements Servlet {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
// TODO Auto-generated method stub
// 在这里实现功能
// 第一步:将ServletResponse强转成HttpServletResponse类型
HttpServletResponse response = (HttpServletResponse) arg1;
// 第二步:获取打印流对象
PrintWriter out = response.getWriter();
// 第三步:显示当前日期到浏览器
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dt = sdf.format(new Date());
out.println("current time:" + dt);
}
}
- 更改配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>ShowTime</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 添加这块代码 -->
<servlet>
<servlet-name>Servlet_001</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_001</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_001</servlet-name>
<url-pattern>/Servlet_001</url-pattern>
</servlet-mapping>
</web-app>
- 添加ShowTime到Tomcat服务器并启动
启动tomcat服务器:
当控制台出现以下信息是说明启动成功:
- 在浏览器中打开
http:localhost/ShowTime/Servlet_001
网页上成功显示了当前时间。这样,一个完整的Servlet就完成了。
我们在浏览器上输入http:localhost/ShowTime/Servlet_001
就可以访问到这个Servlet_001,那么一个Servlet的工作流程是怎样的呢?
什么是web.xml
web.xml主要用来配置Filter、Listener、Servlet等,一份web工程只能有一份web.xml(或者没有)。
WEB工程加载顺序与元素节点在文件中的配置顺序无关,WEB容器的加载顺序是:ServletContext -> context-param -> listener -> filter -> servlet。
这些元素可以配置在文件中的任意位置。
- 加载过程顺序如下:
启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取
<listener>
和<context-param>
两个结点。
紧急着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。
容器将<context-param>
转换为键值对,并交给servletContext。
容器创建<listener>
中的类实例,创建监听器
Servlet的工作流程
首先我们要明白我们在web.xml配置文件中加入的代码是什么意思:
<servlet>
必须含有<servlet-name>
和<servlet-class>
,或者<servlet-name>
和<jsp-file>
<servlet-name>
用来定义servlet的名称,该名称在整个应用中必须是惟一的<servlet-class>
用来指定servlet的完全限定的名称。<jsp-file>
用来指定应用中JSP文件的完整路径。这个完整路径必须由/开始。
<servlet-mapping>
将URL模式映射到某个Servlet,即该Servlet处理的URL。
<servlet-name>
:Servlet的名字,唯一性和一致性,与<servlet>
元素中声明的名字一致。<url-pattern>
:指定相对于Servlet的URL的路径。该路径相对于web应用程序上下文的根路径。
这个代码块主要是让服务器知道让哪个Servlet来处理请求,在浏览器中我们会这样输入来访问某个Servlet:http://localhost:端口/项目名/Servlet根路径
,其中的/Servlet根路径
和我们web.xml中<servlet-mapping>
的<url-pattern>
是一致的。
Servlet流程图:
首先呢,我们在浏览器地址栏中输入了URL,浏览器就会向web服务器,也就是我们的Tomcat来发送请求,Tomcat收到请求,会首先获取请求URL,从中截取到项目名称,然后读取配置文件web.xml,在里面找到和项目名称匹配的项目路径,找到Servlet_001,把这次请求交给它去做处理,Servelt_001就会根据请求方式(get/post)来作出相应的响应(一般来说针对二者的响应是一样的),并把相应传递给浏览器,浏览器收到响应,就会做出进一步的动作。
2.继承GenericServlet
我们现在来使用第二种方式来新建一个Servlet,与第一种方法类似:
- 继承GenericServlet,重写service()方法:
package com.xxx.xxx;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class Servlet_002 extends GenericServlet {
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
// TODO Auto-generated method stub
// 第一步:将ServletResponse强转成HttpServletResponse类型
HttpServletResponse response = (HttpServletResponse) arg1;
// 第二步:获取打印流对象
PrintWriter out = response.getWriter();
// 第三步:显示当前日期到浏览器
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dt = sdf.format(new Date());
out.println("current time:" + dt);
}
}
- 更改web.xml配置文件,保存
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>ShowTime</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置Servlet_001 -->
<servlet>
<servlet-name>Servlet_001</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_001</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_001</servlet-name>
<url-pattern>/Servlet_001</url-pattern>
</servlet-mapping>
<!-- 配置Servlet_002 -->
<servlet>
<servlet-name>Servlet_002</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_002</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_002</servlet-name>
<url-pattern>/Servlet_002</url-pattern>
</servlet-mapping>
</web-app>
-
重启服务器:
-
在浏览器中访问:
http://localhost/ShowTime/Servlet_001
3.继承HttpServlet
先来看一下文档中关于HttpServlet的介绍:
提供了一个可以由之生成子类以为WEB站点创建HTTP servlet 的抽象类。 HttpServlet的子类必须至少覆盖一个方法。通常是下列方法中的一个:
doGet, 如果servlet 支持 HTTP GET 请求
doPost, 针对 HTTP POST 请求
doPut, 针对 HTTP PUT 请求
doDelete,针对HTTP DELETE 请求
init 和destroy, 为管理为servlet的存在而锁定的资源。
getServletInfo, servlet 用以提供关于器其自身信息。
几乎没有必要重载 doOptions 和doTrace 方法。
如果要完成上面的任务-在浏览器页面显示当前时间,那么我们只需要重写doGet方法就可以达到要求:
- 继承HttpServlet类,重写doGet方法
package com.xxx.xxx;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet_003 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
// 不需要再向下转型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dt = sdf.format(new Date());
resp.getWriter().println("current time:" + dt);
}
}
- 更改配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>ShowTime</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置Servlet_001 -->
<servlet>
<servlet-name>Servlet_001</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_001</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_001</servlet-name>
<url-pattern>/Servlet_001</url-pattern>
</servlet-mapping>
<!-- 配置Servlet_002 -->
<servlet>
<servlet-name>Servlet_002</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_002</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_002</servlet-name>
<url-pattern>/Servlet_002</url-pattern>
</servlet-mapping>
<!-- 配置Servlet_003 -->
<servlet>
<servlet-name>Servlet_003</servlet-name>
<servlet-class>com.xxx.xxx.Servlet_003</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet_003</servlet-name>
<url-pattern>/Servlet_003</url-pattern>
</servlet-mapping>
</web-app>
- 重启服务器,打开浏览器:
至此,三种方式我们都已经介绍完了,实际开发中我们通常采用第三种:继承HttpServlet的方法来新建一个Servlet,下面来介绍新建Servlet的简单方法:
实际开发新建Servlet
我们new的时候直接new一个Servlet,eclipse会自动帮我们更改配置文件,eclipse默认的是继承HttpServlet的方式:
package com.xxx.xxx;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Servelt_004
*/
public class Servelt_004 extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public Servelt_004() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//在这里实现功能
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
//doPost方法调用doGet方法
}
}
eclipse会自动帮我们创建好Servelt,默认重写的是doGet和doPsot方法。
这里注意,我们没有必要去针对get和post方法写两份代码,eclipse会默认doPost方法调用的是doGet方法,所以,我们只需要重写doGet方法就可以。
Servlet的生命周期
每个Servlet的运行都遵循如下生命周期:
- 创建Servlet实例。
- Web容器调用Servlet的init()方法,对Servlet进行初始化。
- Servlet初始化后,将一直存在于容器中,用于响应客户端请求,如果客户端发送GET请求,容器调用Servlet的doGet()方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost()方法处理并响应请求。或者统一使用service()方法处理来响应用户请求。
- Web容器决定销毁Servlet时,先调用Servlet的destory()方法,通常在关闭Web应用时销毁Servlet实例。
下面我们来演示一下一个Servlet的声明周期:
- new 一个Srvelt
点击finish。 - 重写方法:
package com.xxx.xxx;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class testLifeCircle
*/
public class testLifeCircle extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public testLifeCircle() {
super();
System.out.println("构造方法被调用了……");
}
/**
* @see Servlet#init(ServletConfig)
*/
public void init(ServletConfig config) throws ServletException {
System.out.println("初始化方法被调用了……");
}
/**
* @see Servlet#destroy()
*/
public void destroy() {
System.out.println("destroy方法被调用了……");
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doGet方法被调用了……");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
- 重启服务器,打开浏览器,返回eclipse,在Servers窗口点击Console我们会看到如下信息:
这是第一次请求,需要调用构造方法和init方法实例化,然后调用doGet方法。
之后多次请求,不需要再实例化,直接调用doGet方法:
此时,如果我们Servlet原代码发生任何改动,比如我们随便加一行注释,然后等待服务器Reloading
这就是一个Servlet的生命周期,总结一下:
方法 | 调用次数 |
---|---|
构造方法 | 一次 |
初始化方法 | 一次 |
doGet/doPost方法 | 多次 |
销毁方法 | 一次 |
- 这足以说明Servlet是单例多线程的。
生命周期还有另外一种解释:
方法 | 调用次数 |
---|---|
构造方法 | 一次 |
初始化方法 | 一次 |
serveice方法 | 多次 |
doGet/doPost方法 | 多次 |
销毁方法 | 一次 |
这种模式简单来说就是当浏览器发送请求后,服务器会首先到你的servlet的父类HttpServlet中调用它的servcie方法去getMethod(),判断请求是get 还是 post,再来选择调用doGet()或者doPost()。
Post和Get的区别
GET的语义是请求获取指定的资源。GET方法是安全、幂等、可缓存的(除非有 Cache-ControlHeader的约束),GET方法的报文主体没有任何语义。
POST的语义是根据请求负荷(报文主体)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST不安全,不幂等,(大部分实现)不可缓存。为了针对其不可缓存性,有一系列的方法来进行优化。
比如在微博这个场景里,GET的语义会被用在「看看我的Timeline上最新的20条微博」这样的场景,而POST的语义会被用在「发微博、评论、点赞」这样的场景中。
访问Servlet的几种方式
1.浏览器输入地址——http://ip地址:端口号/项目名/访问路径
2.超链接
3.form表单
4.window.location.href