Servlet基础知识点回顾(一)
本文最后更新于:2021年10月4日 下午
本篇文章用于简易回顾Servlet基础知识,并阅读自己之前项目里写过的一些Servlet代码。
Servlet基础知识点回顾(一)
什么是Servlet?
Servlet是Sun公司专门用于开发动态Web网页的一门技术,Servlet API中封存了大量的接口和一些实现类来帮助程序员处理web请求和响应。Servlet规定了一系列接口规范,是软件项目工程化的重要体现。
Servlet需要在web.xml中配置路径名和映射,将Servlet映射到Web容器中,并指定Servlet需要处理的路径,Web容器才能找到这个Servlet并进行处理。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
第一个Servlet程序:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("Hello, Servlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Servlet基本原理
以HTTP协议为例,浏览器端发送HTTP请求到Web容器,Web容器接收到这个请求,并将其中诸如请求头、请求体等信息封装为HttpServletRequest
对象,同时也创建一个HttpServletResponse
对象。在浏览器端首次访问时,创建特定的Servlet
对象,这个对象用于处理收到的请求对象,并且编辑响应对象,再将响应对象回传给Web容器,Web容器将响应对象读取转换后发送给浏览器端。
Servlet映射问题
一个Servlet可以指定一个映射路径。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
一个Servlet可以指定多个映射路径。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
一个Servlet可以指定通用路径。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
<!-- 或者指定以特定后缀结尾的路径 -->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<!-- 例如:localhost:8080/asdf/hello.zwy -->
<url-pattern>*.rootzwy</url-pattern>
</servlet-mapping>
一个Servlet可以覆盖index
。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.rootzwy.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!-- 对于具体的路径,Servlet会优先走具体路径,比如 /hello -->
Servlet中的常用对象和方法
HttpServlet
HttpServlet
是一个抽象类,继承自GenericServlet
类,实现了Servlet
接口,封装了一些处理Web请求响应和Servlet管理的方法,例如经典的doGet()
、doPost()
、getServletContext()
等。程序员通过继承此类来重写对应方法,经过service()
方法来处理请求和响应。
ServletContext
Web容器启动时,Web容器为每一个Web程序都创建一个ServletContext
对象,来充当Web程序和Web容器的上下文,每个Servlet
对象代表一个Web程序。以下列出ServletContext
的一些常见基本功能。
Servlet间共享数据:
// In Servlet1
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
ServletContext context = this.getServletContext();
String userName = "zwy";
// 将userName存放进ServletContext中,将在Servlet2中访问这个属性
context.setAttribute("userName", userName);
}
// In Servlet2
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 从ServletContext获取userName
String userName = (String) context.getAttribute("userName");
PrintWriter writer = resp.getWriter();
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
// 输出userName
writer.println("Hello, " + userName);
}
获取web.xml相应元素:
<!-- In web.xml -->
<contxt-param>
<param-name>mysqlUrl</param-name>
<param-value>jdbc:mysql://localhors:3306</param-value>
</contxt-param>
// In Servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContect();
String url = context.getInitParameter("mysqlUrl");
resp.getwriter().println(url);
}
读取资源文件
// In Servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 此处是编译之后资源文件的路径
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/test.properties");
Properties properties = new Properties();
properties.load(is);
String name = properties.getProperty("username");
String pwd = properties.getProperty("password");
PrintWriter writer = resp.getWriter();
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
writer.println("User: " + name);
writer.println("Password: " + pwd);
}
# In properties
username=root
password=123456
HttpServletResponse
HttpServletResponse
是一个接口,继承了ServletResponse
。Web容器在接收到请求时,会针对这个请求,默认创建一个实现这个响应接口的对象,用于封装响应,让程序员通过代码去操作响应信息。
常用方法
PrintWriter getWriter() throws IOException;
ServletOutputStream getOutputStream() throws IOException;
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
// ......
常见应用
向浏览器输出文本或其它数据。
例如下载文件:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取下载文件的路径
ServletContext context = this.getServletContext();
String path = context.getRealPath("/WEB-INF/classes/txt/yinsha.txt");
String fileName = path.substring(path.lastIndexOf("\\") + 1);
System.out.println("输出文件的名称: " + fileName);
// 设置浏览器下载头和编码格式
resp.setHeader("Content-Disposition", "attachment;filename="
+ URLEncoder.encode(fileName, StandardCharsets.UTF_8));
// resp.setContentType("text/html"); // 若指定当前页面为html,则下载的文件都是html文件
resp.setCharacterEncoding("utf-8");
// 获取文件流
FileInputStream in = new FileInputStream(path);
// 创建缓冲区
int len;
byte[] fileBuffer = new byte[1024];
// 获取输出流
ServletOutputStream out = resp.getOutputStream();
// 输出流从Buffer获取文件流
while ((len = in.read(fileBuffer)) != -1) {
out.write(fileBuffer, 0, len);
}
out.close();
in.close();
}
实现简易的动态验证码:
public class SecurityCodeImgServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 网页定时刷新
resp.setHeader("refresh", "3");
// 在缓存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片的画笔,并写入验证码
Graphics2D imgGraphics = (Graphics2D) image.getGraphics();
// Graphics2D imgGraphics = image.createGraphics();
imgGraphics.setColor(Color.WHITE);
imgGraphics.fillRect(0 ,0, 80, 20);
imgGraphics.setColor(Color.BLACK);
imgGraphics.setFont(new Font(null, Font.BOLD, 20));
imgGraphics.drawString(randomSecurityCodeString(), 0, 20);
// 告诉浏览器响应页面的格式
resp.setContentType("image/png");
// Expires表示存在时间,允许客户端在这个时间之前不去检查(发请求)
resp.setDateHeader("expires", -1);
// Cache-control 用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了 Pragma: no-cache)
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
// 把图片写给浏览器(formatName必须是可以使用的图片后缀名)
ImageIO.write(image, "png", resp.getOutputStream());
}
private String randomSecurityCodeString() {
Random random = new Random();
// 设置编码为7位
int digit = 7;
// 生成编码字符串
int limit = 1;
for (int i = 0; i < digit; i++) {
limit *= 10;
}
String randomNum = "" + random.nextInt(limit);
// 字符串前置补0
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < digit - randomNum.length(); i++) {
stringBuffer.append("0");
}
stringBuffer.append(randomNum);
return stringBuffer.toString();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
重定向
void sendRedirect(String var1) throws IOException; // var1是web路径名,如:hello(没有web主路径的"/")
登录重定向和请求转发流程:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 在一个Servlet中重定向到负责登录的jsp或Servlet
resp.sendRedirect("login.jsp");
}
<%--
在此页面(login.jsp)获取用户名信息,然后POST到action下的Servlet页面
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/waiting" method="post">
用户名:
<label>
<input type="text" name="username">
</label> <br>
密 码:
<label>
<input type="password" name="password">
</label> <br>
识别码:
<label>
<input type="text" name="uid">
</label> <br>
<button type="submit">登录</button>
</form>
</body>
</html>
// 在此Servlet中可以获取用户名密码,并进行操作,登录成功后请求转发或重定向到WelcomeServlet
public class loginWaitingServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uid = req.getParameter("uid");
String uname = req.getParameter("username");
if (!uid.equals("root")) {
resp.sendRedirect("pwdError");
} else {
// resp.sendRedirect("welcome");
// 请求转发传递数据
RequestDispatcher dispatcher = req.getRequestDispatcher("welcome");
dispatcher.forward(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
HttpServletRequest
HttpServletRequest
是一个接口,继承了ServletRequest
。Web容器在接收到通过HTTP协议发送过来的请求时,会针对这个请求,默认创建一个实现这个请求接口的对象,用于封装请求的所有信息,让程序员通过代码去操作请求信息。
常用方法
// 几乎所有...
常见应用
获取前端参数
// 用于获取普通的前端参数
String getParameter(String var1);
// 用于获取有多个值的前端参数
String[] getParameterValues(String var1);
请求转发
RequestDispatcher getRequestDispatcher(String var1);