当前位置:   article > 正文

老杜JavaWeb学习笔记:下_javaweb 老杜

javaweb 老杜

JSP技术

 

一、为什么需要JSP技术

【存在问题】

原先仅使用Servlet开发的项目,需要利用Servlet的request对象的out.print方法将html标签输出到浏览器上,导致Servlet类中存在众多html代码。

简而言之,java程序中编写html代码存在以下问题:

  • 编写难度大,麻烦
    • 无报错提示
    • 每次调试都需要编译java,生成class文件
  • 代码耦合度过高
  • 代码不美观
  • 维护成本高

1682243578640

【解决方案】

  • 思考:

    能否让程序员只需编写Servlet程序中的“前端代码“,然后让机器将我们写的“前端代码”自动翻译生成“Servlet”这种“java”程序。再自动将“java程序”编译成“class”程序,再使用JVM调用这个class中的方法。

    ---jsp技术!

二、底层原理

(1)浏览器中访问index.jsp文件

实际上Tomcat会先将index.jsp文件翻译生成index_jsp.java文件,然后进行编译生成index_jsp.class文件,最后执行class文件,显示浏览器页面

(2)JSP实际上是一个Servlet

当index.jsp访问时,会自动翻译生成index_jsp.java,会自动编译生成index_jsp.class,那么index.jsp就是一个类。

inde_jsp继承HttpJspBase,而HttpJspBase类继承HttpSerlvet,所以inde_jsp就是一个Servlet类

1682245141334

1682245171553

(3)jsp生命周期与Servlet生命周期完全相同

  • 两者是一个东西,无区别

  • 两者都是单例的 (假单例)

  • 【jsp生命周期】

    • Servlet创建调用无参构造方法
    • 调用init方法初始化
    • 用户请求一次访问一次service方法
    • 最后销毁时调用destory方法

(3)jsp第一次访问效率较低

  • 为什么运维人员给客户演示时,都提前访问一遍jsp文件?
  • 第一次:
    • 要把jsp文件翻译成java源文件
    • java源文件要编译生成class字节码文件
    • 然后通过class创建servlet对象
    • 调用servlet对象的init方法
    • 调用servlet对象的service方法
  • 第二次:
    • 直接调用单例Servlet对象的service方法

三、JSP概述

3.1 什么是jsp

jsp是一种动态网页开发技术

①JSP是一套规范,所有的web容器/web服务器都遵循该规范进行的翻译。

  • Servlet是javaEE的13个规范之一,JSP也是规范之一

  • 遵循不同规范时,jsp会按照不同的符号输出在jsp文件的不同位置

  • 每个Web容器,所有的服务器都内置一个JSP翻译引擎

②同时JSP是java程序,本质仍是一个Servlet

Tomcat会将jsp文件变为对应的xx_jsp.java文件,编译为xx_jsp.class文件

  • JSP是JavaServer Pages的缩写。
    • 【基于java语言实现的服务器端的页面】
  • 所以对jsp进行错误调试时,仍要打开jsp对应的java文件。检查java代码

③由jsp生成的java文件在哪?

可在Tomcat启动时,打开Tomcat控制台找到CATALINA_BASE,复制其后路径,在本机中访问找到

1682249155834

(开发jsp,最高境界:眼前是jsp,脑里中java代码)

3.2jsp基础语法
3.2.1 jsp输出原理

jsp文件中编写的内容,都会被自动翻译到对应的Java文件,从而编译生成.class文件

  • jsp文件中编写的文字

  • ---翻译到servlet类的service方法的out.write("");中

  • 类似于Servlet中的out.print("")

    • 同时会被当做普通的字符串处理
    • 同时jsp将html/css/js以字符串的形式输出到浏览器后,浏览器会对其进行解释执行,展现页面效果
  • 不同的标签会生成在jsp(生成的java程序)的不同位置

3.2.2 jsp中文乱码

【存在问题】

当在jsp页面中输出中文,会发现页面出现中文乱码问题

1682248601002

【解决方案】

可利用jsp的page指令解决,响应的乱码问题

  • 步骤:在jsp文件上方,添加以下代码设置响应内容类型为UTF-8
<%@page contentType="text/html;charset=UTF-8" %>
<%--表示响应的内容是text/html,采用的字符集为UTF-8--%>
  • 原理:

    添加该指令后,jsp对应的java文件中代码就会相应更改

3.2.3编写java代码
(1)<%%>--翻译到service方法里

①<%xxx%>翻译到哪?

  • xxx会被翻译到其jsp对应的java类的service方法中

②<%xxx%>翻译成了什么?

  • 翻译成: service方法中的一条条语句

③<%%>什么时候用?

用于在jsp页面中编写java语句

  • 当jsp中不添加特殊标记的内容,默认输出为字符串到service方法的out.write("")中

  • 当想在jsp页面中编写java语句,应采用<%%>

<%java语句;%>
  • 该符号中编写的被视作java程序,会被翻译到Servlet类的service内部,而非out.print("")当中

④注意:

  • ❗<%%>写代码,就类似于在service方法体写代码!!
  • service方法不能写静态代码块、不能写方法、不能定义成员变量
  • 同时编写的代码要符合编译顺序,因为就相当于将jsp<%%>中的语句,写入其对应java的service方法里
  • eg: private int i=0;因为方法中只允许有私有化变量/可以写int i =0

1682251722478

(2)<%!%>--翻译到service方法外

①<%!%>翻译到哪里?

  • 翻译到jsp文件生成的java文件的service方法外面

②<%!%>翻译成什么?

  • 翻译成service方法外的一条条语句

③<%!%>什么时候用?

  • 当想将java代码输出jsp对应java程序的service方法外,可以使用<%!%>
    • 使用较少
      • 因为在service方法中写静态变量和实例变量,都会存在线程安全问题,因为jsp就是servlet,而servlet是单例的,多线程并发的环境下,该静态变量和实例变量一旦有修改操作,必然会存在线程安全问题
<%!java代码; %>

1682251729846

【注意】同时使用<%①%>和<%!②%>时,java程序中②在上service方法外,①在下service方法里

(3)<%=%>--翻译到service方法out.print()里

①<%=xxxx%>会被翻译到哪?

  • 其中的xxx会被翻译到jsp其对应java代码的service方法的out.print()中

②<%=xxx%>会被翻译成什么?

  • 翻译成: out.print()
<%=100+200%>
<%--被翻译为out.print(100+200) --%>

③<%=xx%>什么时候用?

  • 当输出的是一个动态变化的内容/含有java变量,使用<%=%>输出
<%  int a = 5;
    int b = 4;
    int i =a+b;
%>
<%=i%>
3.2.5 jsp的注释
  • jsp中的专业注释为:

    <%-- jsp专业注释,不会被翻译到java源代码中 --%>
  • html注释为

    <!--html注释不专业,仍会被翻译到java源代码中-->
3.2.6 jsp输出语句

要向浏览器输出一个java变量,可使用内置对象out的write("")方法

  • jsp代码如下:
    • 此处的out为JSP的九大内置对象之一,可直接在service内部使用。
      • 内置对象,只能在Servlet生成的java程序的service方法中使用哦!
      • 九大内置对象:pageContext、session、application、config、out、page、request、reponse、Exception
<% String name = "jack";out.write("name="+name);%>  
  • 注意:

①固定的内容输出,直接写在jsp文件中即可,不必写到<%%>中,因为jsp文件的内容,本身就会变为ou.write,然后输出到浏览中

②输出的内容若有变量,则利用<%java代码%>的形式展现

③输出内容有java代码,使用以下语法格式:

  • <%=%> 在=号后编写要输出的内容

始终记得:jsp就是servlet,只是职责不同。

3.3jsp和Servlet的区别

jsp的本质就是一个Servlet,但两者职责不同

  • Servlet职责是:收集数据
    • 其强项在于逻辑处理,业务处理,连接数据库,获取/收集数据
  • jsp职责是:展示数据
    • 其强项在于做数据的展示
3.4总结

jsp语法总结:

(1)jsp中直接写代码
  • 翻译到哪?

翻译到jsp生成的java文件serice()方法的out.write("")中

  • 翻译成什么?

翻译成:out.write("");

  • 何时使用?

当输出静态的字符串到浏览器时

如:html标签,不变的标题文字等等

(2)<%%>
  • 翻译到哪?

翻译到jsp生成的java文件service()方法体中

  • 翻译成什么?

一条条语句,写什么翻译为什么,只是放在service()方法体中

  • 何时使用?

要在service()方法体中定义java代码时

(3)<%!%>
  • 翻译到哪?

翻译到jsp生成的java文件service方法体外

  • 翻译成什么?

一条条代码语句,写什么翻译为什么,只是放在service()方法体外

  • 何时使用?

要在service()方法体外定义java代码时

(4)<%=%>
  • 翻译到哪?

翻译到jsp生成的java文件的service方法中的out.print()中

  • 翻译成什么?

翻译成: out.print();

  • 何时使用?

要输出动态的内容

(5)<%@ %>
<%@page contentType="text/html;charset=UTF-8"%>
  • 翻译成什么?

为page指令,通过contentType属性用来设置响应的内容类型

主要用于解决防止乱码

四、利用Servlet+JSP改造纯Servlet的oa项目

4.1未改造前代码

原先未改造的代码,主要利用纯servlet编写,弊端是:

  • 在java后端代码中需要使用很多out.print语句去输出html标签
  • 前后端代码耦合性差
  • 代码繁琐
  • 等....
4.2改造后代码

采用jsp技术,去代替servlet展示html代码,好处是:

  • 前后端代码编写分离
    • jsp会帮助我们将jsp页面中的html代码响应给浏览器
  • 简化代码
    • 省去了许多out.print语句

①创建一个新java工程,右键工程->Add Framework support添加web框架支持

②File->Project Structure->Modules ->选中项目->Dependencies添加servlet,jsp的jar

1683958863658

③Add configurations->添加Tomcat,并进行配置->Deployment 设置项目访问路径

1683958964819

④在工程的web->WEB-INF目录下,新建lib包用于存放数据库连接的jar包

⑤在src中创建utils包,放置DBUtils工具类,用于连接数据库

1683959377031

⑥正式开始编码

  • 首先将所有html文件,变为.jsp文件

  • 同时为防止中文乱码,在文件首行添加<%@page contentType="text/html;charset=UTF-8"%>语句

  • 更改原先所有的html静态路径,变为jsp静态路径

    • 注意页面的路径,需要携带/项目名

    • 因为,当前端发送请求路径是绝对路径时,要以"/"开始,加项目名

      • 同时项目名最好不要写死,在此jsp提供了<%=requet.getContextPat()%>解决这个问题

      • <a href="<%=request.getContextPath()%>/list.jsp">查看</a>
  • 实现页面的正常流转

⑦编码思想,由servlet获取处理数据,jsp去展示

(1)action包下: 编写servlet代码,用于获取数据

  • 1️⃣为简化servlet请求路径配置,采用@WebServlet的servlet注解式开发
@WebServlet({"请求路径,不带项目名"}) 
-写在类名上,表示该类中所有方法的请求路径前缀为/xx/xx
  • 2️⃣为防止一个表增删改查要写多个servlet类,采用将每个Servlet类写成方法的模板方法设计模式开发
servlet中直接重写service方法
-request.getSerlvetPath();//得到servlet请求路径
-if结合"".equals(path);//判断请求路径为xx
-调用对应的doXxx方法
  • 3️⃣利用实体类的思想,存储查询的各字段数据

⑧编写serlvet代码

  • 获取数据,进行处理,跳转至页面
DeptServlet.java代码
  • 将数据库获取的数据存储在javabean中,然后形成数据链表,放入请求域
  • 最后跳转至jsp页面
@WebServlet({"/dept/list","/dept/detail","/dept/delete","/dept/add","/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/delete".equals(servletPath)){
         doDel(request,response);
     }else if("/dept/add".equals(servletPath)){
         doAdd(request,response);
     }else if("/dept/update".equals(servletPath)){
         doUpdate(request,response);
     }
    }

    /**
     * 连接数据库,查询所有部门信息,再跳转至jsp做页面展示
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void  doList(HttpServletRequest request,HttpServletResponse response)
            throws ServletException,IOException{
         //1.连接数据库
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<Dept> deptList = new ArrayList<>();
        try {
            //2.获取连接
            conn = DBUtils.getConnection();
            //3.执行查询语句
            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");
                //创建一个表实体类对象,将数据放入
                Dept dept = new Dept();
                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);
                //将存放数据的实体类,存入链表
                deptList.add(dept);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //4.释放资源
            DBUtils.close(conn,ps,rs);
        }
        //将最后的链表数据存入请求域中
        request.setAttribute("deptList",deptList);
        //转发,注意不可重定向,否则无法共享请求域数据
        request.getRequestDispatcher("/list.jsp").forward(request,response);
    }
    private void doDetail(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
        //从请求域中获取deptno
        String deptno = request.getParameter("deptno");
        //获取数据库连接
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement ps= null;
        Dept dept = new Dept();
        try {
            conn = DBUtils.getConnection();
            String sql = "select dname,loc,principal 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");
                String principal = rs.getString("principal");
                dept.setDeptno(deptno);
                dept.setDname(dname);
                dept.setLoc(loc);
                dept.setPrincipal(principal);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.close(conn,ps,rs);
        }
        request.setAttribute("dept",dept);
        String f = request.getParameter("f");
        if(f.equals("m")){//转发到修改页面
            request.getRequestDispatcher("/edit.jsp").forward(request,response);
        }else if(f.equals("d")){//转发到详情页面
            request.getRequestDispatcher("/detail.jsp").forward(request,response);
        }

    }
    private void doDel(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException {
        String deptno = request.getParameter("deptno");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        int count = 0;
        try {
            conn = DBUtils.getConnection();
            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, deptno);
            count = ps.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(conn, ps, rs);
        }
        if (count == 1) {//删除成功,重定向到列表页面
            //要加项目名,因为此时是重新发起一次请求
            response.sendRedirect(request.getContextPath() + "/dept/list");
        } else {//删除失败
            response.sendRedirect(request.getContextPath() + "/error.jsp");
        }
    }
    private void doAdd(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
        request.setCharacterEncoding("UTF-8");
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");
        String principal = request.getParameter("principal");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        int i = 0;
        try {
            conn = DBUtils.getConnection();
             String sql = "insert into dept(deptno,dname,loc,principal) values (?,?,?,?)";
             ps = conn.prepareStatement(sql);
             ps.setString(1,deptno);
             ps.setString(2,dname);
             ps.setString(3,loc);
             ps.setString(4,principal);
             i = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.close(conn,ps,rs);
        }
        if(i == 1){//添加成功
            response.sendRedirect(request.getContextPath() +"/dept/list");
        }else {
            response.sendRedirect(request.getContextPath() +"/error.jsp");
        }

    }

    private void doUpdate(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
        request.setCharacterEncoding("UTF-8");
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");
        String principal = request.getParameter("principal");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        int i = 0;
        try {
            conn = DBUtils.getConnection();
            String sql = "update  dept set dname=?,loc=?,principal=? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(4,deptno);
            ps.setString(1,dname);
            ps.setString(2,loc);
            ps.setString(3,principal);
            i = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.close(conn,ps,rs);
        }
        if(i == 1){//添加成功
            response.sendRedirect(request.getContextPath() +"/dept/list");
        }else {
            response.sendRedirect(request.getContextPath() +"/error.jsp");
        }

    }
}

⑨在页面中通过请求域,获取动态展示的数据

  • 利用<%%>即编写java语句至service方法,通过srequest请求域,获取存储的List数据
  • 利用<%=%>即out.write,循环将数据输出到浏览器中
    • 获取servlet发给页面的数据,request.getAttribute、request.setAttribute
    • 获取页面发给servlet的数据,request.getParameter
<table border="1px" align="center" width="50%">
    <tr>
        <th>序号</th>
        <th>部门编号</th>
        <th>部门名字</th>
        <th>部门地址</th>
        <th>操作</th>
    </tr>
<%
        int index = 1;
        List<Dept> deptList = (List<Dept>) request.getAttribute("deptList");
        for(Dept dept :deptList){
%>
    <tr>
        <td><%=index++%></td>
        <td><%=dept.getDeptno()%></td>
        <td><%=dept.getDname()%></td>
        <td><%=dept.getLoc()%>></td>
        <td>
            <a href="javascript:void(0)" οnclick="window.confirm('确认删除该部门信息吗?')">删除部门</a>
            <a href="<%=request.getContextPath()%>/edit.jsp">修改部门</a>
            <a href="<%=request.getContextPath()%>/detail.jsp">查看详情</a>
        </td>
    </tr>
  <%}%>

【拓展】:

  • 如何清除服务器缓存

    ctrl+shift+delete

  • 可否只用jsp独立开发web应用?

仅用jsp也可以独立开发web应用,因其本质就是servlet,但是不推荐,会使代码解耦性变差

因为servlet用于数据获取及处理,jsp用于数据展示,jsp中的java代码越少越好

  • jsp文件的扩展名必须是.jsp么?

并非一定是jsp,可通过在Tomcat目录/conf/web.xml文件中进行配置即可

因为对于Tomcat而言,jsp就是一个普通的文本文件,web容器会将生成jsp_serlvet.java文件,再编译成class,最后运行调用java对象相关方法,真正执行与jsp文件无关。

1683965382674

  • 之后还可以学到什么?
    • Serlvet+jsp
      • session
      • cookie
      • EL表达式
      • jstl标签
      • Filter
      • Listener
    • Ajax
    • jQuery
    • Vue
    • mvc架构模式
    • 连接池
    • SSM
    • maven
    • git
页面代码如下:
  • 在此以list.jsp为例,代码如下:
    • 注意使用的Dept是自定义的实体类,使用时要导包
<%@ page import="java.util.List" %>
<%@ page import="com.bean.Dept" %>
<%@page contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>列表页面</title>
</head>
<body>
<h1 align="center">部门列表</h1>
<hr>
<table border="1px" align="center" width="50%">
    <tr>
        <th>序号</th>
        <th>部门编号</th>
        <th>部门名字</th>
        <th>部门地址</th>
        <th>操作</th>
    </tr>
    <%
        int index = 1;
        List<Dept> deptList = (List<Dept>) request.getAttribute("deptList");
        for(Dept dept :deptList){
    %>
    <tr>
        <td><%=index++%></td>
        <td><%=dept.getDeptno()%></td>
        <td><%=dept.getDname()%></td>
        <td><%=dept.getLoc()%></td>
        <td>
            <a href="javascript:void(0)" οnclick="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>
<a href="javascript:void(0)" οnclick="del(no)" >删除</a>
<script type="text/javascript">
    function del(no){
        if(window.confirm("亲,删了不可恢复哦!")){
            document.location.href = "<%=request.getContextPath()%>/dept/delete?deptno=" + no;
        }
    }
</script>
</body>
</html>

五、实现登录功能

5.1不带Session

①步骤一:创建user表

  • user表包括登录用户名+登录密码
  • 同时密码采用密文存储(MD5加密)

②步骤二:编写登录页面

  • 页面含有用户名和密码的输入框
  • 点击登录,提交表单,提交用户名和密码,利用post提交方式

③步骤三:编写对应的Servlet处理登录请求

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

④步骤四:编写提供一个失败页面

【存在问题】:即便用户不登录,但若用户知晓请求路径,即可通过路径跨过登录页面,直接访问部门列表页

【解决方法】:利用session判断其是否登录

5.2带session

要解决用户不登录就可通过部门列表请求路径访问这个问题,可以将用户登录时的信息存储到session中,即当session中存在用户信息,则表示已登录。未存在用户信息则表示未登录

①登录成功时,获取Session对象,同时将登录信息存储在其中。

  • 此时必须要获取到session用于存储登录信息
     if(flag){
            //获取session对象,没有则创建
            HttpSession session = request.getSession();
            session.setAttribute("username",username);
            //跳转到部门列表页面
            response.sendRedirect(request.getContextPath()+"/dept/list");
        }else {
            //跳转失败页面
            response.sendRedirect(request.getContextPath()+"/error.jsp");
        }

②在servlet中(跳转部门列表的请求中),判断是否取得当前session,获取到表示已登录,获取不到表示未登录

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //此处不一定要获取到Session对象,有表示曾登录过,无表示未登录过
        HttpSession session = request.getSession(false);
        //获取session对象中的username属性
        String username = (String)session.getAttribute("username");
        if(session!=null && username!=null){
            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/add".equals(servletPath)){
                doAdd(request,response);
            }else if("/dept/update".equals(servletPath)){
                doUpdate(request,response);
            }
        }else {
            //跳转到登录页面,request.getContextPath()为项目路径
            response.sendRedirect(request.getContextPath());
        }
    }
❗访问jsp也会生成session对象

在访问了jsp页面,就会创建session对象,因为jsp的九大内置对象之一,就有session。如果访问jsp时,不想升生成session,只需在jsp页面中添加以下配置指令:

<%@page session="false"%>

六、登录后显示用户名

登录后,如果要在jsp页面中显示用户名,只需要利用jsp内置对象的session对象,取出username属性即可

  • 注意使用jsp的session对象时,要保证其存在
<h1>欢迎<%=session.getAttribute("username")%></h1>

七、安全退出功能

安全退出指将当前的Sesiion对象,进行手动销毁

核心servlet代码如下:

//获取session对象,不需要创建新的,因而为false
HttpSession session = request.getSession(false);
//如果存在session
if(session != null){
  //将其手动销毁
  session.invalidate();
  //跳转至登录页面
    response.sendRedirect(request.getContextPath()+'/login.jsp');
}

八、jsp的指令

8.1 指令的作用

指令的作用:指导当前jsp的翻译引擎如何进行工作

  • 即如何翻译
8.2 常用指令

①include指令

  • 包含指令,用于在jsp中完成静态包含,目前很少被使用

②taglib指令

  • 引入标签库指令,多在JSTL标签库中使用

③page指令

  • 页面指令,用于定义JSP页面的全局属性
8.3指令的使用语法
<%@指令名 属性名=属性值 属性名=属性值....%>
8.4常用的page指令
①session属性
  • 是否启用session对象
<%@page session="true|false"%>
true表示启用jsp的内置对象Session,若无session对象,则创建
false表示不启用jsp的内置对象Session,则当前jsp页面中无法使用内置对象session
若未设置,默认true
②contentType属性
  • 用于设置响应的内容类型
<%@page contentType="text/json"%>
<!--contentType用于设置内容的响应类型,相当于response.setContentType("text/json");-->

<%@page contentType="text/json;charset=utf-8"%>
<!--在设置响应内容类型的同时,也设置响应时采用的字符集-->
③pageEncoding属性
  • 设置响应时采用的字符集
  • 常和pageEncoding搭配使用,也可直接在contentType中直接写 contentType="text/json;charset=UTF-8"
<%@page contentType="text/json" pageEncoding="UTF-8"%>
<!--相当于response.setConteneType("text/json;charset=UTF-8")-->
④import属性
  • 用于导包,导入后该jsp页面可使用包的方法
  • 导多个包,用逗号( , )分隔
<%@page import="java.util.list, java.util.Date"%>
<!-- import属性用于导包-->
⑤errorPage属性
  • 用于配置出错页面
  • 当前jsp页面出错后,就会跳转到配置的出错页面上
    • 路径地址从web根目录开始
<%@page errorPage="/路径地址" %>

【存在问题】:

当设置出错页面时,页面出错会跳转至错误页面,但此刻浏览器和控制台并不会输出错误信息,不利于程序员维护

【解决方法】:

可以启用页面的exception内置对象,其表示刚刚发生的异常对象

⑥isErrorPage属性
  • 设置该页面exception对象
    • exception为jsp的九大内置对象之一
  • true表示启用
  • false表示不启用
  • 默认为false
    • 常与errorPage属性一起搭配,用于设置的出错页面
<%@page isErrorPage="true"%>
<!--启用exception对象-->

<%
  //利用以下语句,打印异常堆栈信息,输出到后台控制台
  exception.printStackTrace();
%>

九、九大内置对象

[注:我使用的是jdk17,因而javax.servlet更名为jakar.servlet]

9.1 pageContext
  • 【页面作用域】

  • 包名 jakarta.servlet.jsp.PageContext pageContext

9.2 request
  • 【请求作用域】
  • 包名 jakarta.servlet.http.HttpServletRequest request
9.3 session
  • 【会话作用域】
  • 包名 jakarta.servlet.http.HttpSession session
9.4 application
  • 【应用作用域】
  • 包名 jakarta.servlet.ServletContext application

-----------------------------以上是四个作用域对象-----------------------------

1️⃣作用域的大小: pageContext<request<session<application

2️⃣四个作用域都有 :setAttribute、getAttribute、removeAttribute方法

3️⃣使用原则:尽可能使用小的域

----------------------------------------结束-----------------------------------------------

9.5 exception
  • 【异常对象】
  • 包名 java.lang.Throwable exception
9.6 config
  • 【配置对象】
    • 即ServletConfig ,表示web.xml文件的配置
  • 包名 jakarta.servlet.ServletConfig config
9.7 page
  • 【当前页面对象=this】

    • 其实是this,表示当前的servlet对象
  • 包名 java.lang.Object page

9.8 response
  • 【响应对象】

    • 负责响应
  • 包名 jakarta.servlet.http.HttpServletResponse response

9.9 out
  • 【输出对象】

    • 负责输出
  • 包名 jakarta.servlet.jsp.JspWriter out

Session机制和Cookie

关于B/S结构的会话机制称为session机制

一、什么是会话session?

  • 会话对应的英文是:session

    • 而session机制其实是一种规范,而不同的语言会对其进行不同的实现
  • 一次会话:用户打开浏览器,进行一系列操作,最终关闭浏览器,整个过程称为“一次会话”。

    • 请求的服务器端的java对象是:session
    • 一个会话会包含多次请求(一次会话对应N次请求)
    • 一个会话对应一个Session对象
  • 【回顾】**一次请求:**用户在浏览器进行一次点击(超链接/按钮),然后跳转,可以粗略认为是“一次请求”。

    • 请求的服务器端的java对象是:request
    • 一次请求对应一个request对象

    java和session都是服务器端的java对象,都在JVM中

二、Session作用

①session主要作用:保存会话信息

  • 如:用户登录成功,该如何将登陆成功的状态一直保存下来,就应该使用session对象保存

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

  • 因为Http是一种无状态协议

    • 无状态:请求时,B和S是连接的,但请求结束后,连接就会中断
      • 即请求瞬间是连接,请求结束后连接断开,大大减轻服务器压力
    • 因而就需要一个对象去存储状态,即session会话对象
  • java的servlet规范中,session对应的类名是:

    • jarkata.servlet.http.HttpSession【jdk17】
    • javax.servlet.http.HttpSession【jdk17以下】

三、为什么用Session保存会话状态?

request请求域(HttpServletRequest)< session会话域(HttpSession) < application应用域(ServletContext)

以上三者都具有getAttribute()【取】和setAttribute()【存】方法

但request作用域和ServletContext应用域,

  • reuquest是一次请求一个对象
  • ServletContext是服务器启动时创建对象,服务器关闭时销毁对象,该ServletContext对象只有一个

因而request域太小,ServletContext域太大,最适合的还是session

四、session对象的使用

①获取session对象代码如下:

//从WEB服务器中获取当前session对象,如果未获取session对象,则新建
HttpSession session = request.getSession();
//从Web服务中获取当前session对象,如果未获取到session对象,不新建则返回null
HttpSession sesssion = request.getSession(false);

②存储数据,获取数据

session.setAttribute("存储名",存储值名);
session.getAttribute("设置取到值的名",取值名);

五、session对象的实现原理

5.1实现原理

1️⃣在web服务器中有一个session列表。

类似于map集合:

该map集合的key存储的是sessionid

该map集合的value存储的是session对象

**2️⃣当用户打开浏览器,第一次发起请求时:**服务器会创建一个新的session对象,同时为session对象生成一个session的id,然后web服务器会将session的id发送到浏览器,浏览器再将session的id保存到浏览器缓存中。

**3️⃣用户第二次发起请求时:**会自动将浏览器缓存中的sessionid自动发送给web服务器,服务器获取sessionid,根据sessionid查找其对应的session对象。

用户第N次发起请求,同上.....

4️⃣当用户关闭浏览器后,浏览器缓存中保存的sessionid会消失,下次重新打开浏览器后,缓存中找不到sessionid,就自然找不到web服务器中sessionid对应的session对象,而找不到session对象等同于会话结束,web服务器就会重新再创建一个session对象,生成其对应的sessionid,然后存到浏览器缓存中....

6️⃣同时当会话结束,但其对应的session对象并非立刻销毁,因为Http是无状态协议,所以Tomcat会根据会话超时机制,在某个时间后,若用户仍未请求Tomcat中的该session会话对象时,就将为该用户创建的该session对象给销毁。

而其中的sessionid是以cookie的形式存储

其存储在浏览器的内存中

1684042801676

7️⃣浏览器关闭会话一定结束么?

不一定,通常情况下,关闭浏览器后,缓存中的sessionid会消失,即访问web服务器就找不到原先对应的session对象。但有时当浏览器打开一定时间,超过一定时间没有进行操作,虽然并没有关闭浏览器,但根据session的超时机制,它也会销毁其对应的session对象。同时还有手动配置超时时长的情况。

8️⃣手动设置超时时长

如果想要手动设置session超时机制的时长,可在web.xml添加以下代码:

<!--在web.xml添加以下配置,表示设置session的超时机制中,超时时长为30分钟-->
<session-config>
    <session-timeout>30</session-timeout>
</session-config>
5.2注意

①session对象存储在服务器端,由服务器创建,并销毁

②一个session对象一个会话,浏览器打开->关闭

③一次会话内,包含多个请求

如:南京网友多次请求服务器,获取的都是同一个session1对象,前提是在一次会话内

若:第一次请求服务器,不存在session对象,服务器会为其创建一个新的session对象

而:原先的旧session对象并不会马上销毁,而是根据超时机制:当一定时间内,用户仍未访问该session对象,则销毁

④获取当前session对象时,有两种情况

  • 未获取到,则新建

    HttpSession session = request.getSession();

  • 未获取到,则返回null

    HttpSession session = request.getSession(false);

5.3流程图解【一个会话中】

①当用户第一次发起请求时,不存在sessionid,因而服务器为其创建一个session对象,将sessionid响应给浏览器,放入缓存

  • 其中的Jsessionid是以cookie的形式存储

1684043556601

②当用户第N次发起请求时,浏览器缓存以存在sessionid,会请求服务器,获取sessionid对应的session对象

1684043454434

六、Cookie

6.1 Cookie概念

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

不同的路径所提交的cookie不同哦!

  • 如:JSESSION=41C481F208EF1234578
  • 而以上的键值对数据,就是cookie
  • 对于session关联的cookie而言,该cookie保存在浏览器的“运行内存”中
  • 只要浏览器不关闭,用户再次发起请求时,就会自动将运行内存的cookie发送给服务器
  • 而服务器再根据cookie去找到对应的session对象
6.2 cookie组成

http协议中规定,任何一个cookie都是由name和value组成的,且都是字符串类型。

  • java的servlet中,对cookie提供了哪些支持?
    • 一个Cookie类专门表示cookie数据
      • jarkata.servlet.Cookie;【jdk17】
      • javax.servlet.Cookie;【jdk17以下】
  • java怎么把cookie发送给浏览器呢?
    • 利用response.addCookie(cookie)方法
6.3Cookie保存在何处

Cookie最终保存在浏览器客户端上

  • 也可保存在运行内存中(浏览器关闭则消失)
  • 也可保存在硬盘文件中(永久保存)
6.4Cookie的作用

cookie和session机制都是为了保存会话状态

  • cookie是将会话的状态保存在浏览器客户端上 (cookie数据存储在浏览器客户端上)
  • session是将会话的状态保存在服务器端上 (session数据存储在服务器上)

cookie和session存在的意义?因为http协议是无状态 无连接协议。

【经典案例】:京东商城,在未登录情况下,仍可以添加商品至购物车,而下次访问购物车商品仍存在

1️⃣做法:

  • 将购物车中的商品编号放到cookie中,cookie保存在硬盘文件中,即使关闭浏览器。硬盘上cookie还在,再次打开京东商城时,查看购物车,会自动读取本地硬盘中存储的cookie,拿到商品编号,并动态显示。
    • 京东存储购物车的cookie可能如下:productIds = xxx.yyy.zzz 其中为一个个商品编号
    • 注意:如果在浏览器cookie清除掉,是清除对应硬盘的cookie数据,购物车的商品就会消失

【经典案例】:126邮箱的,十天内免登陆

1️⃣做法:

  • 当勾选[十天内免登陆] 登陆成功后,浏览器客户端会保存一个cookie,该cookie会保存将登陆时的用户名和密码信息,保存到本地的硬盘文件中,十天有效,当第二次访问该地址,浏览器自动直接从硬盘文件中取得对应cookie发送给浏览器,浏览器接收到cookie后,获取用户名和密码,自动进行登录
6.5cookie属于谁

cookie机制和session机制,属于http协议的一部分,php开发中也有cookie和session机制,只要做web开发,都会使用到。

6.5浏览器何时发送Cookie

http协议中规定:当发送请求时,会自动携带该path下的cookie数据给服务器

6.6发送Cookie给服务器

利用response对象的addCookie()方法,设置不同的关联路径(URL),选择在哪些路径中携带cookie数据,最后发送给服务器

①关于cookie的关联路径:

【案例】将cookie从服务器端发送到客户端上
  • 主要实现服务器生成cookie,然后将cookie响应给浏览器,浏览器接收cookie,将cookie展示在客户端上

①编写jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="<%=request.getContextPath()%>/cookie/generate">服务器生成cookie,然后cookie响应给浏览器,浏览器接受cookie,再将cookie放到客户端中</a>
  </body>
</html>

②编写对应的servlet代码

@WebServlet({"/cookie/generate"})
public class GenerateCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //创建Cookie对象
        Cookie cookie = new Cookie("productid","1234567891245");
        //设置cookie的失效时间
        cookie.setMaxAge(30);
        //设置cookie的关联路径
        cookie.setPat(request.getContextPath());
        //将Cookie响应到浏览器上
        response.addCookie(cookie);
    }
}

③访问编写好的servlet请求路径即可

1684128269676

6.7服务器接收Cookie

**接收时,利用request对象的getCookie方法接收**该路径携带的cookie数据,返回类型为String[]

①接收cookie

  • 返回值为string[],若浏览器未提交cookie,返回null
//通过request对象,接收浏览器发来的cookie
Cookie[] cookies = request.getCookies();
【案例】通过java程序接收浏览器发送过来的cookie

①编写页面,跳转至servlet

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="<%=request.getContextPath()%>/cookie/send">接收浏览器发来的cookie,在java中接收,同时遍历cookie数据</a>
  </body>
</html>

②编写servlet,通过request对象的方法接收发来的cookie,同时进行遍历

@WebServlet({"/cookie/send"})
public class SendCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取浏览器发送到服务器的cookie数据
        //注意:如果浏览器没有提交cookie,则返回null
        Cookie[] cookies = request.getCookies();
         //如果不是null,则遍历取出cookie
        if(cookies != null){
             for (Cookie cookie : cookies){
                 String name = cookie.getName();
                 String value = cookie.getValue();
                 System.out.println(name+"+"+value );
             }
        }
    }
}

③访问路径,再查看控制台信息

1684133354765

6.8Cookie有效时间

①设置cookie的有效时间

cookie.setMaxAge()
  • 未设置cookie的有效时间:默认保存在浏览器的运行内存中(浏览器关闭则消失)

  • 若设置cookie的有效时间>0

    • 表示一定会保存在硬盘文件中
  • 若设置cookie的有效时间=0

    • 表示该cookie被删除

    • 使用场景:多用于删除同名cookie

  • 若设置cookie的有效时间<0

    • 表示该cookie不会被存储
      • 仅不会被存储到硬盘文件中,会放在浏览器运行内存中
      • 和不调研serMaxAge()一个效果

②创建cookie

Cookie cookie = new Cookie("存储在cookie的数据名",存储的数据);
6.9实现十天内免登陆功能
6.9.1效果图

类似于126邮箱的30天免登陆

6.9.2思路

①首先实现基本的登录功能

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

②修改登录页面,添加勾选复选框[十天内免登陆]

    <form action="<%=request.getContextPath()%>/user/login" method="post">
        用户名<input type="text" name="username" value="" >
        <br>
        密码<input type="text" name="password" value="">
        <br>
        <input type="checkbox" name="remember" value="1">十天内免登陆<br> 
        <button type="submit" >登录</button>
    </form>

③修改Servlet中的login方法

  • 若勾选
    • 则在登录时,创建cookie
    • 同时将用户名和密码进行存储,同时设置过期时间为10天
    • 同时设置cookie的关联路径,使其一访问该路径,就自动携带设置的cookie数据
    • 最后将cookie响应给浏览器
      • 浏览器就会将其自动保存到硬盘文件当中10天
  • 若未勾选,则正常登录跳转
      if(flag){
            //获取Session
            HttpSession session = request.getSession();
            session.setAttribute("username",username);
            String remember = request.getParameter("remember");
            if("1".equals(remember) ){//如果用户点击十天免登陆
             //创建Cookie对象存储用户名,密码
                Cookie cookie1 = new Cookie("username",username);
                Cookie cookie2 = new Cookie("password",password);//真实情况需要加密
            // 设置失效时间 十天
                cookie1.setMaxAge(60*60*24*10);
                cookie2.setMaxAge(60*60*24*10);
            //设置关联路径
                cookie1.setPath(request.getContextPath());
                cookie2.setPath(request.getContextPath());
             //响应cookie给浏览器
                response.addCookie(cookie1);
                response.addCookie(cookie2);
            }
            //跳转到部门列表页面
            response.sendRedirect(request.getContextPath()+"/dept/list");
        }else {
            //跳转失败页面
            response.sendRedirect(request.getContextPath()+"/error.jsp");
        }

④在web.xml配置文件中,设置欢迎页,跳转到servlet请求中

<!--    会访问welcome,不用添加/,直接从根路径开始-->
    <welcome-file-list>
        <welcome-file>welcome</welcome-file>
    </welcome-file-list>

⑤编写WelcomeServlet

  • 用户再次访问时:

    • 获取cookie,遍历得到其中的用户名username和密码passwrod两个cookie值
    • 若都取到了,进行登录的操作,若成功->跳转部门列表页面
    • 若未取到,说明之前并未勾选[十天免登陆]/cookie有效期到,跳转登录页面
  • 要么跳转部门列表页面

  • 要么跳转登录页面

@WebServlet({"/welcome"})
public class WelcomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Cookie,不可能size=0,只可能size>0,或为null
        Cookie[] cookies = request.getCookies();
        String username = null;
        String password = null;
//如果获取的cookie不为空
        if(cookies != null){
            //循环遍历cookie,得到username和password
            for(Cookie cookie : cookies){
                String name = cookie.getName();
                if("username".equals(name)){
                    username = cookie.getValue();
                }else if("password".equals(name)){
                    password = cookie.getValue();
                }
            }
            //如果获取的username和password都不为空,跳转至部门列表页面
            if(username !=null && password != null){
                //进行登录的操作
                Connection conn = null;
                PreparedStatement ps = null;
                ResultSet rs = null;
                Boolean flag = false;
                try {
                    conn = DBUtils.getConnection();
                   String sql = "select * from user where username = ? and password = ?";
                    ps = conn.prepareStatement(sql);
                    ps.setString(1, username);
                    ps.setString(2, password);
                    System.out.println(sql);
                    System.out.println(ps);
                    rs = ps.executeQuery();
                    System.out.println(rs);
                    if(rs.next()){
                        flag = true;
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }finally {
                    DBUtils.close(conn,ps,rs);
                }
                if(flag) {
                    //获取Session
                    HttpSession session = request.getSession();
                    session.setAttribute("username", username);
                    response.sendRedirect(request.getContextPath()+"/dept/list");
                }
            }else {
                //否则跳转登录页面
                response.sendRedirect(request.getContextPath()+"/index.jsp");
            }
        }

    }

⑥到此,十天免登录功能完成

  • 同时:只有修改密码,或删除cookie的硬盘文件,十天免登陆才会失效

可访问浏览器,测试点击十天免登陆后成功登录时,再次访问该路径,是否直接跳过登录页面,进入到部门列表

1684139887952

1684139874141

七、cookie禁用

cookie禁用是指,服务器正常发送cookie给浏览器,但浏览器拒收。

  • cookie禁用了,sessionid就会一直找不到,因此每次请求都获取到新的session对象
  • 同时每次请求生成的session,只能利用超时机制去销毁

当cookie禁用,该怎样实现session机制?

  • 利用URL重写机制

    • 在路径后添加分号 (;),然后将sessionid的值拼接过去

    • 但使用该机制,会大大提高开发者成本,开发者在编写所有请求路径时,都要添加一个Sessionid

      因而正常若禁用cookie,就干脆不让用户使用

localhost:8080/项目名/访问资源;jsessionid=xxxxxxxxxxxxxxx

八、Javaweb域对象

①一共有以下三个域对象:

  • request(对应类名: HttpServletRequest)
    • 请求域 [ 请求级别 ]
  • session(对应类名: HttpSession)
    • 会话域 [ 会话级别 ]
  • applicaion(对应类名: ServletContext)
    • 应用域 [ 项目级别 ]

②三个域的大小关系:

  • request<session<application

③三者都有以下的公共方法:

  • setAttribute(向域当中绑定数据)
  • getAttribute(向域当中获取数据)
  • removeAttribute(移除域中的数据)

④使用原则:尽量使用小的域

EL表达式

一、EL表达式的作用

EL(Expression Language)表达式语言,可以算是jsp语法的一部分,它有以下功能:

  • EL可以代替jsp中的java代码,使jsp文件的程序更加整洁、美观
    • 原先的jsp夹杂了许多<% java代码 %>、<%= java代码%>,导致jsp中既有html又有java,十分混乱
主要作用:进行页面展示

(从域中取+输出到浏览器)从某个域中取数据,然后转换为字符串,输出到浏览器

因而要利用EL表达式取数据,首先必须得存储在域中

①作用一:从某个域中取数据

  • 四个域
    • pageContext
    • request
    • session
    • application

②作用二:将取出的数据转为字符串

  • 如果是一个java对象,也会自动调用java对象的toString()方法将其转换为字符串

③作用三:将字符串输出到浏览器

  • 同jsp的:<%=%>一致,将其输出到浏览器

二、EL表达式的基本语法

  • 注意:不能加双引号 “ ”,会被看做字符串

  • 面试题: abc和���和{"abc"}有什么区别?

${abc}表示从某个域中取数据,并且被取的数据name为“abc”,之前一定有该语句:域.setAttribute("abc",对象)

${“abc”}表示将“abc”作为字符串输出到浏览器,而并非从某个域中取到

①第一步:声明当前页面的字符集编码形式

<%page contentType="text/html;charset=UTF-8"%>

②第二步:通过作用域.setAttribute存数据

request.setAttribute("存的值",存储的名)

③第三步:通过EL表达式取数据,并输出浏览器

${xx.xx}
2.1从域中取数据并展示

直接在${}中写“存储的数据名“,servlet中利用request.setAttribute("存储的数据名",存储的数据)

【示例】

2.1.1取出单个的值

1️⃣语法

 ${取值的名}

2️⃣示例

<%
request.setAttribute("username","zhangsan")
%>
<!--取出userename的值-->
${username}
等同于<%=request.getAttribute("username")%>
2.1.2取出实体类对象的属性值

1️⃣语法

  • 不带引号,会将其看做变量。

  • 带双引号,则会去找 对象的对应属性

    • 常用于要取的属性name含有特殊字符:xxx.xx的情况

      eg:${requestScope["adb.de"]}可取出属性名为 adb.de的值

 ${ 对象名.属性名 }
 ${ 对象名[" 属性名 "] }

2️⃣底层原理

  • 先创建一个符合javabean规范的User类

  • 底层上:是调用了其属性名对应的getter方法

    • 注意:若没有get方法,则报异常
      • 没有按驼峰命名法编写的get方法,不报异常,但编码规范不建议这样写
  • 但是EL省略get同时小写 ≈ 属性名

    • 相当于一个username---》EL会将其➕get,再将u->U-》getUsername()

3️⃣示例

<%
 User user = new User();
 user.setUsername("jackjson");
 user.setPassword("1234");
 user.setAge(50);
//数据必须存储在某个域的范围内,因为EL表达式只能从某个域中取数据
 request.setAttribute("userObj",user);
%>
<!--利用EL表达式取-->
<!--输出内容:EL表达式会调用对应的toString()方法-->
${userObj}
<!-- ${userObj}底层是从域中取出userObj对象,然后调用其toString()方法-->


<!--输出内容:实体类对应的属性名username的值-->
${userObj.username} 
${userObj["username"]}
使用这些语法的前提是:userObj对象有getUsername()方法
2.1.3取出两个关联实体类对象的属性

1️⃣语法

${实体类.对象名.属性值}

2️⃣示例

  • 如:User类中含有Address类型属性,怎样取出user其中所在的城市呢?
    • ${user.addr.city}
<%                                           class User{
 //创建User对象                                 private String username;
 User user = new User();                       private String password;
 user.setUsername("jackjson");                 private int age;
 user.setPassword("1234");                     private Address addr;
 user.setAge(50);                              .......对应的get、set方法
                                             }
 //创建Address对象	
 Address a = new Address();                 class Address{
 a.setCity("北京");                           private String city;
 a.setStreet("大兴区");                       private String street;
 a.setZipcode("1111");                       private String zipcode;
                                            ....对应的get、set方法
 //设置User对象的address                     }
 user.setAddr(a);
 
//将user对象存入域中
 request.setAttribute("userObj",user);
%>
<!-- 利用EL表达式 -->
<!--取出user对象是哪个城市的-->
${userObj.addr.city}   相当于user对象.getAddr().getCity()
     
     
     
<!--取出user对象是哪个街道的-->
${userObj,addr.street}   相当于user对象.getAddr().getStreet() 
2.1.4从Map集合中取数据

1️⃣语法

  • 通过key来获取
    • 底层仍然调用其getXxx方法
${集合名.对象名.属性名}

2️⃣示例

①先向域中存储一个Map类型的数据
<%

 Map<String,User> userMap = new HashMap<>();
 User user = new User();
 user.setUsername("lisi");
 userMap2.put("user",user);
 request.setAttribute("hhh",userMap2);
 
%>
②将域中的数据取出
<!--取出map的数据-->
${hhh.user.username}
2.1.5从数组中取数据

1️⃣语法

  • 直接输出数组对象
    • 底层调用其toString()方法
${nameArray}
  • 输出数组对象中的某个值
    • 通过下标来获取
    • ❗:即便下标越界,仅取不出数,输出内容为空
      • 因为EL表达式会对空进行处理
${nameArray[下标值]}

2️⃣示例

<%
   //①字符串数组对象
   String[] usernames = {"zhangsan","lisi","wangwu"};
   //向域中存储数组
   request.setAttribute("nameArray",usernams);
   //②实体类数组对象
   User u1 = new User();
   u1.setUsername("zhangsan");
   
   User u2 = new User();
   u2.setUsername("lisi");
   
   User[] user = {u1,u2};
   request.setAttribute("userArray",users);
%>

${nameArray[0]} 
<!--取出nameArray数组的第一个-->

${userArray[1].username} 
<!--取出userArray数组的第二个,其中的username值-->
2.1.6从List集合中取数据

1️⃣语法

  • 下标值从0开始
${list列表名[下标值]}

2️⃣示例

<%
   List<String> list = new ArrayList<>();
   list.add("abc");
   list.add("def");
   request.setAttribute("myList",list)
%>

${myList}
${myList[0]}
${myList[1]}
2.1.7从Set集合中取数据

1️⃣语法

2️⃣示例

<%
  Set<String> set = new HashSet<>();
  set.add("a");
  set.add("b");
  request.setAttribute("set",set);
%>

三、向域中取值的优先级

四个域存储数据的属性名一致的情况,取值的优先级是?

优先从小范围内读取数据

3.1 未指定范围时
  • 默认的取值优先级是:pageContext->reuqest->session->application (从小范围域-->大范围域)
3.2 指定范围时
  • EL表达式有四个隐含的隐式的范围对象:

    • pageScope
    • requestScope
    • sessionScope
    • applicationScope
  • 可利用隐含的范围对象,进行指定

    ${pageScope.data} 从pageContext域中获取data数据

    ${requestScope.data} 从request域中获取data数据

    ${sessionScope.data} 从session域中获取data数据

<%
    //四个域都存储了数据,且name相同
    pageContext.setAttribute("data","pageContext");
    request.setAttribute("data","request");
    session.setAttribute("data","session");
    application.setAttribute("data","application");
%>

${data}
<!--从小范围到大范围中取:pageContext->request->session->application-->
  • 在实际开发中,name都不相同,所以xxScope一般可省略

四、EL表达式对空值处理

EL表达式对空值会进行处理,如果利用request.getAttribute("")取值为空时,会输出null,而EL表达式输出,会将null变为一串空字符串,对应页面的展示更为友好。

即EL表达式表面为${},其实还是转化为jsp代码,再翻译为java去执行

${username}
===================等同于============
<%=request.getAttribute("username) == null ? "":request.getAttribute("username")%>

五、EL表达式指令

5.1 isELIgnored
  • 表示是否忽略该页面中所有的EL表达式
    • true 表示不忽略EL表达式
      • 页面的EL表达式会当做普通字符串输出
    • false 默认为false,表示启用EL表达式
<%@page contentType="text/html;charset=UTF-8" isELIgnored="true"%>
  • 若只想忽略某个EL表达式语句,在前加反斜杠\
\${username}

六、EL表达式常用隐式对象

6.1pageContext

(1)作用

其常用于获取应用的根路径:${pageContext.request.contextPath}

【省流】:EL中需要获取request对象,而非直接得到,用pageContext隐含对象的getRequest()方法得到

jsp中九大内置对象就有pageContext,与EL表达式的隐式对象pageContext是同一个。

①jsp中获取request对象有两种方法:

①request
②pageContext.getRequest()
在jsp中的getRequet()似乎沒用,但其实是为在EL表达式中使用作铺垫的

②EL表达式中获取request对象:

  • 利用pageContext隐式对象的getRequest()方法
${pageContext.request}
<!--别忘了EL表达式中.request就是调用对应的getRequest()方法哦!-->

通过pageContext隐式对象的getRequest()方法得到request对象后,就可以调用.一切request相关的方法,只需注意EL表达式中getXxx()-->xxx即可

③获取应用的根路径

①jsp中可以写成
<!--此处需要强转,因为只有HttpSerlvet才有getContextPath()这个方法-->
<%=(HttpServeltRequest)pageContext.getRequest()).getContextPath() %>
<%=request.getContextPath()%>
②对应的EL表达式为
${pageContext.request.contextPath}
6.2param

(1)作用

用于获取页面路径中,请求参数一位数组的第一个参数

(2)语法

${param.参数名}

(3)示例

若用户在浏览器上提交单值数据:http://localhost:8080/jsp/15.jsp?username=lisi

①jsp中:
用户名:<%=request.getParameter("username")%>
②EL表达式中:
用户名:${param.username}

若用户在浏览器上提交多值数据:http://localhost:8080/jsp/15.jsp?aihao=study&aihao=dance&aihao=read

①jsp中
爱好:<%=request.getParameter("username")%>
②EL表达式中:会返回第一个值,正常应使用paramValues
爱好:${param.aihao}
6.3paramValues

(1)作用

获取页面路径中,请求参数一位数组的所有参数值

(2)语法

${paramValues.参数名}

(3)示例

若用户在浏览器上提交多值数据:http://localhost:8080/jsp/15.jsp?aihao=study&aihao=dance&aihao=read

①jsp中
爱好:<%=request.getParameterValues("aihao")%>
②EL表达式中
<!--获取的是一位数组-->
爱好:${paramValues.aihao}
<!--获取数组中的元素-->
爱好:${paramVlaue.aihao[0]}
6.4 initParam

(1)作用

获取web.xml中配置的ServletContext的初始化参数

ServletContext是Serlvet上下文对象,对应的JSP九大内置对象中的:application

(2)语法

initParam.属性名

(3)示例

web.xml

<context-param>
    <param-name>pageSize</param-name>
    <param-value>20</param-value>
</context-param>
<context-param>
    <param-name>pageNum</param-name>
    <param-value>5</param-value>
</context-param>

①jsp获取ServletContext中的初始化参数

<%
   String pageSize = application.getInitParameter("pageSize");
   String pageNum  = application.getInitParameter("pageNum");
%>
记录条数:<%=pageSize%>
页码: <%=pageNum%>

②EL表达式获取ServletContext中的初始化参数

记录条数:${initParam.pageSize}
页码: ${initParam.pageNum}

七、EL表达式的运算符

7.1算术运算符
+ - * / %

①+号只进行求和,不进行拼接

  • 因而会将+两旁的转为数字,无法转换则报错
${20+10} //30
${20+"10"} //30
${20+"abc"} //报错
${"asd"+"axs"} //报错
7.2关系运算符
== != < <= > >= eq
①==和eq

EL表达式的这两个运算符,都调用了equals方法,用于比较两个变量的引用是否相同

  • 注意String重写了,是比较内容
${"abc"=="abc"}//true
${"abc"}==${"abc"} //acb==abc
<%
   Object o1 = new Object();
   request.setAttribute("o1",o1);
   request.setAttribute("o2",o1);
%>
${o1 == o2} //true
<%
  String s1 = "abc";
  String s2 = "abc";
  request.setAttribute("a",s1);
  request.setAttribute("b",s2);
%>
${a == b}//false

<%
 String s1 = new String();
 String s2 = new String();
 request.setAttribute("a",s1);
 request.setAttibute("b",s2);
%>
${a == b}//true

<%
   Object o1 = new Object();
   Object o2 = new Object();
   request.setAttribute("o1",o1);
   request.setAttribute("o2",o2);
%>
${o1 == o2}//false
②!=和not

也调用equals方法, 表示取反

  • 使用时要注意优先级,加括号
${!(stu1 eq stu2)}
7.3逻辑运算符
! && || not and or
7.4条件运算符
  • 又称三目运算符
? :

示例:

${empty param.username ? "對不起,用戶名爲空" : "成功登录"}
7.5取值运算符
[] 和 .
7.6 empty运算符

判断是否为空,空->true,非空->false

${empty param.user} 
<!--判断获取到user参数是否为空-->
${not empty param.user}
<!--判断获取到user参数是否不为空-->
${!empty param.user}
<!--判断是否获取到user参数是否不为空-->

${empty param.password == null}
<!--前半部分是boolean类型,与null类型不相等,所以结果为false-->

JSTL标签库

一、什么是JSTL标签库?

JSTL全称Java Standard Tag Lib(java标准的标签库)

JSTL标签库通常结合EL表达式一起使用,目的是消除JSP中的java代码

【JST原理解析】

引入标签库的uri后面路径,指向一个xxx.tld文件

而tld文件实际上是一个xml配置文件

在tld文件中,描述了“标签”和“java”类之间的关系

  • 常用的核心标签库对应的tld文件是?

    • c.tld
  • tld文件在哪?

    • 在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有c.tld文件

    1684725710788

  • 源码解析:配置文件tld

<tag>
   <description>
       对该标签的描述
    </description>
    <name>标签名</name>
    <tag-class>标签对应的java类</tag-class>
    <body-content>标签体中可以出现的内容</body-content>
        <!--如果是jsp,表示该标签体中可以出现所有符合jsp语法的代码,如EL表达式-->
    <!--属性-->
    <attribute>
        <description>对属性的描述</description>
        <name>属性名</name>
        <required>是否为必须,false非必须,true表示必须</required>
        <rtexprvalue>该属性是否支持EL表达式</rtexprvalue>	
    </attribute>
</tag>
<c:forEach items="属性"> 标签
    标签体
</c:forEach>
【旧版jar包下载教程】

jar包下载地址:**http://tomcat.apache.org/taglibs/standard/

①点击download,耐心等待跳转页面

②下载taglibs-standard-impl-1.2.5.jar 和 taglibs-standard-spec-1.2.5.jar

  • 在下载页面中一共有4个jar包:

    Impl: taglibs-standard-impl-1.2.5.jar JSTL实现类库

    Spec: taglibs-standard-spec-1.2.5.jar JSTL标准接口

    EL: taglibs-standard-jstlel-1.2.5.jar JSTL1.0标签-EL相关

    Compat: taglibs-standard-compat-1.2.5.jar 兼容版本

  • 从README得知:

    如果不使用JSTL1.0标签,可以忽略taglibs-standard-jstlel包, README没有介绍taglibs-standard-compat

    包,估计应该是兼容以前版本

1684909765877

【新版jar包下载教程】

jakarta.servlet.jsp.jstl-2.0.0.jar下载地址

jakarta.servlet.jsp.jstl-api-2.0.0.jar下载地址

点击以上两个链接,耐心等待,即可直接下载,注意根据项目使用的jdk版本,需要使用引入对应的jar包,否则运行项目,会出现报错。

1684914472609

二、使用步骤

  • 第一步:引入JSTL标签库对应的jar包

    • 在IDEA中如何引入?

      【IDEA->WEB-INF下新建lib目录,jar包拷贝到lib中,Add lib】

    • ❗引入时,注意保持jar的版本一致,统一为tomcat10之前/之后

    • 什么时候需要将引入的jar放入lib目录下?

      当引入的jar包是Tomcat中没有的,必须放在lib目录下

    • tomcat10之后:

      jakarta.servlet.jsp.jstl-2.0.0.jar

      jakarta.servlet.jsp.jstl-api-2.0.0.jar

    • tomcat10之前:

      javax.servlet.jsp.jstl.jar

      taglibs-standard-impl-1.2.5.jar

      taglibs-standard-spec-1.2.5.jar

  • 第二步:在jsp页面中引入要使用的标签库(采用taglib指令)

    • JSTL提供了很多标签库,所以使用时需指定
<!--核心标签库-->
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!--格式化标签库:负责格式化操作-->
<%@taglib prefix="fmt" uri="http://java.sum.com/jsp/jstl/fmt"%>
<!--sql标签库-->
<%@taglib prefix="sql" uri="http://java.cum.com/jsp/jstl/sql"%>
  • 第三步:在需要使用标签的位置使用即可
    • 标签是jstl标签,实际上是java程序
示例:
<%@page contentType="text.html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sum.com/hsp/jstl/core"%>
<%
  //创建List集合
  List<Student> stuList = new ArrayList<>();
  //创建Stduent对象
  Student s1 = new Student("110","经常");
  Student s2 = new Student("220","获取");
  Student s3 = new Student("330","消防车");
  //添加到List集合中
  stuList.add(s1);
  stuList.add(S2);
  stuList.add(s3);
  //将list集合存储到请求域中
  request.setAttribute("stuList",stuList);
%>

<!--遍历stuList集合的数据,并输出到浏览器-->

<!--①使用java代码-->
<%
  List<Strudent> stuList = (List<Student>)request.getAttribute("stuList");
  for(stu : stuList){
%>
  id:<%=stu.getId()%>,name:<%=stu.getName()%>
<%
  }
%>

<!--②使用jstl标签库:forEach标签-->
<c:forEach items="stuList" var="s">
   id:${s.id}
   name:${s.name}
</c:forEach>

三、常用标签

3.1 forEach标签

1️⃣作用

forEach标签用于循环

2️⃣语法

①遍历集合

  • items:
    • 要迭代的集合
    • 非必须
    • 支持EL表达式
  • var
    • 迭代时的每一项元素
      • 即别名
  • varStatus
    • 表示var的状态对象
      • 是一个java对象
    • 具有count属性值,主要用于编号
      • 从1开始,以1递增
<%@page contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%>
<%
  //创建List集合
  List<Student> stuList = new ArrayList<>();
  //创建Stduent对象
  Student s1 = new Student("110","经常");
  Student s2 = new Student("220","获取");
  Student s3 = new Student("330","消防车");
  //添加到List集合中
  stuList.add(s1);
  stuList.add(S2);
  stuList.add(s3);
  //将list集合存储到请求域中
  request.setAttribute("stuList",stuList);
%>
<c:forEach items="stuList" var="s" varStatus="stuStatus">
    编号:${stuStatus.count}
    id:${s.id}
</c:forEach>

②遍历变量

  • var:

    • 用于指定循环时的变量
  • begin

    • 开始位置
  • end

    • 结束位置
  • step

    • 步长
      • 类同每次+几
<c:forEach  var="i" begin="1" end="10" step="1">
  ${i}
</c:forEach>
输出:1 2 3 4 5 6 7 8 9 10

3️⃣示例

<%@page contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%>
<%
  //创建List集合
  List<Student> stuList = new ArrayList<>();
  //创建Stduent对象
  Student s1 = new Student("110","经常");
  Student s2 = new Student("220","获取");
  Student s3 = new Student("330","消防车");
  //添加到List集合中
  stuList.add(s1);
  stuList.add(S2);
  stuList.add(s3);
  //将list集合存储到请求域中
  request.setAttribute("stuList",stuList);
%>
<c:forEach items="stuList" var="s">
  id:${s.id}
  name:${s.name}
</c:forEach>
3.2 if标签

1️⃣作用

if标签用于判断

2️⃣语法

  • test:

    • 判断的条件,为真执行,为假不执行

    • 是必须的

    • 支持EL表达式

    • 只能是Boolean类型

  • var:

    • 每次判断的结果值 true/false

      • 存储在域中
    • 非必须的

    • 支持EL表达式

  • scope:

    • 用来指定var的存储域
      • 有四个域:page(pageContext域)、request(request域)、session(session域)、application(application域)
<c:if test="" var="" scope="">
</c:if>

3️⃣示例

<%@page contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%>
<c:if test="${empty param.username}">
    如果param中的username为空,则显示该句话
</c:if>
<c:if test="${not empty param.user}">
    如果param中的username不为空,则显示这句话
</c:if>
<!--无else标签时,可用两个if替代-->
3.3 choose标签

1️⃣作用

多分支条件选择,"当....执行....;否则...."

2️⃣语法

  • test
    • 表示条件判断语句
<c:choose>
  <c:when test=""></c:when>
  <c:when test=""></c:when>
  <c:when test=""></c:when>
  <c:otherwise>
  
  </c:otherwise>
</c:choose>

3️⃣示例

<%@page contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sum.com/jsp/jstl/core"%>
<c:choose>
  <c:when test="${param.age < 18}">
   青少年
  </c:when>
  <c:when test="${param.age < 35 }">
   青年
  </c:when>
  <c:when test="${param.age < 50}">
   中年
  </c:when>
  <c:otherwise>
  老年
  </c:otherwise>
</c:choose>

四、利用jstl改造oa系统

4.1使用技术

​ 利用servlet+jsp+EL表达式+JSTL标签库

1️⃣EL表达式

将项目上下文路径由jsp的<%=request.getContextPath()%>变为${pageContext.request.contextPath}

2️⃣JSTL标签库

  • 先向页面引入

  • <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

forEach、if、choose标签,将jsp页面的循环,变为< c:forEach>< /c:forEach>+EL表达式${xx.xx}取值

3️⃣利用EL表达式和JSTL标签库,都是为了将jsp页面中的java代码尽可能的减少

因而只在jsp页面中添加改造的代码

4.2改造系统功能代码
  • 在此以list列表页为例
(1)改造前

1684915462733

(2)改造后

1684915415875

4.3改造页面路径代码(base标签)

(1)base标签

  • 作用:设置整个页面的基础路径

  • 常用写法:

    • 写死的

      http://localhost:8080/oa
    • 动态的

      协议://服务器IP:端口号/项目根路径/
      ${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/
      • 协议 ${pageContext.request.scheme}
      • 服务器IP ${pageContext.request.serverName}
      • 端口号 ${pageContext.request.serverPort}
      • /项目根路径 ${pageContext.request.contextPath}

在HTML代码中,有一个base标签,能设置整个网页的基础路径

  • 默认会将base标签中的路径,添加在没有以"/"开始的路径前
<title>首页</title>
<base href="http://localhost:8080/oa">
<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"> 
<!--设置整个网页的基础路径写法有以上两种
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/386921
推荐阅读
相关标签