JSP + Session Cookie详解
JSP概述
掌握了servlet后,就可以利用servlet来开发动态页面了,但是使用Servlet开发动态页面,存在种种问题,来看下面的例子:
使用Servlet来开发百度首页:
分析上面的案例不难发现以下问题:
Html与Java代码混合在一起,维护困难 |
---|
每一行都是一个println语句,效率低下 |
编译器无法检测问题,调试麻烦 |
JSP概念
全称JavaServerPage服务器页面,为了使开发动态页面更加简单而出现,本质上也是一个Servlet
动态与静态
一个页面如果不会随着任何条件(时间,用户信息...)改变而发生变化,那它就是静态的,静态页面通常只能提供最基本的信息展示
动态页面是会随着访问时的时间,地点,提交的数据不同而展示不同的内容,它就是动态页面,例如可以根据登录用户的不同而展示的不同的购物信息,通常页面的数据来自于数据库
特点:
-
JSP可将原本都在Servlet中的java代码与HTML分离,降低耦合,可维护性好
-
可编写原生HTML,且编译器会进行语法检查,开发效率更高
-
JSP本质就是Servlet,被执行前会被先转译为java文件
-
注意:JSP文件需放在web(webContent)资源目录下,后缀为.jsp
jsp执行过程
示例:
转译后的java文件解析
找到转译后的java文件
打开该文件
可以看到其原理与servlet完全相同,利用printWriter来向前台返回响应数据
JSP语法
表达式
语法格式:<%=表达式%>
作用:通过表达式可以输出表达式的结果值。其本质就是执行了print语句;
示例:
注意:表达式中不能有分号,只能是一行
代码块
语法格式:<%代码内容%>
作用:编写任何Java代码,通常是用来输出产生HTML文本内容的,这是与普通HTML最大的区别,有了代码块你可以很轻松的实现动态页面
示例:
声明块
语法格式:<%!代码内容%>
作用:在JSP中声明方法和变量
示例:
注释
语法:
<%-- jsp注释 --%> 页面不可见
<!-- html注释 --> 页面可见
// /**/ 代码块内java注释 转译jsp时会放到java文件中
指令
编译指令
编译指令,用于处理当前jsp全局配置,例如导入Java类,使用标签库,或内容编码等,由Servlet引擎处理,在JSP转译Servlet时生效
通常位于JSP文件开始的地方,这样可以保证要使用的资源已经被导入
指令名 | 作用 |
---|---|
include | 静态引入其他JSP页面 |
taglib | 导入标签库,设置标签前缀等 |
page | 导入Java类,设置响应编码等 |
语法:<%@ page 属性名称="属性值">
示例:<%@ page import="java.util.HashMap" %>
动作指令
动作指令通常可以被JSP脚本替代,是对JSP脚本的HTML标准化写法
指令名 | 作用 |
---|---|
jsp:forward | 执行页面转向,将请求的处理转发到下一个页面/servlet。 |
jsp:param | 用于传递参数,必须与其他支持参数的标签一起使用。 |
jsp:include | 用于动态引入一个JSP页面。 |
jsp:plugin | 用于下载JavaBean或Applet到客户端执行。 |
jsp:useBean | 创建一个JavaBean实例。 |
jsp:setProperty | 设置JavaBean实例的属性值。 |
jsp:getProperty | 输出JavaBean实例的属性值。 |
语法:<jsp:动作名称 名称="值">
示例<jsp:forward page="/FirstServlet"></jsp:forward>
JSP语法汇总
Cookie
由于HTTP协议状态无连接的特性,造成了用户状态无法维持的问题,客户端前脚刚刚登陆成功,后脚就要重新登陆,毫无体验,为了解决这个问题,出现了Cookie,以及后来的session
- Cookie是服务器保存在浏览器的一小段文本数据(4k内),通常与用户个人信息有关
- cookie数据以键值对形式存在
- 用于在HTTP协议下维持客户端与服务器的状态
- cookie具有时效性,在有效期内
- 每次请求相同的服务器(域名相同,路径相同)时,浏览器都会自动将cookie放入请求头一起发送给服务器
图解:
Cookie相关方法
方法名称 | 作用 |
---|---|
Cookie(key,value) | cookie的构造函数。 |
setMaxAge() | 设置cookie的最大存活时间。 |
getValue(key) | 获取对应key的cookie值。 |
setValue(key,newValue) | 修改对应key的cookie的值。 |
response.addCookie(cookie) | 往响应中添加对应key的cookie对象。 |
利用Cookie来实现7天免登陆
用于处理登录请求的LoginServlet:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//登录判断逻辑 (实际开发中从请求中获取参数匹配数据库)
System.out.println("login success");
//创建Cookie对象
Cookie c = new Cookie("user","admin");
//设置cookie有效期
c.setMaxAge(60*60*24*7);
//添加cookie到响应中
response.addCookie(c);
}
}
用于作为用户主页的IndexServlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "IndexServlet",urlPatterns = "/IndexServlet")
public class IndexServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求中所有cookie
Cookie[] cs = request.getCookies();
//判断是否有cookie
if(cs!=null){
String name = null;
//遍历所有cookie
for (Cookie c: cs) {
//找到用户名称
if(c.getName().equals("user")){
name =c.getValue();
}
}
//在页面输出已经登录过的用户名称
response.setContentType("text/html;charset=utf-8");
if(name != null){
response.getWriter().println("user:" + name);
}
}
}
}
在浏览器中先访问LoginServlet,重新启动浏览器,访问IndexServlet可以看到之前登录的用户信息
注意:
如果没有设置最大有效期,cookie则是临时的浏览器进程关闭就消失了
Cookie解决了客户端与服务器需要维持状态的问题,但也带来了新的问题
- cookie是存储在客户端的磁盘上的,虽然经过加密,但是也有被破解和盗用的风险
- 每次请求都需要附带cookie,cookie数据较多时造成较大的网络开销
就像你把现金放在家里每次买东西都必须带着现金去一样
为了解决上面的问题,而出现了session
Session
Session(会话),字面上理解,它是一个抽象的概念,描述浏览器和服务器在一段时间内的所有交互动作
本质上:
-
session用于保存与浏览器进程对应的数据
-
数据存储在服务器内存中,具有时效性,默认为30分钟,可通过代码或web.xml修改
-
服务器从Cookie中获取sessionID来找到对应的session对象,从而实现维持用户的状态
-
session在服务器是一个Java对象,可以添加任何类型的属性到其中
-
当session在一段时间内没有被访问时将被销毁
图解:
注意:如果请求的是JSP将自动创建session对象
session常用方法
方法名称 | 作用 |
---|---|
session.setAttribute(key,value); | 为session增加额外的属性,用于传值 |
session.getAttribute(key); | 从session中取出指定的属性 |
request.getSession(boolean); | 获取当前session对象,如果没有是否创建新的 |
session.getId(); | 获取ID |
session.setMaxInactiveInterval(); | 设置最大超时时间单位为秒 |
session.getMaxInactiveInterval(); | 获取最大超时时间 |
session.getCreationTime(); | 获取创建时间 |
session.getLastAccessedTime(); | 获取当前会话最后一次请求时间 |
session.invalidate(); | 使session立即失效 |
利用session来实现登录状态同步
处理登录请求的LoginServlet:
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 java.io.IOException;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//登陆了验证逻辑
System.out.println("login success!");
//获取session并 添加用户信息到session中
req.getSession().setAttribute("user","张三");
}
}
用于显示用户主页的IndexServlet:
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;
@WebServlet("/IndexServlet")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session
HttpSession session = req.getSession();
//取出用户信息
String name = (String) session.getAttribute("user");
//设置响应编码并输出用户名称
resp.setContentType("text/html;charset=utf-8");
if(name != null){
resp.getWriter().println("user is :"+name);
}else{
resp.getWriter().println("user not login!");
}
}
}
登录成功后进入IndexServlet可以看到用户信息
但是重启浏览器时用户信息就没有了
注意:
浏览器进程结束时sessionID就消失了,但这并不意味着服务器对应的session对象立即会销毁,如果没有调用session.invalidate
方法session将达到超时时间后才会销毁,这意味着大量的session将导致资源耗尽
web.xml配置超时时间:
JSP内置对象
JSP本质还是Servlet,只是对其进行了简化封装,为了方便在JSP中进行操作,JSP提供了经常需要使用到的对象
本质:
打开一个转移后的java文件,找到79行左右可以看到一个非常熟悉的方法_jspService,尽管前面多了__jsp
上图中圈出了9个JSP的内置对象,大多数都是在Servlet中见过的,除了下面的
-
page,是当前jsp对象实例的this引用
-
pageContext,页面上下文,页面的的内置对象都来自于该对象,它是JSP对象操作web资源对象的中转站(jsp本身并不是Servlet)
-
exception对象仅在错误页面中可用,即用于展示错误信息的页面
错误页面
当服务器处理过程产生异常或是,请求不正确是,tomcat会显示自带的最基础的错误页面,通常需要进行定制
1.编写错误页面,利用page指令,将这个jsp设置为错误页面
2.在web.xml中配置错误页面,指定要处理的状态码和对应的页面
3.也可以针对某个特殊异常作处理,优先匹配异常类,然后匹配状态码
4.在某个servlet中模拟异常
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
throw new ArrayIndexOutOfBoundsException("索引越界了!");
}
页面内容:
注意:
400+的错误页面无法使用jsp来完成,因为400+错误通常都是HTTP协议问题,请求是不正确的,服务器不会产生任何相关的对象
JSP中的四个作用域
作用域值得就是有效范围,在概念上与大家知道的局部作用域,全局作用域一个意思,
但是这里不是自定义的作用域,而是jsp已经提供了的作用域
四个作用域:
名称 | 描述 |
---|---|
page | PageContext页面上下文,在当前页面有效 |
request | 对同一个请求有效,(请求转发是同一个,重定向是新的) |
session | 当前会话有效 |
application | ServletContext,当前web应用程序全局有效 |
用来做什么?
在不同servlet或jsp中切换传参,是非常常见的操作,而要传参就必须借助上述4个对象
学习作用域的目的是为了合理的选择最合适的对象来存储数据,从而节省服务器资源开销
作用域范围图解
老司机经验:能用小的就别用大的
EL表达式
使用代码块可以利用java代码,来操作对象的属性,展示数据等,但是对于简单频繁的数据展示操作,使用代码块或者表达式都会导致html整体结构变得混乱,
那动作指令呢?,虽然语法近似html标签,但还是不够简洁,于是便有了EL表达式
EL概念
EL(Expression Language) 是为了使JSP写起来更加简单。它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。
简单的说,EL就是要在不影响页面结构的情况下没用简洁的语法来对作用域中的数据进行存取,并提供了完善的运算符支持,
基本语法:
${对象名称.属性} 获取对象属性
${对象名称.方法(参数)} 调用对象方法
${参数1 表达式 参数2} 使用运算符
EL包含的内容:
作用域访问
自动投影,EL会从4个作用域找自动查找匹配的属性名称,从小到大查找
//测试代码
${requestScope.user="jerry2"}
${applicationScope.user="jerry4"}
${pageScope.user="jerry1"}
${sessionScope.user="jerry3"}
<h1>${user}</h1>
运算符
https://tva1.sinaimg.cn/large/006tNbRwgy1g9tz7scwisj30p606r752.jpg
隐式对象
https://tva1.sinaimg.cn/large/006tNbRwgy1g9tz8lnbslj30qh0gkwik.jpg
注意:在使用EL存取对象属性时必须保证对象提供了相应的get/set方法
示例:
JSTL
有了EL我们可极大的减少在页面中插入java代码块的情况,但是EL是表达式,这意味着其无法提供流程控制能力,即(分支,循环)
于是在广大程序员的吐槽中,JAVAEE又推出了JSTL标准
概述:
JSTL(JSP Standard Tag Library,JSP标准标签库)是一个不断完善的开放源代码的JSP标签库,是由apache的jakarta小组来维护的。其使命是使用标签来替换JAVA的流程控制,最终实现JSP中完全或尽量不包含JAVA代码
注意:如果要使用JSTL,则必须将jstl.jar和 standard.jar文件放到classpath中。
下载地址:
http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
IDEA下配置步骤:
使用案例:
<%--
Created by IntelliJ IDEA.
User: jerry
Date: 2019/12/12
Time: 4:12 下午
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--输出1-10之间的偶数
st表示循环状态 可获取当前循环的索引等信息
var 表示每次遍历的临时变量
--%>
<c:forEach begin="1" end="10" varStatus="st" var="num">
<c:if test="${st.index % 2 ==0}">
<h1>${num}是偶数啊</h1>
</c:if>
</c:forEach>
<%--重定向--%>
<%--<c:redirect url="404.html"></c:redirect>--%>
<%--输出--%>
<c:out value="hello world"></c:out>
<%--添加属性到某个scope--%>
<c:set scope="request" var="number" value="a"></c:set>
<%--选择结构--%>
<c:choose >
<c:when test="${number=='as'}">
<c:out value="你选择a了"/>
</c:when>
<c:when test="${number=='b'}">
<c:out value="你选择b了"/>
</c:when>
<c:otherwise>
<c:out value="不是a和b"/>
</c:otherwise>
</c:choose>
</body>
</html>
以后就用JSP做动态页面吗?NO
看看动态页面的技术发展吧