Servlet
简介
Servlet就是sun公司用于开发动态web的一门技术。sun公司在这些API中提供了一个接口叫做:Servlet。如果你想开发一个Servlet程序,只需要完成两个小步骤:
1、编写一个类实现Servlet接口
2、把开发好的java类部署到web服务器中。
把实现了Servlet接口的java程序叫做Servlet,本质还是Java
HelloServlet
Servlet接口在sun公司有两个默认的实现类:HttpServlet、GenericServlet
Servlet原理
Servlet是由web服务器调用,web服务器在收到浏览器请求后,就有以下的方法去请求和响应:
Servlet实现
-
构建一个Maven项目,删除src目录,在这个目录下创建Moudel,这个空的工程就是Maven主工程。
-
关于Maven父子工程的理解
父项目中会有
<modules> <module>servlet-01</module> </modules>
-
子项目中会有
<parent> <groupId>com.louis</groupId> <artifactId>kuang</artifactId> <version>1.0-SNAPSHOT</version> </parent>
父项目中的java再子项目中可以直接使用。
-
Maven环境优化
1、修改web.xml为最新
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> </web-app>
2、将Maven的结构搭建完整
-
编写一个Servlet程序
一个Servlet程序一定要实现一个Servlet接口。方法如下
1、编写一个普通类
2、实现Servlet接口,我们可以直接继承HttpServlet
package com.louis.servlet; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * @author XRY * @date 2023年04月29日20:18 */ public class HelloServlet extends HttpServlet { //由于get和post只是请求实现的方式不同,可以相互调用,业务逻辑都一样。 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ServletOutputStream outputStream = resp.getOutputStream(); PrintWriter writer = resp.getWriter(); writer.println("Hello Servlet!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }
-
编写Servlet的映射
由于我们写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> <!-- 注册Servlet--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.louis.servlet.HelloServlet</servlet-class> </servlet> <!-- Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
-
配置Tomcat
-
启动测试
web.xml中Mapping问题
-
一个Servlet可以指定一个映射路径
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
一个Servlet可以指定多个路径
<!--Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping>
-
一个Servlet可以指定通用映射路径(默认请求路径)
<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
可以指定一个后缀或者前缀等
<!-- Servlet的请求路径--> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>*.louis</url-pattern> </servlet-mapping>
*注意:前面不能加项目映射到的路径。
-
优先级问题
制定了固有的映射路径,优先级最高,如果找不到就会走默认的处理请求。
ServletContext
web容器启动的时候,它会为每一个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用,可以用来实现数据共享。
共享数据
我们在这个Servlet中保存的数据,可以在另一个servlet中拿到。
- 存放数据类
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("你好!");
// this.getInitParameter() 初始化参数
// this.getServletConfig() Servlet配置
// this.getServletContext() Servlet上下文
ServletContext servletContext = this.getServletContext();
String name = "louis";
servletContext.setAttribute("name",name);//将一个数据保存在ServletContest中,名字为name,值为name
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 取数据的类
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
ServletContext servletContext = this.getServletContext();
String name = (String)servletContext.getAttribute("name");
resp.getWriter().println("名字"+ name);
System.out.println();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 配置web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>khan.louis.test.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>khan.louis.test.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
数据转发
ServletContext还可以实现数据的转发功能。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
System.out.println("进入了Demo01");
// RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/getc");//转发的请求路径
// requestDispatcher.forward(req,resp);//调用forward实现请求转发
servletContext.getRequestDispatcher("/getc").forward(req,resp);
}
读取资源文件
Properties
在resources目录下新建properties(如果不存在,可能就是maven出现资源导出失败的问题,为了解决这个我问题,我们可以在build中配置resources。)
发现:他们都被打包到了classes路径下,我们俗称这个路径为classpath
package khan.louis.test;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author XRY
* @date 2023年05月02日16:53
*/
public class PropertiesServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
InputStream resourceAsStream = servletContext.getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username + "<>" + password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表这个请求的HttpServletRequest对象,代表响应的HttpServletResponse对象;
如果要获取客户端请求过来的参数需要找HttpServletRequest,
如果给客户端响应一些信息使用HttpServletResponse
类中方法的简单分类
负责向浏览器发送数据的方法
public ServletOutputStream getOutputStream() throws IOException;//写平常的流使用
public PrintWriter getWriter() throws IOException;//写中文使用
负责向浏览器发送响应头的方法
public void setCharacterEncoding(String charset);
public void setContentLength(int len);
public void setContentLengthLong(long len);
public void setContentType(String type);
public void setDateHeader(String name, long date);
public void addDateHeader(String name, long date);
public void setHeader(String name, String value);
public void addHeader(String name, String value);
public void setIntHeader(String name, int value);
public void addIntHeader(String name, int value);
响应状态码
public int getStatus();
常见应用
-
向浏览器输出信息
-
下载文件
①要获取下载文件的路径
②下载的文件名
③设置让浏览器能够支持下载问哦们需要的东西
④获取下载文件的输入流
⑤创建缓冲区
⑥获取OutputStream对象
⑦将FileOutStream流写到buffer缓冲区,使用OutputStream将缓冲区数据输出到客户端。
package khan.louis.test;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author XRY
* @date 2023年05月02日17:43
*/
public class TestResponse extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//①要获取下载文件的路径
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html");
String realPath = "servlet-02\\src\\main\\resources\\图片1.png";
// G:\练习库\coding\JAVAIDEA\javaweb\kuang\servlet-02\src\main\resources\图片1.png
System.out.println("下载的文件路径为:" + realPath);
// ②下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// ③设置让浏览器能够支持"Content-Disposition"下载我们需要的东西
resp.setHeader("Content-Disposition","attachment;filename=" + fileName);
// ④获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// ⑤创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// ⑥获得OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// ⑦将FileOutStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
while((len = in.read(buffer))>0){
out.write(buffer, 0, len);
}
in.close();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
验证码功能
验证码怎么来的?
前端实现
后端实现: 需要用到java的图片类,生成一个图片
package khan.louis.test;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
/**
* @author XRY
* @date 2023年05月03日15:50
*/
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5秒自动刷新一次
resp.setHeader("refresh", "3");
//在内存中创建图片
BufferedImage bufferedImage = new BufferedImage(100,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();//画笔
//设置图片的背景颜色
graphics.setColor(Color.white);
graphics.fillRect(0,0,100,20);
//给图片写数据
graphics.setColor(Color.BLUE);
graphics.setFont(new Font(null, Font.BOLD, 20));
graphics.drawString(makeNum(),0, 20);
//告诉浏览器这个请求使用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
//把图片写给浏览器
ImageIO.write(bufferedImage,"jpg",resp.getOutputStream());
}
//生成随机数
private String makeNum(){
Random random = new Random();
String s = random.nextInt(99999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7-s.length(); i++) {
sb.append("0");
}
s = sb.toString() + s;
return s;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
重定向
一个web资源收到客户端请求后,它会通知客户端去访问另一个web资源,这个过程叫做重定向
常见场景:
用户登录
package khan.louis.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author XRY
* @date 2023年05月03日16:32
*/
public class TestRedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("/servlet/image");
}
}
重定向实现步骤
重定向resp.sendRedirect(“”);主要实现了下面的两个步骤:
resp.setHeader("Location", "/servlet/image");
resp.setStatus(302);
实例
<html>
<body>
<h2>Hello World!</h2>
<%--这里提交的路径,需要找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<meta charset="UTF-8">
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
</html>
package khan.louis.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author XRY
* @date 2023年05月03日16:47
*/
public class RequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理请求
String username = req.getParameter("username");
String pwd = req.getParameter("password");
System.out.println(username + " \n" + pwd);
//重定向一定要注意路径问题,否则404
resp.sendRedirect("/servlet/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
<%--
Created by IntelliJ IDEA.
User: XRY
Date: 2023/5/3
Time: 17:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http访问服务器,Http请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息。
获取前端传递的参数、请求转发
getParameter(String s);//返回单个 getParameterValues(String s);//返回多个
示例
<%@ page contentType="text/html; charset=gb2312"%>
<html>
<head>登录</head>
<body>
<h2>登录</h2>
<%--表单表示的意思:以post方式提交表单,提交到我们的login请求--%>
<form action="${pageContext.request.contextPath}/login" method="post">
<meta charset="UTF-8">
<div style="text-align: center">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:
<input type="checkbox" name="hobby">女孩
<input type="checkbox" name="hobby">代码
<input type="checkbox" name="hobby">唱歌
<input type="checkbox" name="hobby">电影
<br>
<input type="submit">
</div>
</form>
</body>
</html>
package khan;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
/**
* @author XRY
* @date 2023年05月03日19:10
*/
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] parameterValues = req.getParameterValues("hobby");
System.out.println("===================================");
System.out.println(username + "\t" + password);
System.out.println(Arrays.toString(parameterValues));
// resp.sendRedirect("/request/success.jsp");
//通过请求转发
//这里的 / 代表类当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req, resp);
req.setCharacterEncoding("UTF-8");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
//success.jsp
<%--
Created by IntelliJ IDEA.
User: XRY
Date: 2023/5/3
Time: 19:25
To change this template use File | Settings | File Templates.
--%>
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登录成功</h1>
</body>
</html>
面试题:请你聊聊重定向和转发的区别?
相同点:
两者都会实现页面的跳转
不同点:
请求转发的时候,url不会产生变化 307
重定向的时候,url地址栏会发生变化 302