前言
出于提升HTTP传输效率考虑,HTTP协议是无状态的。
HTTP不会记录每次请求的状态,这就造成了同一个会话中的2个请求之间相互独立,彼此之间没有任何联系。
本文将基于servlet的session机制结合JSP,实现1个用户登录的Web应用,深入理解一下Java Web应用的核心架构。
一、会话保持技术
在Web应用中的1次会话中,包含多次HTTP请求和响应。
会话技术是针对HTTP请求无状态的特性,在一次会话中(多次HTTP请求和响应之间)保存数据 。
Cookie和Session是2种HTTP请求会话保持技术,其中Session机制基于Cookie机制。
二、Cookie
Cookie信息保存在客户端(浏览器),信息大小限制在4KB,其机制如下。
- 客户端请求服务器,服务器通过HTTP响应头中set-Cooke键告诉客户端保存Cookie。
- 浏览器接收到服务器回写Cookie要求,通过键值对的形式,在本地设置Cookie,键重复就覆盖。
- 每次浏览器请求服务器之前都会自动携带上浏览器本地的Cookie信息,除非使用Ajax向服务器发送请求。
- 服务器可以通过客户端携带的Cookie校验当前客户端并保存HTTP请求状态。
1.添加cookie信息
//2.创建明文Cookie信息
Cookie cookie = new Cookie("product1", product);
//2.1创建加密Cookie信息
Cookie encodecookie = new Cookie("product2", URLEncoder.encode("爱我中华","utf-8"));
//3.将Cookie返回给浏览器
response.addCookie(cookie);
response.addCookie(encodecookie);
2.获取cookie信息
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
//获取明文cookie
if ("product1".equals(cookie.getName())) {
System.out.println(cookie.getValue());
}
//获取加密cookie
if ("product2".equals(cookie.getName())) {
System.out.println(URLDecoder.decode(cookie.getValue(), "utf-8"));
}
}
}
3.设置cookie有效期
cookie默认实效时间是浏览器关闭之后,也可以指定cookie的实效时间,时间单位为秒。
//2.创建明文Cookie信息
Cookie cookie = new Cookie("product1", product);
//设置明文cookie的失效时间为5分钟之后
cookie.setMaxAge(300);
//2.1创建加密Cookie信息
Cookie encodecookie = new Cookie("product2", URLEncoder.encode("爱我中华","utf-8"));
//设置密文cookie的失效时间为3分钟之后
cookie.setMaxAge(180);
三、Session
Session基于Cookie,保存在服务器端,Session可以在一次会话的多次请求之间共享数据;
Sessione有以下特点:
- Session存储数据在服务器
- Session存储类型任意(Object)
- Session存储大小和数量没有限制(相对于内存)
- Session存储相对安全
Session机制如下:
- 1. 浏览器向服务器发送一个添加购物车请求
- 2. 服务请创建一个Session
- 3. 通过程序向Session中添加商品信息
- 4. 服务器通过response将session的唯一标识JSESSIONID写回到浏览器的Cookie中(通过Set-Cookie响应头)
- 5. 当浏览器发送查询请求的时候,会将cookie中的JSESSIONID信息携带到服务器中(通过Cookie请求头)
- 6. 服务器接收到请求之后,通过session的唯一标识定位到session
- 7. 我们就就可以通过程序从session获取信息了
1. 设置session信息
void setAttribute(String name,Object value)
//2.获取session对象,并将session信息保存到session对象
HttpSession session = request.getSession();
session.setAttribute("product", product);
2. 获取session信息
Object getAttribute(String name)
HttpSession session = request.getSession();
String product =(String) session.getAttribute("product");
3. 删除session信息
void removeAttribute(String name)
session.removeAttribute("product");
四、JSP技术
TCP协议是工作在传输层的数据传输管道,HTTP协议工作在应用程, 通过传输管道在浏览器和服务器之间传输字符串信息。
在Java Web技术中我们可以以下技术构建1个Web应用。
- Tomcat接收和响应浏览器的请求和响应, 通过路由锁定1个Servlet并执行。
- Servlet根据户端请求信息,操作数据库,获取数据,封装到域对象中,转发(forward)到1个.jsp页面。(动态数据准备)
- JSP把获取到域对象中封装好的动态数据,渲染到HTML,完成动态数据和静态数据的整合。 (动态数据渲染到HTML)
- Tomcat把整合好的动态数据+HTML静态数据,一同响应给客户端浏览器。
JSP(Java Server Pages)是SUN公司在Servlet的基础上推出的一种技术,它将Java代码和HTML语句混合在同1个.jsp文件中编写。
1个JSP页面由以下2部分构成
- HTML语句 (输出静态部分)
- Java代码 (输出动态部分)
1.JSP入门案例
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP案例</title>
</head>
<body>
<%--静态部分--%>
欢迎访问本网站,当前时间为
<%--动态部分--%>
<%
Date date = new Date();
String formatDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
out.write(formatDate);
%>
</body>
</html>
JSP入门案例
2.JSP执行流程
JSP本质上就是也是个Servlet,当我们通过浏览器访问1个js页面或者通过1个servlet转发(forward)到1个jsp页面时,Tomcat会经过以下流程,把jsp页面转换成1个Servlet。
Tomcat-------》Web.xml-------》.jsp页面--------》Java代码Servle-------》.Class文件-------》在JVM中运行.Class文件。
详细流程如下:
- 客户端发出请求
- web容器将jsp转化为servlet代码(.java)
- web容器将转化为servlet代码编译(.class)
- web容器加载编译后的代码并执行
- 将执行结果响应给客户端
3.JSP的EL表达式
JSP中的EL(Expression Language)表达式语言,主要用来简化jsp中对java代码的操作,帮我们从内置的域对象中获取数据在前端显示出来。
语法:${表达式}
3.1.域对象
上一篇文章得知Tomcat都会创建request对象和session对象,借助request和session对象进行数据保存;
在jJSP中我们可以使用EL表达式,从这些对象中获取数据 ,由于数据作用的范围(域)不同,在jsp中称这些封装了servlet数据的对象,为内置域对象。
内置域对象分别如下
- page: 当前页面有效
- request: 当前请求有效
- session: 当前会话有效
- application:当前应用有效
EL表达式获取数据时,会依次从这4个域中寻找,直到找到为止。
而这四个域对象的作用范围如下图所示
3.1.获取内置域对象中封装的对象数据
从对象获取数据使用点(对象.属性);
获取精准域中数据
- ${requestScope.键名} 从HttpServletRequest域中获取
- ${sessionScope.键名} 从HttpSession域中获取
简化写法
- ${键名} 按照上面的域范围从小到大依次从四个域中进行查找;如果从某一个域中找到了,立即返回;如果四个域中都没有,则返回空
<h3>获取域对象中存储的对象</h3>
user对象:${user}<br>
user对象name:${user.name}<br>
user对象age:${user.age}<br>
3.2.获取内置域对象中其他数据类型的数据
<h3>获取域对象中存储的List</h3>
List的集合:${userList}<br>
List的集合第1个元素:${userList[0]}<br>
List的集合第2个元素:${userList[1]}<br>
4.JSP标准标签库
Java server pages standarded tag library,即JSP标准标签库。
JSP标准标签库可以在jsp中实现Java的流程控制(判断+ 循环遍历)。
<%--使用jstl,必须加上这行--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
4.1导入jar包
4.2.if标签
if标签用于单分支条件判断,相当于Java中的`if(条件){}`的用法,它的常见用法如下:
<c:if test="条件">
条件成立的逻辑
</c:if>
注意: 它只有if的功能,没有else的功能,如果需要else,需要自己取反操作
4.3.forEach标签
forEach用于循环遍历,相当于java中的for关键字,它的常见属性如下:
begin:设置循环的开始
end:设置循环的结束
var:声明循环过程中的临时变量名字
step:设置步长——间隔几次循环,执行一次循环体中的内
items:指定要循环的对象
varStatus:保存了当前循环过程中的信息(循环的开始、结束、步长、次数等)
forEach标签的主要用法有下面两种:
4.3.1. 增强for循环
<c:forEach items="数据集合" var="x">
${x}
</c:forEach>
4.3.2. 普通for循环
<c:forEach begin="1" end="10" step="1" var="x">
${x}
</c:forEach>
5.Servler+JSP实现用户登录案例
package com.zhanggen.servlet;
import domain.Person;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
HttpSession session = request.getSession();
String sessionCode = (String) session.getAttribute("code");
String username = request.getParameter("username");
String password = request.getParameter("password");
String code = request.getParameter("code");
if (!"zhanggen".equals(username) || !"123.com".equals(password)) {
response.getWriter().write("用户名/密码错误");
return;
}
if (!"".equals(sessionCode) && !"".equals(code) && code.equals(sessionCode)) {
ArrayList<Person> friendsList = new ArrayList<>();
friendsList.add(new Person("Lisa", 36, "河南.安阳"));
friendsList.add(new Person("Cindy", 26, "内蒙古.乌兰察布"));
friendsList.add(new Person("Vici", 22, "辽宁.沈阳"));
friendsList.add(new Person("Andrea", 29, "河南.郑州"));
friendsList.add(new Person("Wilsion", 36, "四川.绵阳"));
friendsList.add(new Person("张无忌", 36, "河北.秦皇岛"));
request.setAttribute("username", username);
request.setAttribute("friendsList", friendsList);
///WEB-INF/下的资源,只能通过请求转发进行访问
request.getRequestDispatcher("/index.jsp").forward(request, response);
return;
}
response.getWriter().write("验证码错误");
}
}
LoginServlet
----------------------------------------------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用jstl,必须加上这行--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>微信</title>
</head>
<body>
<h1>欢迎${username}来到微信</h1>
<table style="border: aqua solid 2px">
<thead>
<tr>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
<td>地址</td>
</tr>
</thead>
<tbody>
<c:forEach items="${friendsList}" var="friend" varStatus="status">
<c:if test="${status.count%2 ==0}">
<tr style="color: antiquewhite">
<td>
${status.count}
</td>
<td>
${friend.name}
</td>
<td>
${friend.age}
</td>
<td>
${friend.address}
</td>
</tr>
</c:if>
<c:if test="${status.count%2 !=0}">
<tr style="color: blue">
<td>
${status.count}
</td>
<td>
${friend.name}
</td>
<td>
${friend.age}
</td>
<td>
${friend.address}
</td>
</tr>
</c:if>
</c:forEach>
</tbody>
</table>
</body>
</html>
index.jsp