参考资料:
《Java Web开发基础教程》 —— 李俊(主要)编著
- 1. Servlet 简介
- 2. 开发环境搭建
- 2.1 安装配置Tomcat
- 2.2 IDEA创建Web项目
- 3. Hello Servlet
- 3.1 编写测试类
- 3.2 编写跳转页面
- 3.3 修改Tomcat配置
- 4.Servlet 标识的两种方式
- 4.1 注解方式
- 4.2 XML配置文件方式
- 5. Servlet 的生命周期
- 5.1 初始化阶段
- 5.2 运行阶段
- 5.3 销毁阶段
- 6. Servlet 的常用对象
- 6.1 ServletConfig 对象
- 6.1.1 方法说明
- 6.1.2 测试
- 6.2 ServletContext对象
- 6.2.1 获取Web应用的初始化参数
- 6.2.2 实现多个Servlet的数据共享
- 6.3 HttpServletRequest 对象
- 6.3.1 获取请求消息头
- 6.3.2 获取请求消息体
- 6.3.3 解决乱码 & 请求转发 Servlet 传递
- 6.3.4 请求转发的5个特点
- 6.4 HttpServletResponse 对象
- 6.4.1 常用设置响应消息头字段方法
- 6.4.2 response 对象输出流的方法
- 6.4.3 文件下载 & 文本输出
- 6.4.4 解决乱码 & 请求重定向
- 6.4.5 请求重定向的5个特点
Servlet 的定义:Servlet是一种运行在Web服务端的Java应用程序,是由SUN公司提供的一种用于开发动态Web资源的技术。
Servlet 的作用:
- 收集来自网页表单
- 通过与数据库交互来操作数据
- 动态创建网页
Servlet 的本质:遵循规范进行编写的Java类,没有main方法,创建、使用、销毁都有Servlet容器进行管理
如上图所示,将HTTP请求交由给Servlet程序处理的HTTP服务器通常称为Servlet容器,常见容器有Tomcat、Jetty、WebLogic以JBoss等
2. 开发环境搭建2.1 安装配置Tomcat
- Windows10 操作系统
- JDK8——用于支持基于Java语言开发的软件环境
- Tomcat9——基于Java语言开发的免费开源的Web应用服务器,即Servlet容器
- IDEA 2021 专业版——Java语言开发的集成环境,可搭建JavaWeb项目(Community Edition社区版除外)
这里主要介绍Tomcat的安装与配置
Tomact官网 https://tomcat.apache.org/
以 【Tomcat 9.0.52 Released】版本为例
选择可执行文件,方便安装
安装后的目录结构
配置环境变量,在系统变量一栏新建,用于表示JDK和Tomcat的目录
在系统变量中的PATH变量添加JDK和Tomcat的bin文件夹
至此,Tomcat配置完毕,接下来是web项目的创建。
2.2 IDEA创建Web项目首次创建Java企业项目,需要新建Tomcat的服务器
找到之前安装好的Tomcat目录
若下方未显示JDK,点击添加后,选择对应的JDK目录即可
点击下一步后,只需选择Servlet
目前的项目结构
3.1 编写测试类
HelloServlet.java
package com.example.project;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello")
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("Hello,Servlet!");
}
}
3.2 编写跳转页面
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
JSP - Hello World
<%= "Hello World!" %>
Hello Servlet
访问localhost:8080/project_war_exploded的页面
点击超链接后的页面
通过刚才的测试可以发现,访问的URL末尾有项目的名称project_war_exploded,那么如何省去这个名称,使得测试URL看起来更简洁呢,接下来就需要配置一下Tomcat
首先设置自动更新类和资源,这样在修改JSP页面的时候,服务器可以自动的刷新
接着点击部署,然后向下滑动滚动条
选择第一个空白
选择完毕后依次点击应用Apply和确定OK
再次启动Tomcat后,效果如下
对于部分3的测试,你是否疑惑,为什么在点击超链接后,web服务器能执行java类的代码呢?
在了解这些之前,我们看看前端页面的关键一句代码:Hello Servlet
当我们在页面点击超链接后,地址URL变成了localhost:8080/hello,同时也可以直接访问这个地址。
在helloServlet类中有这么一句:@WebServlet(name = "helloServlet", value = "/hello")
在Java当中像这样带@符号的叫做注解,其作用是将当前类标识为一个Servlet类,这样Tomcat
容器便可以访问到该类。
注:Servlet标识不能重复,否则部署项目时会报错
4.1 注解方式有两种方式使用注解来标识Servlet类
- @WebServlet(name = "ServletClassName", value = "url")
- @WebServlet(name = "ServletClassName", urlPattern="url")
其中的url便是前端网页调用时需要访问的地址比如 @WebServlet(name = “helloServlet”, value = “/hello”)
4.2 XML配置文件方式在web项目中通常会有web.xml配置文件
注解是JDK1.5版本、Servlet3.0版本之后开始出现的,在那之前,JavaWeb项目里的Servlet声明通常是通过配置该文件,比如声明之前的hello类,具体代码如下:
hello com.example.project.HelloServlet hello /hello
XML配置Servlet说明
用于注册Servlet,并且需要制定Servlet名称和完整类名 则用于映射Servlet对外访问的虚拟路径 必须存在于某个 中 则用于指定访问该Servlet的虚拟路径,以"/"开头则表示Web应用程序的根目录
Servlet和Java中其他任何对象一样,都有生命周期,即对象从创建到销毁的过程。
5.1 初始化阶段可以在Servlet类中重写init()方法,来编写Servlet类初始化的代码
当Servlet 容器(Tomcat)接收到客户端发来的HTTP请求要访问某个Servlet时,首先检查内存中是否已经有了该Servlet对象,如果有就直接使用,否则就创建Servlet实例对象。
调用 init()方法实现Servlet的初始化工作,在整个生命周期过程中只执行一次。
在代码具体实现中有两种重写方式:
- public void init(ServletConfig config) 该方法用到了Servlet的config对象,这是读取关于Servlet的一些配置,有XML配置和注解配置两种方式,在之后部分会详细介绍
- public void init() 仅仅做初始化的工作,具体工作由开发者编写
package com.example.project;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello")
public class HelloServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
@Override
public void init() throws ServletException {
super.init();
}
}
5.2 运行阶段
Servlet容器(Tomcat)将HTTP请求和响应封装成Java对象 并作为参数传给service()方法,该方法会根据HTTP请求的类型决定调用的doGet()或doPost()方法来处理业务逻辑。
Servlet容器对于每一次的访问请求都会调用一次service()方法,而在实际开发中,一般只关注doGet()和doPost()方法中具体业务的实现。
5.3 销毁阶段当服务器关闭或Web应用被移除Servlet容器时,Servlet对象被销毁。
销毁之前,容器会调用destroy()方法,以便Servlet对象释放所占用的资源。
6. Servlet 的常用对象Servlet的具体实现是通过继承HttpServlet类来进行的,作为一个Java类,它包含一些常用对象:
| 对象名 | 描述 |
|---|---|
| ServletConfig | 初始化Servlet |
| ServletContext | 共享多个Servlet的信息 |
| HttpServletRequest | HTTP请求信息 |
| HttpServletResponse | HTTP响应信息 |
6.1.1 方法说明
该对象主要是用于存储一些关于Servlet的配置信息,常用方法有:
| 方法名 | 作用 |
|---|---|
| String getInitParameter(String name) | 根据初始化参数名返回对应的参数 |
| Enumeration getInitParameterNames() | 返回所有的初始化参数名 |
| ServletContext getServletContext() | 返回一个表示当前Web应用的ServletContext对象 |
| String getServletNames() | 返回 Servlet 的名字 |
- 注解方式
package com.example.project;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet",
value = "/hello",
initParams = {
@WebInitParam(name = "Encoding", value = "UTF-8"),
@WebInitParam(name = "myName", value = "lazycat")
})
public class HelloServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println(config.getInitParameter("Encoding"));
System.out.println("Hello " + config.getInitParameter("myName"));
}
}
效果图:
- XML方式
web.xml
hello com.example.project.HelloServlet Encoding UTF-8 myName lazycat hello /hello
这种方式下的Servlet类简化为:
HelloServlet.java
package com.example.project;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println(config.getInitParameter("Encoding"));
System.out.println("Hello " + config.getInitParameter("myName"));
}
}
通过比较发现,还是注解看起来简洁一些。
6.2 ServletContext对象Servlet容器(Tomcat)启动时,会为每个Web应用创建一个唯一的ServletContext对象来代表当前Web应用。
该对象不仅封装了当前Web应用的所有信息,而且实现了多个Servlet之间数据的共享。
它的主要作用有:获取Web初始化参数、实现多个Servlet的数据共享
常用方法有:
| 方法名 | 作用 |
|---|---|
| Enumeration getAtrributeNames() | 返回所有ServletContext所有域属性名的一个可迭代对象 |
| Object getAttribute(String name) | 根据参数属性名返回相应属属性值 |
| void removeAttribute(String name) | 根据参数属性名删除相应的域属性 |
| void setAttribute(String name, Object obj) | 设置域参数,其中name是属性名,obj是属性值 |
在web.xml文件中,除了配置Servlet的初始化信息外,还可以配置整个Web应用的初始化信息
appName javaweb appAddress myapp
在Servlet类中获取所有初始化参数的值.以doGet方法为例:
ServletContext context = this.getServletContext(); Enumeration6.2.2 实现多个Servlet的数据共享names = context.getInitParameterNames(); while(names.hasMoreElements(){ String name = names.nextElement(); String value = context.getInitParameter(name); System.out.println(name + "," + value); }
以下两个Servlet类均以doGet方法为例
servlet1.java
this.getServletContext().setAttribute("data", "test");
servlet2.java
ServletContext sc = this.getServletContext();
System.out.println(sc.getAttribute("data"));
6.3 HttpServletRequest 对象
该对象是doGet和doPost方法的第一个参数。
HTTP请求消息分为请求消息头 和 请求消息体 两部分。
6.3.1 获取请求消息头相关方法:
| 方法名 | 作用 |
|---|---|
| String getMethod() | 获取HTTP的请求方式(如GET,POST等) |
| String getRequestURI() | 获取URL请求的资源名称部分,即位于URL的主机和端口号之后,参数之前的部分 |
| String getContextPath() | 获取URL请求的参数部分,即URL中间号(?)后面的所有内容 |
| String getServletPath() | 获取URL中Web应用程序的路径,以"/"开头 |
| Enumeration getHeaderNames() | 获取所有请求消息头的名称 |
| String getHeader(String name) | 获取一个指定名称的头字段的值 |
| String getCharcterEncoding() | 获取请求消息体部分的字符集编码 |
相关方法:
| 方法名 | 作用 |
|---|---|
| String getParameter(String name) | 获取某个指定名称的参数值,若不存在则返回null |
| String[] getParameterValues(String name) | Http请求消息中可有相同名称的参数,(如表单的复选框),返回所有对应参数值 |
解决乱码 :
- doPost方法request.setCharacterEncoding("utf-8");
- doGet方法
String data = req.getParameter("data")
data = new String(data.getBytes("iso-8859-1"), "utf-8");
请求转发:
在Servlet中,当一个Web资源收到客户端请求后,如果希望Servlet处理请求,或希望将请求的数据转发到页面上显示,可通过RequestDispathcer 对象的 forward()方法实现。
ServletRequest接口定义了 getRequestDispatcher(String path) 方法
作用:返回封装某个路径所指定资源的RequestDispatcher对象,其中path必须以"/"开头,表示当前Web应用的根目录
注:WEB-INF目录的内容对 RequestDispathcer对象 可见
| 方法名 | 作用 |
|---|---|
| forward(ServletRequest request, ServletResponse response | 从一个Servlet传递给另一个Web资源。该方法必须在响应提交给客户端之前被调用 |
范例:servlet1 请求传递到servlet2(以doGet方法为例)
servlet1.java
request.setAttribute("name", "lazycat");
request.getRequestDispathcer("/servlet2").forward(request, response);
servlet2.java
String name = (String)request.getAttribute("name");
6.3.4 请求转发的5个特点
- 浏览器地址栏无变化,请求转发属于服务器端内部的行为,作为客户端并不能知道该过程
- 不管服务器端请求转发多少次,对于客户端都属于一次请求。
- 转发的Servlet之间共享request域对象的数据
- 可转发到WEB-INF目录下的页面文件中
- 不可以访问工程以外的资源。该过程只能发生在同一个工程的不同Web资源之间
HTTP响应消息分为状态行、响应头和消息体三部分
6.4.1 常用设置响应消息头字段方法| 方法名 | 作用 |
|---|---|
| void setHeader(String name, String value) | 设置HTTP的响应头字段 |
| void setContentLength(int len) | 设置响应消息的实体内容大小,单位为字节(B) |
| void setContextType(String type) | 设置Servlet输出内容的MIME类型。例如,若发送到客户端的内容是PDF的文档数据,则设置为"application/pdf",若为文本,则为"text/html;charset=UTF-8" |
| void setCharacterEncoding(String charset) | 设置输出内容使用的字符编码 |
| 方法名 | 作用 |
|---|---|
| getOutputStream() | 获取字节输出流对象,可直接输出字节数组中的二进制数据 |
| getWriter() | 获取类型为PrintWriter的字节输出流对象,可直接输出字符文本内容 |
通过该对象可实现文件下载,以doGet方法实现下载PDF文件为例(pdf在index.jsp同级目录中):
response.setContextType("application/pdf");
response.setHeader("Context-Disposition", "attachment;fileName="+"save.pdf");
String path = this.getServletContext().getRealPath("/");
File file = new File(path + "test.pdf");
ServletOutputStream out;
try{
FileInputStream inputStream = new FileInputStream(file);
out = response.getOutputStream();
int b = 0;
byte[] buffer = new byte[512];
while((b = inputStream.read(buffer)) != -1){
out.wirte(buffer, 0, b);
}
inputStream.close();
out.close();
out.flush(); // 强制刷新缓冲区
} catch(IOException e){
e.printStackTrace();
}
文本输出
String data = "test print"; PrintWriter out = response.getWriter(); out.write(data);6.4.4 解决乱码 & 请求重定向
解决乱码:
方式一:
response.setCharacterEncoding("UTF-8");
response.setHeader("Context-type", "text/html;charset = utf-8");
方式二:
response.setContextType("text/html;charset="utf-8")
请求重定向:指当Web服务器端接收到客户端的请求后,可能由于某些原因不能访问当前请求的URL所指向的Wweb资源,或服务器根据业务需求而重新指定了一个新的资源路径,让客户端重发请求
可是使用sendRedirect(String location) throws IOException 实现请求重定向
location用于指定重新发送请求的URL地址(相对或者绝对地址)
原理图如下:
- 浏览器地址栏会发生变化,请求重定向会告知客户端浏览器重新定位的URL地址,浏览器则会根据该地址重新发送请求
- 客户端发送了两次请求。客户端第一次请求资源后,根据sendRedirect()方法返回的URL路径,重新发送第二次请求
- 不共享request域对象中的数据。两次请求的数据无法共享
- 不能访问WEB-INF下的资源
- 可访问当前工程以外的资源



