JavaWeb-JSP

目录

1 分析使用存粹的Servlet开发web应用的缺陷

2 JSP原理解析

3 JSP基本语法

4 Servlet和JSP改造oa项目

5 Servlet和JSP结合改造oa项目后的代码

6 session机制

6.1 session机制概述

6.2 session机制原理

7 使用session解决oa项目的登录问题

8 退出系统

9 Cookie原理

10 使用cookie实现十天内免登录功能

11 JSP指令


1 分析使用存粹的Servlet开发web应用的缺陷

这就需要再Servlet当中编写html/css/JavaScript等前端代码,在java中编写前端代码:难度大、麻烦、耦合度高、不美观、维护成本高等等问题。

思考一下,在Servlet中写前端代码最麻烦的是,前端代码在Servlet中是放在out.print("");中的,是以字符串的形式写进去的,然后out.print()输出就会以前端的形式输出。这样的字符串是很难写的,所以是否存在这样一种工具,不需要再写Servlet了,我们只需要写这个Servlet程序中的前端代码,然后这个工具会将我们写的前端代码自动翻译成Servlet这种java程序,然后在自动将java程序编译成class文件,最后再使用jvm调用这个class中的方法。确实存在,这就是JSP。

2 JSP原理解析

我的第一个JSP程序:

在WEB-INF目录之外创建一个index.jsp文件,这个文件中没有任何内容。

启动服务器,访问这个jsp,地城执行的是index_jsp.class这个java程序。实际上,当我们执行这个jsp文件时,这个文件会被tomcat翻译生成index_jsp.java文件,然后会将这个Java文件编译成index_jsp.class文件。访问index.jsp实际上就是执行index_jsp.class。那么实质上index_jsp就是一个类,index_jsp类继承HttpJspBase,而HttpJspBase类继承HttpServlet,所以index_jsp类就是一个Servlet,并且jsp的声明周期和Servlet生命周期完全相同。

那我到底是怎么得出这些呢,可以打开文件夹查看一下:在idea输出端找到CATALINA_BASE这个目录,在电脑中打开,当执行jsp文件的时候就会生成下面两个文件:

 我们打开index_jsp.java查看:发现这个类继承HttpJspBase

我们再在tomcat源码中找到这个类查看:发现它确实继承的HttpServlet。

jsp文件第一次访问的时候比较慢

大部分的运维人员在给客户演示项目的时候,都要提前把所有的jsp文件夹先访问一遍。因为第一次访问要先把jsp文件翻译成java源文件,然后再把java源文件编译成class文件,然后通过class创建servlet对象,然后调用servlet对象的init方法,最后调用service方法。

第二次就比较快了,因为第二次直接调用单例servlet对象的service方法即可.

JSP是什么

  • JSP本质上就是一个servlet,一个Java程序。Servlet是javaEE的13个子规范之一,JSP也是。
  • 开发JSP的最高境界:眼前事JSP代码,脑袋呈现的是Java代码。
  • 对JSP进行错误调试的时候,还是要打开JSP文件对应的java文件,检查java代码

JSP既然本质上是Servlet,那么JSP和Servlet到底有什么区别呢?

职责不同:Servlet的职责是什么:收集数据(逻辑处理,业务处理,连接数据库,收集数据)

                  JSP的职责:展示数据(做数据展示,也就是更擅长写前端代码)

3 JSP基本语法

在jsp编写文字,都会被翻译到哪里?会被翻译到Servlet类的service方法的out.write("翻译到这里"),直接翻译到双引号里,被java程序当做普通字符串打印输出到浏览器。其实这操作和我们之前的操作差不多,我们之前用到的是out.print,不过这里它可以自动帮我们翻译,不需要我们一点点写了。

下面随便在jsp文件写点东西:

找到jsp文件翻译成的java文件,查看信息,果然被自动加了out.write

 

JSP的page指令解决响应时中文乱码问题:<%@ page contentType="text/html;charset=UTF-8" %>

怎么在JSP中编写Java代码:

  • <% java语句; %>
  • 在这个符号中编写java代码,被翻译到Servlet类的service方法内部,在写代码时要思考在这个符号里面写代码就是在“方法体”里面写代码,方法体可以写什么不可以写什么,心里是否明白。
  • 方法体中编写代码要遵循自上而下的顺序
  • service方法当中不能写静态代码块,不能写方法,不能定义成员变量等等
  • 在同一个JSP当中<%%>这个符号可以多次出现

<%!%>:这个符号当中编写的java程序会自动翻译到service方法之外。这个方法很少用,因为在service方法外面编写静态变量和实例变量都会存在线程安全问题。

在JSP中如何编写JSP专业注释: <%--这是JSP专业注释--%>,这样的注释不会翻译到java源代码中。

怎么向浏览器输出一个java变量:

  • 我们发现在jsp自动生成的Servlet文件中,输出信息使用的是out.write,因此我们可以在<%%>里面使用这个语句来向浏览器输出java信息。
  • 注意:这里的out就是jsp的九大内置对象之一,可以直接拿来用,但必须只能在service方法内部使用。
  • 如果向浏览器输出的是固定的字符串,可以直接写,不需要写在<%%>中
  • 如果输出的内容有java代码可以使用这个语法格式:<%= %> 在等号后编写输出的内容,这个符号最终会被翻译成out.print();
  • 什么时候使用<%= %>呢,输出的内容有java变量,是一个动态的内容时。

4 Servlet和JSP改造oa项目

首先创建一个新的模块,将之前写好的html文件改成jsp文件,并加上page信息避免出现乱码,复制到web目录下。然后我们开始改造。首先打开首页index.jsp,我们发现这里用的路径是list.html,肯定是不对的,改成jsp,但好像仍然不够完美。因为前端发送请求时,我们最好加上项目名,那我们加上“/oa3”,但好像还是不完美。因为我们不喜欢将项目名写死,动态获取是最好的,使用<%=request.getContextPath()%>获取项目名。由此我们也发现了jsp的好处,可以写java代码,让项目更完美。

接下来将其它jsp文件中的路径都改一下,完成页面的正常流转。

创建一个Servlet,和上一个oa项目一样,只创建一个Servlet,然后不同的功能通过调用方法来实现。

现在有个问题是,如何将Servlet中得到的信息传到jsp???可以先将数据放到请求域,然后转发到jsp文件 。在咋混发数据之前先创建一个javabean用来封装数据并且使用集合来装更多的数据。传到jsp文件时就可以得到请求域中的数据然后输出,不过发现这样仍然很麻烦,后续还会学更方便的方法。

其实解决了将Servlet中得到的信息传到jsp的问题,这个项目就差不多可以完成了。

实现登录功能

  • 步骤一:数据库中添加一个用户表:t_user。里面存储用户登录信息(用户名和密码),密码一般在数据库表当中存储的是密文,不是明文,因为以防被管理员做不好的事情(本次先用明文)
  • 步骤二:实现登录界面,一个登录的表单,用于输入用户名和密码
  • 步骤三:后台要有一个Servlet来处理登录的请求,成功跳转到部门列表页面,失败跳转到失败的页面。
  • 步骤四:提供一个登陆失败页面。

5 Servlet和JSP结合改造oa项目后的代码

首先项目目录结构:

add.html

<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>新增页面</title>
	</head>
	<body>
		<h1>添加信息</h1>
		<hr >
		<form action="<%=request.getContextPath()%>/dept/save" method="post">
			部门编号<input type="text" name="dno" /><br>
			部门名称<input type="text" name="dname" /><br>
			部门位置<input type="text" name="loc" /><br>
			<input type="submit" value="添加" />
		</form>
	</body>
</html>

detail.html

<%@ page import="com.itzw.oa.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" %>

<%
	//获取请求域数据
	Dept d = (Dept)request.getAttribute("dept");
	String deptno = d.getDeptno();
	String dname = d.getDname();
	String loc = d.getLoc();
%>

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>详情信息</title>
	</head>
	<body>
		<h1>详情信息</h1>
		<hr >
		部门编号:<%=deptno%><br>
		部门名称:<%=dname%><br>
		部门位置:<%=loc%><br>
		<input type="button" value="后退" onclick="window.history.back()" />
	</body>
</html>

edit.html

<%@ page import="com.itzw.oa.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" %>

<%
	//拿到请求域数据
	Dept dept = (Dept)request.getAttribute("dept");
	String deptno = dept.getDeptno();
	String dname = dept.getDname();
	String loc = dept.getLoc();
%>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>修改信息</title>
	</head>
	<body>
		<h1>修改信息</h1>
		<hr >
		<form action="<%=request.getContextPath()%>/dept/modify" method="post">
			部门编号<input type="text" name="dno" value="<%=deptno%>" readonly /><br>
			部门名称<input type="text" name="dname" value="<%=dname%>"/><br>
			部门位置<input type="text" name="loc" value="<%=loc%>"/><br>
			<input type="submit" value="修改" />
		</form>
	</body>
</html>

error.html


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
登录失败,请<a href="index.jsp">重新登录</a>
</body>
</html>

index.html

<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>欢迎页面</title>
	</head>
	<body>
		<%--<a href="<%=request.getContextPath()%>/dept/list">查看部门列表</a>--%>

		<%--<%=request.getContextPath()%>--%>

		<h1>用户登录</h1>
		<hr>

	<form action="<%=request.getContextPath()%>/user/login" method="post">
		用户名:<input type="text" name="username"><br>
		密&nbsp;&nbsp;码:<input type="password" name="password"><br>
		<input type="submit" value="提交">
	</form>
	</body>
</html>

list.html

<%@ page import="java.util.List" %>
<%@ page import="com.itzw.oa.bean.Dept" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>部门列表</title>
		<script type="text/javascript">
			function del(dno){
				var ok = window.confirm("亲,删除了不可恢复哦");
				if (ok){
					document.location.href="<%=request.getContextPath()%>/dept/delete?deptno="+dno;
				}
			}
		</script>
	</head>
	<body>
		<h1>部门信息</h1>
		<hr >
		<table border="1px" align="center" width="50%">
			<tr>
				<td>序号</td>
				<td>部门编号</td>
				<td>部门名称</td>
				<td>操作</td>
			</tr>

			<%
				int i = 0;
				//从request中获取集合
				List<Dept> deptList = (List<Dept>) request.getAttribute("deptList");
				//循环遍历
				if ((deptList != null)&&(deptList.size() != 0) ){
					for (Dept dept : deptList) {
						//在后台输出看看对不对
						//System.out.println(dept.getDname());
			%>

			<tr>
				<td><%=++i%></td>
				<td><%=dept.getDeptno()%></td>
				<td><%=dept.getDname()%></td>
				<td><a href="javascript:void(0)" onclick="del(<%=dept.getDeptno()%>)">删除</a>
					<a href="<%=request.getContextPath()%>/dept/detail?f=m&deptno=<%=dept.getDeptno()%>">修改</a>
					<a href="<%=request.getContextPath()%>/dept/detail?f=d&deptno=<%=dept.getDeptno()%>">详情</a>
				</td>
			</tr>
			<%
					}
				}

			%>


		</table>
		<hr >
		<a href="<%=request.getContextPath()%>/add.jsp">添加信息</a>
	</body>
</html>

 DeptServlet

package com.itzw.oa.web.action;

import com.itzw.oa.bean.Dept;
import com.itzw.oa.util.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@WebServlet({"/dept/list","/dept/detail","/dept/delete","/dept/save","/dept/modify"})
public class DeptServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String servletPath = request.getServletPath();
        if ("/dept/list".equals(servletPath)){
            doList(request,response);
        }else if ("/dept/detail".equals(servletPath)){
            doDetail(request,response);
        }else if ("/dept/delete".equals(servletPath)){
            doDel(request,response);
        }else if ("/dept/save".equals(servletPath)){
            doSave(request,response);
        }else if ("/dept/modify".equals(servletPath)){
            doModify(request,response);
        }

    }

    /**
     * 根据前端提交数据修改信息
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doModify(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //拿到前端提交的数据
        String dno = request.getParameter("dno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");
        //连接数据库修改数据
        Connection conn = null;
        PreparedStatement ps = null;

        int i = 0;
        //连接数据库
        try {
            conn = DBUtil.getConnection();
            String sql = "update dept set dname = ?,loc = ? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,dname);
            ps.setString(2,loc);
            ps.setString(3,dno);
            i = ps.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);
        }
        if (i == 1){
            //修改成功,重定向到list页面
            String contextPath = request.getContextPath();
            //重定向一定要记得加项目名
            response.sendRedirect(contextPath+"/dept/list");
        }


    }

    /**
     * 新增部门信息
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doSave(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取前端提交的信息
        String dno = request.getParameter("dno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");
        //连接数据库,将获取到的信息存入数据库
        Connection conn = null;
        PreparedStatement ps = null;

        int i = 0;
        //连接数据库
        try {
            conn = DBUtil.getConnection();
            String sql = "insert into dept(deptno,dname,loc) values (?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1,dno);
            ps.setString(2,dname);
            ps.setString(3,loc);
            i = ps.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);
        }

        if (i == 1){
            //新增成功,重定向到list页面
            String contextPath = request.getContextPath();
            //重定向一定要记得加项目名
            response.sendRedirect(contextPath+"/dept/list");
        }
    }

    /**
     * 根据部门编号删除部门信息
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doDel(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //获取编号
        String deptno = request.getParameter("deptno");
        //连接数据库
        Connection conn = null;
        PreparedStatement ps = null;

        int i = 0;
        //连接数据库
        try {
            conn = DBUtil.getConnection();
            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,deptno);
            i = ps.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);
        }

        if (i == 1){
            //删除成功,重定向到list页面
            String contextPath = request.getContextPath();
            //重定向一定要记得加项目名
            response.sendRedirect(contextPath+"/dept/list");
        }
    }

    /**
     * 根据编号获取部门信息
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doDetail(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //获取编号
        String deptno = request.getParameter("deptno");
        //创建对象
        Dept dept = new Dept();
        //连接数据库获取部门信息
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        //连接数据库
        try {
            conn = DBUtil.getConnection();
            String sql = "select dname,loc from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,deptno);
            rs = ps.executeQuery();

            if (rs.next()){
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");
                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,rs);
        }

        //将数据放入请求域
        request.setAttribute("dept",dept);
        //转发
        String f = request.getParameter("f");
        if ("m".equals(f)){
            //跳转到修改页面
            request.getRequestDispatcher("/edit.jsp").forward(request,response);
        }else {
            //跳转到详情页面
            request.getRequestDispatcher("/detail.jsp").forward(request,response);
        }

    }

    /**
     * 部门列表
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doList(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //创建一个集合
        List<Dept> depts = new ArrayList<>();

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        //连接数据库
        try {
            conn = DBUtil.getConnection();
            String sql = "select deptno,dname,loc from dept";
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();

            while (rs.next()){
                String deptno = rs.getString("deptno");
                String dname = rs.getString("dname");
                String loc = rs.getString("loc");
                //怎么将这个数据拿到jsp文件中呢
                //可以先将数据放到请求域,然后转发到jsp文件
                //先将数据装到对象中
                Dept dept = new Dept();
                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);
                //因为不止一个对象,所以要装到集合中
                depts.add(dept);
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,rs);
        }
        //将集合放到请求域
        request.setAttribute("deptList",depts);

        //转发
        request.getRequestDispatcher("/list.jsp").forward(request,response);
    }
}

UserServlet

package com.itzw.oa.web.action;

import com.itzw.oa.util.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@WebServlet("/user/login")
public class UserServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //先获取到前端提交的用户名和密码username,password:
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        boolean success = false;
        //连接数据库
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            String sql = "select * from t_user where username = ? and password = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,username);
            ps.setString(2,password);
            rs = ps.executeQuery();
            if (rs.next()){
                //查到数据就是登录成功
                success = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //判断是否登录成功
        if (success){
            //登录成功
            response.sendRedirect(request.getContextPath()+"/dept/list");
        }else {
            //登录失败
            response.sendRedirect(request.getContextPath()+"/error.jsp");
        }
    }
}






















bean目录下的Dept

package com.itzw.oa.bean;

public class Dept {
    private String deptno;
    private String dname;
    private String loc;

    public Dept() {
    }

    public Dept(String deptno, String dname, String loc) {
        this.deptno = deptno;
        this.dname = dname;
        this.loc = loc;
    }

    public String getDeptno() {
        return deptno;
    }

    public void setDeptno(String deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno='" + deptno + '\'' +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

util目录里的内容、resources里的内容之前有展示过,都是一样的,这就不展示了。

但我们发现了一个问题,这里的登录功能好像只是个摆设,不管有没有这个登录功能我都可以根据URL访问资源。这个问题就需要后面的学习来解决了。

6 session机制

6.1 session机制概述

什么是会话?

  • 会话对应的英语单词:session
  • 用户打开浏览器,进行一系列操作,关闭浏览器,整个过程叫一次会话。会话对应的服务器端的java对象是:session
  • 回顾:什么是一次请求:用户在浏览器上点击了一下然后页面停下来,可以粗略认为是一次请求,请求对应的服务器端的java对象是:request
  • 一次会话包含多次请求
  • 在java的Servlet规范中,session对应的类名:HttpSession
  • session机制属于B/S结构的一部分
  • session对象的主要作用是:保持会话状态。用户登录成功了,这是一种登录成功的状态,使用session对象可以保持这个登录状态。

为什么需要session对象来保存会话状态呢?

  • 因为Http协议是一种无状态协议
  • 什么是无状态协议:请求的时候,B和S是连接的,但是请求结束之后,连接就断了。为什么要这么做?因为这样可以降低服务器的压力。请求的瞬间是连接的,结束之后就会断开,这样服务器压力小。
  • 只要B和S断开了,那么关闭浏览器这个动作,服务器知道吗?不知道。

为什么不使用request对象或者ServletContext对象保存会话状态?

  • request是一次请求一个对象,request请求域太小
  • ServletContext对象是服务器启动的时候创建的,服务器关闭的时候销毁,这个ServletContext对象只有一个。ServletContext对象的域太大。
  • session对象是界于这两者中间的。

思考一下:Session对象的实现原理

  • HttpSession session = request.getSession();
  • 这行代码很神奇,张三访问的时候获取的session对象就是张三,李四访问的时候获取的session对象就是李四。这是怎么做到的呢,在张三访问的时候,张三和session对象都做了一个标记,这个标记就是他们能识别他们自己的session对象的原因,这就是cookie,这个知识后面再学。

总结一下session的存在原因

因为Http协议是无状态协议,请求结束连接就断了。但我们有时候需要让服务不断,比如登录状态,我们不想每次打开这个网页都要登录一次。所以就有了session,它可以让本次会话中的登录状态一直保存。

6.2 session机制原理

说这么废话先写个session对象看看啥样子:

HttpSession session = request.getSession();

就这一行代码就可以获取session对象,我们打开浏览器,不管访问几次都是同一个session对象。需要注意的是:session对象是存储在服务器端的。如果没有获取到任何session对象则新建。

session实现原理:

  • 在web服务器中有一个session列表,类似于map集合,key存储的是sessionid,value存储的是session对象
  • 用户第一次发送请求时,服务器会新建一个新的session对象,同时给session对象生成一个id,然后会将这个id发送给浏览器,浏览器会将这个id保存在缓存中。
  • 用户第二次发送请求时,会将缓存中的sessionid发送给服务器,服务器获取到id然后在session列表找这个id对应的session对象。

为什么关闭浏览器会话结束

因为sessionid存储在缓存中,关闭浏览器缓存会清除,当再次向服务器发送请求的时候并没有id可以发送,找不到服务器的session对象,自然就会话结束。但服务器中依然存在这个session对象,只是浏览器找不到了。

session对象什么时候被销毁

  • 一种销毁:超时销毁,可以设置超时时间,到了那个时间如果还没有对这个session对象访问就自动销毁,和浏览器关不关没关系。
  • 一种销毁:手动销毁
  • 那么如何设置超时时长?可以在web.xml文件设置,如下:
  •     <!--这里表示40分钟-->
        <session-config>
            <session-timeout>40</session-timeout>
        </session-config>

cookie被禁用了,session还能找到吗?

  • 前面说id是被存在缓存也就是cookie中,那么cookie被禁用如何呢?
  • 找不到了。每一次请求都是新的对象。
  • cookie被禁用,session机制还能实现吗?可以实现,需要使用URL重写机制,在URL后面加上;jsessionid=id全名
  • URL重写会大大提高开发成本,所以大部分网站的设计是:如果你禁用cookie,你就别用了,老子不伺候你。

总结一下目前学到的域对象:

  • request(对应的类名:HttpServletRequest):请求域(请求级别的)
  • session(对应的类名:HttpSession):会话域(用户级别的)
  • application(对应的类名:ServletContext):应用域(项目级别的,所有用户共享)
  • 它们三个域都有以下三个公共的方法:setAttribute(向域中绑定数据)、getAttribute(从域中获取数据)、removeAttribute(删除域当中的数据)
  • 使用原则:尽量使用小的域

7 使用session解决oa项目的登录问题

登录成功后,可以将用户信息存储到session当中,也就是说session当中有用户的信息就代表登录成功了,没有则表示没登录过,则跳转到登录界面

在实现登录功能的Servlet中,在成功登录后加将用户名存入session

不管访问哪个资源都要先判断session中有没有登录的用户名,有才能访问。

 可以使用下面代码做个简单的欢迎语句,不过不能直接使用session.getAttribute,不知道为啥

<h3>欢迎[<%=request.getSession().getAttribute("username")%>]</h3>

8 退出系统

这里就是手动销毁session对象

list界面加上超链接

<a href="<%=request.getContextPath()%>/user/exit">[退出系统]</a>

Servlet实现退出操作 

    private void doExit(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        //获取session
        HttpSession session = request.getSession(false);
        if (session != null){
            //手动销毁session对象
            session.invalidate();
            //跳转到登录界面
            response.sendRedirect(request.getContextPath());
        }
    }

9 Cookie原理

session的实现原理中,每一个session对象都会关联一个sessionid

  • JSESSIONID=41SHG372864246HD3219748NDJS4
  • 以上这个键值对就是cookie对象
  • 对于session关联的cookie来说,这个cookie被保存在浏览器的运行内存当中,只要浏览器不关闭,用户再次发送请求的时候,会自动运行内存中的cookie发送给服务器。服务器根据这个id值找到对应的session对象。

cookie怎么生成?cookie保存在什么地方?cookie有啥用?浏览器什么时候发送cookie,发送哪些cookie给服务器

cookie最终是保存在浏览器客户端上的。

  • 可以保存在运行内存中(浏览器关闭cookie就消失了)
  • 也可以保存在硬盘中(永久保存)

cookie有啥用?

  • cookie和session机制都是为了保存会话状态
  • cookie是将会话的状态保存在浏览器客户端上。(cookie数据存储在浏览器客户端上)
  • session是将会话状态保存在服务器上(session对象是存储在服务器上的)
  • 为什么要有cookie和session机制呢?因为HTTP协议是无状态无连接协议。

cookie经典案例

  • 京东商城,在很久之前,未登录的情况下,可以向购物车加物品,然后关闭商城,再次打开浏览器购物车的商品还在,这是怎么做到的呢?购物车的商品编号放入cookie中,cookie保存在硬盘中,这样就可以了。注意:cookie如果清除掉,购物车的商品就消失了。
  • 126邮箱有个功能:十天免登录。这是怎么实现的呢?用户输入正确的用户名和密码选择十天免登录后,浏览器客户端会保存一个cookie,这个cookie保存用户名密码等信息,保存在硬盘当中,十天有效。
  • 怎么让cookie失效:十天自动失效;或者改密码;或者在客户端清除cookie。

cookie机制和session机制都不属于java中的机制,都是HTTP协议的一部分。

HTTP协议规定任何一个cookie都是由name和value组成的。

在java的servlet中,对cookie提供了哪些技术支持?

  • 提供一个Cookie类专门表示cookie数据。jakarta.servlet.http.Cookie。
  • java程序怎么把cookie数据发送给浏览器?response.addCookie(cookie)。

在HTTP中这样规定:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器。

接下来我们在idea使用cookie感受一下:

        //new一个cookie
        Cookie cookie = new Cookie("productid","122498164512645");
        //设置cookie存活时间(单位是s),本次设置一小时
        cookie.setMaxAge(60*60);
        //响应给浏览器
        response.addCookie(cookie);

可以用Cookie对象的setMaxAge方法设置存活时间,打开浏览器可以查看:

值得注意的是:当cookie的有效时间设置为0时表示该cookie被删除,有效时间设置为负数的时候表示这个cookie不被存储到硬盘文件当中,等于没有使用这个方法,好像没啥卵用啊。

关于cookie的path,cookie的关联路径:

  • 假设现在发送的请求路径为“localhost:8080/servlet11/cookie/generate”生成的cookie,如果cookie没有设置path,默认path是什么?
  • 默认的path是:“localhost:8080/servlet11/cookie”以及它的子路径,也就是说以后只要浏览器发送的请求是这个路径或者它的子路径,cookie都会发送到服务器。
  • 手动设置cookie的path:cookie.setPath("servlet11");表示只要是这个servlet11项目的请求路径,都会提交这个cookie给服务器。

那么浏览器发送cookie给服务器后,java程序怎么接收呢:

        //接收cookie
        Cookie[] cookies = request.getCookies();
        //遍历cookie
        if (cookies != null){
            for (Cookie cookie : cookies) {
                String name = cookie.getName();
                String value = cookie.getValue();
                System.out.println(name + " = " + value);
            }
        }

10 使用cookie实现十天内免登录功能

同样是改造之前的oa项目

先实现登录功能:

  • 登录成功跳转到部门列表页面
  • 登录失败跳转到登录失败页面

修改前端页面:

  • 在登录界面给个复选框(十天内免登录)
  • 用户选择复选框表示支持十天内免登录,没有选择就不支持

修改servlet中的login方法

  • 如果用户登录成功了,并且用户登录时选择了十天内免登录功能,这个时候应该在Servlet的login方法中创建cookie,用来存储用户名和密码,并且重置路径,设置有效期,将cookie响应给浏览器
  • 用户再次访问网站的时候,有两个走向:要么跳转到部门列表,要么跳转到登录界面,这是由java程序来进行判断的
  • 也就是说我们还要再写一个Servlet来判断

本次新加了一个Servlet,写了一个welcome配置信息。因为,当我成功使用cookie后,再次访问这个网页的任何一个资源的时候都是可以直接访问的,并且直接访问oa项目不指定具体路径的时候是直接可以调到列表页面的,所以之前的默认页面(登录页面)就不能继续使用了。

web.xml配置信息:

    <welcome-file-list>
        <welcome-file>welcome</welcome-file>
    </welcome-file-list>

welcome:

package com.itzw.oa.web.action;

import com.itzw.oa.util.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@WebServlet("/welcome")
public class Welcome extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        Cookie[] cookies = request.getCookies();
        String username = null;
        String password = null;
        if (cookies != null) {
            //cookie有内容
            for (Cookie cookie : cookies) {
                //取出cookie
                String name = cookie.getName();
                //判断是否是登录的用户名
                if ("username".equals(name)) {
                    //取出用户名
                    username = cookie.getValue();
                } else if ("password".equals(name)) {
                    //取出密码
                    password = cookie.getValue();
                }
            }
        }
            //先判断取到的用户名和密码是否为空
            if (username != null && password != null){
                //连接数据库查看cookie获取到的用户名和密码是否和数据库一样
                Connection conn = null;
                PreparedStatement ps = null;
                ResultSet rs = null;
                boolean success = false;
                try {
                    conn = DBUtil.getConnection();
                    String sql = "select * from t_user where username = ? and password = ?";
                    ps = conn.prepareStatement(sql);
                    ps.setString(1,username);
                    ps.setString(2,password);
                    rs = ps.executeQuery();
                    if (rs.next()){
                        //查到数据就是登录成功
                        success = true;
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                if (success){
                    //cookie里的数据和数据库对应上了,跳转到列表页面
                    //获取Session
                    HttpSession session = request.getSession();
                    //将username放入session对象中
                    session.setAttribute("username",username);
                    response.sendRedirect(request.getContextPath()+"/dept/list");
                }else {
                    //错误,跳转到登录界面
                    response.sendRedirect(request.getContextPath()+"index.jsp");
                }
            }else{
                //cookie为空,跳转到登录界面
                response.sendRedirect(request.getContextPath()+"/index.jsp");
            }
        }
}

UserServlet新增:

            //登录成功创建cookie
            String f = request.getParameter("f");
            //选择了十天内免登录开始创建cookie
            if ("1".equals(f)){
                //存储登录的用户名和密码
                Cookie cookie1 = new Cookie("username",username);
                Cookie cookie2 = new Cookie("password",password);
                //设置有效时间为10天
                cookie1.setMaxAge(60*60*24*10);
                cookie2.setMaxAge(60*60*24*10);
                //设置path(只要访问这个应用,浏览器就一定携带这两个path)
                cookie1.setPath(request.getContextPath());
                cookie2.setPath(request.getContextPath());
                //响应给浏览器
                response.addCookie(cookie1);
                response.addCookie(cookie2);
            }

注意上面的代码放在重定向前面

之前有些路径是直接使用的项目名,经过本次修改之前的那些路径不能用了,需要加上具体路径。

11 JSP指令

指令的作用:指导JSP的翻译引擎如何工作

指令包括哪些呢?

  • include指令:包含指令,很少用了
  • taglib指令:引入标签库的指令,JSTL再学这个
  • page指令:目前重点学习

 指令的使用语法是什么?<%@指令名 属性名=属性值 属性名=属性值%>

关于page指令当中都有哪些常用的属性?

  • <%@page session="false" %>,其中false表示不启动内置对象session,当前JSP页面无法使用session对象;如果是true表示JSP的内置对象session一定被启动,并且没有session对象会创建。如果没有设置默认为true
  • <%@page contentType="text/html" %> <%@page contentType="text/json;charset=UTF-8" %>,contenType属性用来设置响应的内容类型,同时还可以设置字符集。
  • <%@page pageEncoding="UTF-8" %>,用来设置响应时采用的字符集
  • <%@page import="com.sun.java_cup" %>,用来导包
  • <%@page errorPage="/error.jsp" %>,当页面出现异常后跳转到error.jsp页面,不管什么错误都会跳转到这个页面。
  • <%@page isErrorPage="true" %>,但是经过上面的操作我们发现这样就无法发现错误了。可以在错误页面加上这个属性,改为true可以在jsp使用九大内置对象之一exception,它可以打印异常信息。<%exception.printStackTrace();%>

JSP九大内置对象

  • pageContext:页面作用域
  • request:请求作用域
  • session:会话作用域
  • application:应用作用域
  • exception:
  • config
  • page:其实是this,当前的Servlet对象
  • out:负责输出
  • response:负责响应

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值