Servlet学习02

Servlet注解,简化配置

  • 作用

    • 每次新增Servlet时,都需要在web.xml文件配置(注册Servlet)。如果大的项目,web.xml文件的内容会很庞大。所以可以通过Servlet注解进行简化,同时也提高了开发效率。
    • Servlet3.0以后版本就推出了注解式开发
      • 注解式开发的优点
        1. 开发效率高,不需要编写大量的配置信息,
        2. web.xml文件体积变小
      • 注意
        • 有些需要变化的信息,还是需要配置到web.xml文件中,一般采用 注解 + 配置文件 的开发模式。
        • 一些不会经常修改的配置建议使用 注解
  • 写法

    package com.bz.oa.web.action;
    
    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.io.PrintWriter;
    
    // @WebServlet(name="a", urlPatterns = {"/a"})
    // urlPatterns只有一个时可以简写,如下
    // @WebServlet(name="a", urlPatterns = "/a")
    // urlPatterns和value 相同,可以简写,如下
    // @WebServlet(name="a", value = "/a")
    // 注解中只有value属性时可以省略属性名,如下
    @WebServlet("/a")
    public class AServlet extends HttpServlet {
    	@Override
    	protected void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		response.setContentType("text/html;charset=UTF-8");
    		PrintWriter out = response.getWriter();
    
    		String servletName = getServletName();
    		out.println("<h1>servlet name = " + servletName + "</h1>");
    
    		String contextPath = request.getContextPath();
    		out.println("<h1>contextPath = " + contextPath + "</h1>");
    
    	}
    }
    
    • 在类的上方添加注解:@WebServlet

使用模板方法设计模式优化OA项目

  • 概念
    • 类爆炸:类太多
  • 如何解决类爆炸问题?
    • 以前的设计是一个请求一个Servlet类,1000个请求对应1000个Servlet类,导致类爆炸。
    • 优化思路
      • 一个 请求对应一个方法,一个业务对应一个Servlet类
        • 以oa系统为例,处理部门相关业务的对应一个DeptServlet,处理用户相关业务的对应一个UserServlet……

分析使用纯粹Servlet开发Web应用的缺陷 - JSP技术入场

  • 在Servlet当中编写HTML、CSS、JS等前端代码。存在什么问题?
    • java程序中编写前端代码,编写难度大,麻烦;而且代码不美观
    • java程序中编写前端代码,显然程序的耦合度非常高
    • java程序中编写前端代码,维护成本高(非常难维护)

JSP技术

本质

  • 底层中Tomcat会把jsp文件翻译生成对应的java文件,然后再编译生成class文件
  • 访问index.jsp,实际上执行的是index_jsp.class文件中的方法
  • jsp实际上就是一个Servlet
  • jsp的生命周期与Servlet的生命周期一样
  • jsp和Servlet都是单例(假单例)的。

JSP和Servlet有什么区别?

  • 职责不同
    • Servlet的职责是:收集数据
    • JSP的职责是:展示数据

第一次访问jsp比较慢的原因

  • 第一次访问jsp时,jsp文件会翻译成java文件,然后还需要编译生成class文件

jsp错误调试方法

  • 需要找到jsp翻译后的java文件,检查java代码。

注意

  • JSP文件的扩展名可以不为xxx.jsp。可以在CATALINA_HOME(tomcat)/conf/web.xml文件的中进行配置

    <servlet-mapping>
    	<servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
    

jsp语法

视频教程

基础语法

  • 在jsp文件直接编写代码,都会自动翻译到对应的java文件中的server方法的out.write(“翻译到这里”);中。

  • JSP的page指令,解决响应时的中文乱码问题

    <!--响应的内容类型为text/html;采用的字符集为:UTF-8-->
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
  • JSP中如何编写java程序,会自动翻译到对应的java文件中的server方法中。

    <% java语句; %>
    
  • JSP专业注释,不会被翻译到Java源代码中

    <%-- JSP注释内容 --%>
    
  • 声明,会自动翻译到对应的java文件中的server方法之外(很少用)

    <%! java代码; %>
    
  • 如何在浏览器上输出java变量

    <%
    	int num = 10;
    %>
    
    <%
    	out.write("num = " + num);
    %>
    
    <%-- 或 --%>
    <%=num %>
    

基本语法总结

  • <%page %>
    • page指令,通过contentType属性用来设置响应的内容类型,字符集编码
  • JSP直接编写普通字符串
    • 翻译到service方法的out.write(“这里”);
  • <%%>
    • 翻译到service方法之中,是java语句
  • <%! %>
    • 翻译到service方法之外
  • <%= %>
    • 翻译到service方法的out.print(这里);

什么是javabean?

  • Javabean翻译为:咖啡豆。
  • 整个java程序中有很多bean存在,由很多bean组成。
  • 实际上javabean可以理解为符合某种规范的java类,例如:
    • 有无参数构造方法
    • 属性私有化
    • 对外提供公开的set和get方法
    • 实现java.io.Serializable接口
    • 重写toString()
    • 重写hasCode() + equals()
    • ……
  • javabean其实就是java中的实体类,负责数据的封装
  • 由于javabean符合javabean规范,具有更强的通用性

Servlet注解 + 模板方法设计模式 + JSP技术 优化OA系统

  • 目录结构

    src
     |----com.bz.oa
     		|----bean
     			   |----Dept.java
     		|----utils
     			   |----DBUtil
     		|----web.action
     			   |----DeptServlet
     |----resources
     		|----jdbc.properties
    web
     |----WEB-INF
     		|----lib
     			  |-----mysql jdbc驱动包
     		|----web.xml
     |----addDept.jsp
     |----deptList.jsp
     |----detail.jsp
     |----editDept.jsp
     |----error.jsp
     |----index.jsp
    
  • addDept.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>新增部门</title>
    </head>
    <body>
        <h1>新增部门</h1>
        <hr/>
        <form action="<%=request.getContextPath()%>/dept/add" method="post">
            部门编号<input type="number" name="deptno" /><br/>
            部门名称<input type="text" name="dname" /><br/>
            部门位置<input type="text" name="address" /><br/>
            <input type="submit" value="保存" />
        </form>
    </body>
    </html>
    
  • deptList.jsp

    <%@ page import="com.bz.oa.bean.Dept" %>
    <%@ page import="java.util.List" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <meta charset='utf-8' />
        <title>部门列表</title>
    </head>
    
    <body>
    
        <script type="text/javascript">
            function del(deptno) {
                if (window.confirm('删除后无法 恢复,是否确定删除?')) {
                    document.location.href = "<%=request.getContextPath()%>/dept/delete?deptno=" + deptno;
                }
            }
        </script>
    
        <h1>部门列表</h1>
        <br/>
        <table border="1" align="center" width="50%">
            <tr>
                <th>部门编号</th>
                <th>部门名称</th>
                <th>操作</th>
            </tr>
    
            <%
                List<Dept> depts = (List<Dept>)request.getAttribute("deptList");
    			for(Dept dept : depts) {
            %>
    
            <tr>
                <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?deptno=<%=dept.getDeptno()%>&isEdit=1">编辑</a>
                    <a href="<%=request.getContextPath()%>/dept/detail?deptno=<%=dept.getDeptno()%>&isEdit=0">详情</a>
                </td>
            </tr>
    
            <%
                }
            %>
    
        </table>
        <hr/>
        <a href="<%=request.getContextPath()%>/addDept.jsp">新增部门</a>
    </body>
    </html>
    
  • detail.jsp

    <%@ page import="com.bz.oa.bean.Dept" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>部门详情</title>
    </head>
    <body>
        <%
            Dept dept = (Dept) request.getAttribute("deptDetail");
    		String isEdit = request.getParameter("isEdit"); // 0:详情 1:修改
            if (isEdit.equals("1")) {
        %>
    
        <h1>修改部门</h1>
        <hr/>
        <form action="<%=request.getContextPath()%>/dept/update" method="post">
            部门编号<input type="number" name="deptno" value="<%=dept.getDeptno()%>" readonly />
            <br/>
            部门名称<input type="text" name="dname" value="<%=dept.getDname()%>" />
            <br/>
            部门位置<input type="text" name="address" value="<%=dept.getAddress()%>" />
            <br/>
            <input type="submit" value="保存" />
        </form>
    
        <%
            } else {
        %>
    
        <h1>部门详情</h1>
        <hr/>
        <p>部门编号:<%=dept.getDeptno()%></p>
        <p>部门名称:<%=dept.getDname()%></p>
        <p>部门地址:<%=dept.getAddress()%></p>
        <input type="button" value="返回" onClick="window.history.back()" />
    
        <%
            }
        %>
    </body>
    </html>
    
  • editDept.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>部门编辑</title>
    </head>
    <body>
        <h1>修改部门</h1>
        <hr/>
        <form action="<%=request.getContextPath()%>/deptList.jsp" method="post">
            部门编号<input type="number" name="deptno" value="deptno" readonly />
            <br/>
            部门名称<input type="text" name="dname" value="dname" />
            <br/>
            部门位置<input type="text" name="address" value="address" />
            <br/>
            <input type="submit" value="保存" />
        </form>
    </body>
    </html>
    
  • error.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>error</title>
    </head>
    <body>
        <h1>操作失败</h1>
        <a href="<%=request.getContextPath()%>/dept/list">返回</a>
    </body>
    </html>
    
  • index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>欢迎使用oa系统</title>
      </head>
      <body>
        <a href="<%=request.getContextPath()%>/dept/list">查看部门列表</a>
      </body>
    </html>
    
  • jdbc.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://ip地址:3306/bztest
    user=root
    pwd=root
    
  • Dept.java

    package com.bz.oa.bean;
    
    import java.util.Objects;
    
    /**
     * 一个普通的Java类,这个Java类可以封装零散的数据,代表了一个部门对象
     */
    public class Dept {
    	private String deptno;
    	private String dname;
    	private String address;
    
    	public Dept() {
    	}
    
    	public Dept(String deptno, String dname, String address) {
    		this.deptno = deptno;
    		this.dname = dname;
    		this.address = address;
    	}
    
    	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 getAddress() {
    		return address;
    	}
    
    	public void setAddress(String address) {
    		this.address = address;
    	}
    
    	@Override
    	public String toString() {
    		return "Dept{" +
    				"deptno='" + deptno + '\'' +
    				", dname='" + dname + '\'' +
    				", address='" + address + '\'' +
    				'}';
    	}
    
    	@Override
    	public boolean equals(Object o) {
    		if (this == o) return true;
    		if (o == null || getClass() != o.getClass()) return false;
    		Dept dept = (Dept) o;
    		return Objects.equals(deptno, dept.deptno) && Objects.equals(dname, dept.dname) && Objects.equals(address, dept.address);
    	}
    
    	@Override
    	public int hashCode() {
    		return Objects.hash(deptno, dname, address);
    	}
    }
    
  • DBUtil.java

    package com.bz.oa.utils;
    
    import java.sql.*;
    import java.util.ResourceBundle;
    
    /**
     * JDBC工具类
     */
    public class DBUtil {
    
    	// 静态变量,在类加载时执行
    	// 并且是有顺序的,自上而下的顺序
    	// 注意:这里的resources目录是在src下的
    	private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
    	private static String driver = bundle.getString("driver");
    	private static String url = bundle.getString("url");
    	private static String user = bundle.getString("user");
    	private static String pwd = bundle.getString("pwd");
    
    
    	static {
    		try {
    			// 注册驱动
    			Class.forName(driver);
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 获取数据库连接对象
    	 * @return connection 数据库连接对象
    	 * @throws SQLException
    	 */
    	public static Connection getConnection() throws SQLException {
    		return DriverManager.getConnection(url, user, pwd);
    	}
    
    
    	/**
    	 * 释放资源
    	 * @param conn 数据库连接对象
    	 * @param stat 数据库操作对象
    	 * @param res 结果集对象
    	 */
    	public static void close(Connection conn, Statement stat, ResultSet res) {
    		if (res != null) {
    			try {
    				res.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    
    		if (stat != null) {
    			try {
    				stat.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    
  • DeptServlet.java

    package com.bz.oa.web.action;
    
    import com.bz.oa.bean.Dept;
    import com.bz.oa.utils.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/add", "/dept/delete", "/dept/update"})
    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/add".equals(servletPath)) {
    			doSave(request, response);
    		} else if("/dept/delete".equals(servletPath)) {
    			doDel(request, response);
    		} else if("/dept/update".equals(servletPath)) {
    			doUpdate(request, response);
    		}
    	}
    
    	/**
    	 * 修改部门信息
    	 * @param request
    	 * @param response
    	 * @throws ServletException
    	 * @throws IOException
    	 */
    	private void doUpdate(HttpServletRequest request, HttpServletResponse response)
    		throws ServletException, IOException{
    		String deptno = request.getParameter("deptno");
    		String dname = request.getParameter("dname");
    		String address = request.getParameter("address");
    
    		Connection conn = null;
    		PreparedStatement ps = null;
    		int count = 0;
    
    		try {
    			conn = DBUtil.getConnection();
    			String sql = "update t_dept set dname=?, address=? where deptno = ?";
    			ps = conn.prepareStatement(sql);
    			ps.setString(1, dname);
    			ps.setString(2, address);
    			ps.setString(3, deptno);
    			count = ps.executeUpdate();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, null);
    		}
    		String contextPath = request.getContextPath();
    		if(count > 0) {
    			// 修改成功
    			response.sendRedirect(contextPath + "/dept/list");
    		} else {
    			// 修改失败
    			response.sendRedirect(contextPath + "/error.jsp");
    		}
    	}
    
    	/**
    	 * 连接数据库,查询所有的部门信息,将部门信息收集好,然后跳转到JSP做页面展示
    	 * @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, address from t_dept";
    			ps = conn.prepareStatement(sql);
    
    			rs = ps.executeQuery();
    
    			while(rs.next()) {
    				String deptno = rs.getString("deptno");
    				String dname = rs.getString("dname");
    				String address = rs.getString("address");
    				System.out.println(deptno + ", " + dname + ", " + address);
    				// 将以上零散的数据封装成java对象
    				Dept dept = new Dept(deptno, dname, address);
    				// 将部门对象放到集合中
    				depts.add(dept);
    			}
    
    			// 将部门对象集合放到请求域中
    			request.setAttribute("deptList", depts);
    
    			// 转发(不用重定向)
    			request.getRequestDispatcher("/deptList.jsp").forward(request, response);
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, rs);
    		}
    	}
    
    	/**
    	 * 获取部门详情
    	 * @param request
    	 * @param response
    	 * @throws ServletException
    	 * @throws IOException
    	 */
    	private void doDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		String deptno = request.getParameter("deptno");
    		String isEdit = request.getParameter("isEdit"); // 0:详情 1:修改
    
    		Connection conn = null;
    		PreparedStatement ps = null;
    		ResultSet rs = null;
    		Dept dept = new Dept();
    
    		try {
    			conn = DBUtil.getConnection();
    
    			String sql = "select dname, address from t_dept where deptno = ?";
    			ps = conn.prepareStatement(sql);
    			ps.setString(1, deptno);
    			rs = ps.executeQuery();
    			if(rs.next()) {
    				dept.setDeptno(deptno);
    				dept.setDname(rs.getString("dname"));
    				dept.setAddress(rs.getString("address"));
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, rs);
    		}
    		request.setAttribute("isEdit", isEdit);
    		request.setAttribute("deptDetail", dept);
    		// 转发到jsp页面
    		request.getRequestDispatcher("/detail.jsp").forward(request, response);
    	}
    
    	/**
    	 * 添加部门
    	 * @param request
    	 * @param response
    	 * @throws ServletException
    	 * @throws IOException
    	 */
    	private void doSave(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		request.setCharacterEncoding("UTF-8");
    		String deptno = request.getParameter("deptno");
    		String dname = request.getParameter("dname");
    		String address = request.getParameter("address");
    
    		Connection conn = null;
    		PreparedStatement ps = null;
    		int count = 0;
    		try {
    			conn = DBUtil.getConnection();
    			// 开启事务
    			conn.setAutoCommit(false);
    			String sql = "insert into t_dept (deptno, dname, address) values (?, ?, ?)";
    			ps = conn.prepareStatement(sql);
    			ps.setString(1, deptno);
    			ps.setString(2, dname);
    			ps.setString(3, address);
    			count = ps.executeUpdate();
    			// 提交事务
    			conn.commit();
    		} catch (SQLException e) {
    			if(conn != null) {
    				// 回滚事务
    				try {
    					conn.rollback();
    				} catch (SQLException ex) {
    					ex.printStackTrace();
    				}
    			}
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, null);
    		}
    		if (count > 0) {
    			// 新增成功
    			response.sendRedirect(request.getContextPath() + "/dept/list");
    		} else {
    			// 新增失败
    			response.sendRedirect(request.getContextPath() + "/error.jsp");
    		}
    	}
    
    	/**
    	 * 删除部门
    	 * @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 count = 0;
    		try {
    			conn = DBUtil.getConnection();
    			// 开启事务
    			conn.setAutoCommit(false);
    			String sql = "delete from t_dept where deptno = ?";
    			ps = conn.prepareStatement(sql);
    			ps.setString(1, deptno);
    			count = ps.executeUpdate();
    			// 提交事务
    			conn.commit();
    		} catch (SQLException e) {
    			if (conn != null) {
    				// 回滚事务
    				try {
    					conn.rollback();
    				} catch (SQLException ex) {
    					ex.printStackTrace();
    				}
    			}
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, null);
    		}
    
    		if (count > 0) {
    			// 删除成功
    			response.sendRedirect(request.getContextPath() + "/dept/list");
    		} else {
    			// 删除失败
    			response.sendRedirect(request.getContextPath() + "/error.jsp");
    		}
    	}
    }
    

index.jsp修改为登录页,实现登录操作

  • 新增t_user表

    drop table if exists t_user;
    
    create table t_user (
    	id int primary key auto_increment,
    	username varchar(255),
    	password varchar(255)
    );
    
    insert into t_user (username, password) values ('bz', '123');
    commit;
    
    select * from t_user;
    
  • index.jsp调整

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>欢迎使用oa系统</title>
      </head>
      <body>
    
        <form action="<%=request.getContextPath()%>/user/login" method="post">
          <h1>用户登录</h1>
          <hr>
          用户名:<input type="text" name="username" /><br/>
          密码:<input type="password" name="password"/><br/>
          <input type="submit" value="登录">
        </form>
    
    
        <a href="<%=request.getContextPath()%>/dept/list">查看部门列表</a>
      </body>
    </html>
    
  • UserServlet.java

    package com.bz.oa.web.action;
    
    import com.bz.oa.utils.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 javax.xml.transform.Result;
    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 {
    		String username = request.getParameter("username");
    		String password = request.getParameter("password");
    
    		Connection conn = null;
    		PreparedStatement ps = null;
    		ResultSet res = 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);
    
    			res = ps.executeQuery();
    
    			if(res.next()) {
    				success = true;
    			}
    		} catch (SQLException e) {
    			e.printStackTrace();
    		} finally {
    			DBUtil.close(conn, ps, res);
    		}
    
    		String contextPath = request.getContextPath();
    		if(success) {
    			// 登录成功
    			response.sendRedirect(contextPath + "/dept/list");
    		} else {
    			// 登录失败
    			response.sendRedirect(contextPath);
    		}
    	}
    }
    

存在问题

  • 在浏览器地址栏直接输入地址,可以跳过登录环节,进行数据的增删改查操作,这是一个很大的安全漏洞。可以 通过会话(session)机制

会话(session)机制

什么是会话?

  • 用户打开浏览器,进行一系列操作,最后关闭浏览器。这整个过程叫做:一次会话
  • 一次会话对应n次请求
  • 超时时间默认是30分钟

作用

  • 保存会话状态

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

  • 因为HTTP协议是一种无状态协议
    • 什么是无状态:请求的时候,B和S是连接的,但是请求结束后,连接就断了。(减轻服务器压力)

实现原理

  • 在web服务器中有一个类似map集合的session列表,这个map集合的key存储的是sessionid,这个map集合的value存储的是对应的session对象
  • 用户发送第一次请求时:服务器会创建一个新的session对象,同时给session对象生成一个id,然后web服务器会将session的id发送给浏览器,浏览器会将这个id保存到缓存中,所以关闭浏览器后,浏览器中的缓冲就被释放了,所以sessionid就不存在了
  • 用户第二次发送请求时,会自动将浏览器缓存中的sessionid自动发送给服务器,服务器获取到sessionid后,再从session列表中查找对应的session对象

工作原理

  • 用户打开浏览器,第一次请求时,服务器生成session对象,同时生成sessionid,并将sessionid响应给浏览器
  • 用户第二次请求时,自动将浏览器内存中的sessionid发送给服务器,服务器根据id查找session对象
  • 用户关闭浏览器时,内存释放,cookie消失,sessionid消失,会话等同于结束

Cookie禁用的含义

  • 服务器把生成的sessionid响应给浏览器,但是浏览器拒收了,浏览器不缓冲sessionid。所以每次请求都重新生成session对象

Cookie禁用了,如何实现session机制

  • 需要使用URL重写机制,在URL后追加“;sessionid=第一次响应的sessionid值”

    http://xxxx;sessionid=xxxxxxxxxxxxxxxxx
    
  • URL重写机制会导致开发成本加大,原因:每个请求后都需要加sessionid。所以目前的方案是如果不允许cookie,就建议别使用该系统。

session的销毁方式

  • 超时销毁
  • 手动销毁

session的配置

  • 第一种:在web.xml文件配置

    <!-- session的超时时长是30分钟 -->
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    

案例

  • 第一次请求:服务器生成session对象,并响应给浏览器,浏览器以Cookie形式保存

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1BrX1N0r-1658934971915)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220727214129522.png)]

  • 第二次请求:浏览器从Cookie取sessionid,发送请求时一并发送给服务器,服务器根据拿到的sessionid查询session列表中的数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zujuMqwt-1658934971918)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220727214211792.png)]


在java的Servlet规范中,session对应的类名:HttpSession(jakarta.servlet.http.HttpSession)

session机制属于B/S结构的一部分

// 从服务器中获取session对象,如果获取不到,就新建session对象
HttpSession session = request.getSession();

// 从服务器中获取session对象,如果获取不到,返回null
HttpSession session = request.getSession(false);

Cookie

cookie保存的位置?

  • jakarta.servlet.http.Cookie
  • cookie最终保存在浏览器客户端上,可以保存在运行内存中,也可以保存在硬盘文件中(永久保存)

cookie有啥用?

  • cookie和session机制其实都是为了保存会话状态
  • cookie是将会话的状态保存在浏览器客户端上,session是将会话状态保存在服务器上

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

只要是web开发,不管哪种语言,cookie和session都是需要的。

HTTP协议规定:

  1. 任何一个cookie都是由name和value组成的,name和value都是字符串
  2. 当浏览器发送请求时,会自动携带该path下cookie数据给服务器

cookie有效时间

默认不设置有效时间
  • 默认保存浏览器的运行内存中,浏览器关闭则cookie失效
  • 只要设置cookie的有效时间大于0,这个cookie一定会存储到硬盘文件当中。
  • cookie有效时间等于0,cookie会被删除
  • cookie有效时间小于0,cookie不会被存储到硬盘文件中,但是会被存储到浏览器运行内存中(跟不调用setMaxAge方法一样)。
如何设置有效时间
Cookie cookie = new Cookie(k, v);

// 单位:秒
// 设置cookie在一小时之后失效
cookie.setMaxAge(60 * 60);

// 手动设置cookie的path
cookie.setPath("/webServlet");

关于cookie的path

假如当前发送请求路径为:http://localhost:8080/webServlet/aaa/bbb生成cookie

  • 默认情况

    • 如果cookie没有设置path,默认的path为:http://localhost:8080/webServlet/aaa 以及它的子路径
    • 也就是说,以后只要浏览器的请求路径是 http://localhost:8080/webServlet/aaa 这个路径以及这个路径下的子路径,cookie都会被发送到服务器。
  • 手动设置cookie的path

    • cookie.setPath(“/webServlet”);表示只要是这个webServlet项目的请求路径,都会提交这个cookie给服务器。

浏览器发送cookie给服务器,服务器的java程序如何接收?

// 如果浏览器没有提交cookie,这个方法返回值为null,并不是返回一个长度为0的数组
Cookie[] cookies = request.getCookies();

if(cookies != null) {
    for(Cookie cookie : cookies) {
        String name = cookie.getName();
        String value = cookie.getValue();
        System.out.println(name + " = " + value);
    }
}

EL表达式

  • EL表达式有什么用?

    • Expression Language(表达式语言)
    • EL表达式可以代替JSP中的java代码,让JSP文件中的程序看起来更加整洁,美观
    • EL表达式可以算是JSP语法的一部分,EL表达式归属于JSP
  • EL表达式出现在JSP中主要是:

    • 从某个作用域中取数据,然后将其转换成字符串,最后将其输出到浏览器
  • EL表达式的三大功效:

    • 第一功效:从某个作用域中取数据
      • 四个域
        • pageContext
        • request
        • session
        • application
    • 第二功效:将取出的数据转换成字符串
      • 如果是一个java对象,也会自动调用java对象的toString方法将其转换成字符串
    • 第三功效:将字符串输出到浏览器
  • EL表达式的基本语法:

    ${表达式}
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值