Servlet运行原理
Tomcat 的代码内置了 main 方法,当我们启动 Tomcat 的时候,就是从 Tomcat 的 main 方法开始执行的
被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取并集中管理
Tomcat 通过反射这样的语法机制来创建被 @WebServlet 注解修饰的类的实例
这些实例被创建完之后,就会调用其中的 init 方法进行初始化
这些实例被销毁之前,就会调用其中的 destory 方法进行收尾工作
Tomcat 内部也是通过 Socket API 进行网络通信
Tomcat 为了能够同时处理多个 HTTP 请求,采取了多线程的方式实现,因此 Servlet 是运行在多线程环境下的
Post请求测试
浏览器直接发送的是get请求,这里使用Postman进行接口测试
@WebServlet("/testDoPost")
public class TestDoPostServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("testPost success!");
}
}
Response重定向到其他页面
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://blog.csdn.net/weixin_44610169");
}
}
post请求体body中携带信息获取
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
resp.getWriter().write("username = " + username + " password = " + password);
}
}
body内容格式为JSON数据获取
JSON格式的数据需要解析,Java内置库没有对JSON的解析,所以这里导入第三方数据库jackson,使用jackson解析读取请求中body内的数据.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
//定义一个JavaBean类
class UserInfo{
public String username;
public String password;
}
//读取请求中所有body
public String readBody(HttpServletRequest req) throws IOException {
//获取请求体长度
int contentLength = req.getContentLength();
//获取请求体内容
byte[] bytes = new byte[contentLength];
//读取请求体内容
ServletInputStream inputStream = req.getInputStream();
//将请求体内容读取到bytes中
inputStream.read(bytes);
//将bytes转换为字符串返回
return new String(bytes, StandardCharsets.UTF_8);
}
@WebServlet("/jsonParameter")
public class JsonParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
//读取请求体内容
String body = readBody(req);
//将请求体内容转换为JavaBean对象
UserInfo userInfo = new ObjectMapper().readValue(body, UserInfo.class);
//输出解析结果
resp.getWriter().write("username: " + userInfo.username);
resp.getWriter().write("password: " + userInfo.password);
}
}
Cookie
-
Cookie 是什么?
Cookie 是浏览器提供的在客户端存储数据的一种机制(由于浏览器禁止了网页中的代码直接访问本地磁盘的文件,因此想要在网页中实现持久化存储,就可以通过 Cookie 这样的机制)
-
Cookie 里面存什么?
Cookie 存储的数据都是程序员自定义的,存储的数据是一个字符串,是键值对结构的,键值对之间使用 ; 分割,键和值之间使用 = 分割
-
Cookie 从哪里来?
服务器返回响应的时候,可以把要在客户端保存的数据以 Set-Cookie 这个 header 的方式返回给浏览器
-
Cookie 到哪里去?
客户端下次访问服务器的时候,就会把之前保存好的 Cookie 再发送给服务器
-
Cookie 的典型应用场景:
可以使用 Cookie 来保存用户的登录信息。比如我们登录过某个网站后,下次登录时就不需要重新输入用户和密码了
-
Cookie 的缺陷:
每次请求都要把该域名下所有的 Cookie 通过 HTTP 请求传给服务器,因此 Cookie 的存储容量是有限的。
Session
在计算机中,尤其是在网络应用中,Session 称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。注意会话状态仅在支持 Cookie 的浏览器中保留。
session的本质
会话的本质就是一个哈希表,其中存储了一些键值对结构,key 叫做 sessionId,是一个不随机的、不重复的、唯一的字符串,value 就是要保存的身份信息,通过 HttpSession 对象来保存。key 和 value 都是 Servlet 自动创建的。
每个用户登录都会生成一个会话,服务器会以哈希表的方式将这些会话管理起来
一个会话的详细数据通过一个 HttpSession 对象来存储,并且 HttpSession 对象中存储的数据也是键值对结构,key 和 value 都是程序员自定义的
-
当用户成功登录之后,服务器在 Session 中会生成一个新的记录,并把 sessionId 返回给客户端(例如 HTTP 响应中可以通过 Set-Cookie 字段返回,其中 Cookie 的 key 为 “JSESSION”,value 为服务器生成的 sessionId 的具体的值)
-
然后客户端只需要保存这个 sessionId ,当后续再给服务器发送请求时,请求中就会带上 sessionId(例如 HTTP 请求中会带上 Cookie 字段用于传递 Session)
-
服务器收到请求后,就会根据请求中的 sessionId 在 Session 中查询对应用户的身份信息,在进行后续操作
Servlet实现登录测试
前端页面login.html使用form表单发送数据,servlet接收后进行操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form action="/servlet_test/login1" method="post">
<input type="text" placeholder="用户名" name="username"/>
<input type="password" placeholder="密码" name="password"/>
<input type="submit" value="登录">
</form>
</body>
</html>
@WebServlet("/login1")
public class loginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null || username.isEmpty() || password == null || password.isEmpty()){
resp.getWriter().write("<h3>用户名或密码不能为空</h3>");
return;
}
if (!username.equals("root") && !password.equals("root")){
resp.getWriter().write("<h3>用户名或密码错误</h3>");
return;
}
if (username.equals("root") && password.equals("root")){
// 登录成功后创建session
HttpSession session = req.getSession(true);
session.setAttribute("visitCount", 0);
// 登录成功后跳转页面
resp.sendRedirect("index");
}
}
}
@WebServlet("/index")
public class LoginSuccessServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
HttpSession session = req.getSession(false);
if(session == null){
// 如果为空重定向到登录页面
resp.sendRedirect(req.getContextPath() + "/login.html");
return;
}
// 登录成功后获取session中的数据
Integer visitCount = (Integer) session.getAttribute("visitCount");
visitCount += 1;
session.setAttribute("visitCount", visitCount);
resp.getWriter().write("登录成功,欢迎回来!您已经登录" + visitCount + "次!");
}
}
文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<!--通过 form 表单构造上传文件,要加上一个 enctype 字段,
它表示 body 中的数据格式,它的默认值为:x-www-form-urlencoded,
这里要修改成:multipart/form-data,它是上传文件或者图片的数据格式-->
<form action="/servlet_test/uploadFile" method="post" enctype="multipart/form-data">>
<input type="file" name="file1">
<input type="submit" value="上传">
</form>
</body>
</html>
@MultipartConfig
@WebServlet("/uploadFile")
public class uploadFileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
Part file1 = req.getPart("file1");
String fileName = file1.getSubmittedFileName();
String contentType = file1.getContentType();
long size = file1.getSize();
System.out.println("文件名:" + fileName + ",文件类型:" + contentType + ",文件大小:" + size + "字节");
file1.write("C:\\Users\\86152\\Desktop\\" + fileName);
resp.getWriter().write("文件上传成功");
}
}
传文件操作还需要给 Servlet 加上一个 @MultipartConfig 注解,否则服务器代码无法使用 getPart() 方法