• 11 JSP


    Servlet :职责

    1. 接受请求

    2. 获得请求参数

    3. 业务处理( 不直接做 ) 外包Servlet

    4. 响应浏览器( 打标签 ) 外包给JSP

    一、概述

    1.1 什么是JSP?

    JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTMLXML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。

    1.2 为什么学jsp?

    使用Servlet,实现动态网页,那么需要编写大量的输出语句,手工拼接网页内容,相当恶心。
    JSP将Java代码和特定变动内容嵌入到静态的页面中,实现以静态页面为模板,动态生成其中的部分内容。JSP引入了被称为“JSP动作”的XML标签,用来调用内建功能。另外,可以创建JSP标签库,然后像使用标准HTML或XML标签一样使用它们。标签库能增强功能和服务器性能,而且不受跨平台问题的限制。
    JSP文件在运行时会被其编译器转换成更原始的Servlet**代码。**JSP编译器可以把JSP文件编译成用Java代码写的Servlet,然后再由Java编译器来编译成能快速执行的二进制机器码,也可以直接编译成二进制码。

    二、JSP 快速上手

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>第一个JSP页面title>
      head>
      <body>
      Hello JSP
      body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    JSP 文件后缀 .jsp jsp和html没太大区别,多了一些jsp专属的成分。

    三、JSP 脚本

    3.1 注释脚本

    <%-- 服务器注释脚本 --%>

    我是张家辉

    服务器注释脚本 源代码看不见 html 注释前端源代码可见

    3.2 普通脚本★

    <%
           for(int i=1;i<=9;i++){
               for(int j=1;j<=i;j++){
                    out.println( i+"*"+j+"="+(i*j));
               }
               out.println("<br/>");
           }
     %>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    方便在jsp页面中嵌入 程序逻辑代码

    3.3 定义脚本

    <%!
        String bookName="葵花宝典";
        public  String getBook(){
            return bookName;
        }
    %>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    用来定义变量或者方法

    3.4 表达式脚本★

    <div style="color: red">
        <%=bookName%>
    div>
    <div style="color: red">
        <%=getBook()%>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出变量值,或者函数调用的返回值

    四、JSP 原理

    jsp 本质上就是一个Servlet,但是编写jsp比编写servlet要简单的多,尤其是展现页面的时候,jsp可以直接在页面中编写标签,而Servlet需要通过输出语句拼接。
    用户编写jsp代码最终会被编译成一个Java类(类似于Servlet),你认为很蠢的操作,其实有人帮你做。本质和Servlet拼接原理一致,使用JSP表面写标签,背地里转换成输出语句动态输出这些标签代码。

    package org.apache.jsp;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;
    public final class life_jsp extends org.apache.jasper.runtime.HttpJspBase
        implements org.apache.jasper.runtime.JspSourceDependent,
                     org.apache.jasper.runtime.JspSourceImports {
      public void _jspInit() {
      }
      public void _jspDestroy() {
      }
      public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
          throws java.io.IOException, javax.servlet.ServletException {
          out.write("\r\n");
          out.write("\r\n");
          out.write("\r\n");
          out.write("\r\n");
          out.write("    Title\r\n");
          out.write("\r\n");
          out.write("\r\n");
          out.write("   

    测试原理和生命周期2

    \r\n"
    ); out.write("\r\n"); out.write("\r\n"); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    生命周期和Servlet一致,多了一个环节(编译环节,jsp转换成java代码),第一次请求或者当jsp代码发生改变时,进行编译转换。

    总结: Servlet 和jsp 都是动态网页技术,jsp底层实现就以servlet方式实现的,本质是一样的,只是jsp在展示页面方面要强于servlet。

    五、JSP 指令(三大)

    指令是jsp特有的语法,指令可以增强页面表达的功能,不同指令有不同的含义,在jsp中存在3大指令元素。
    指令的语法
    <%@ commandName attr %>

    5.1 page指令

    <%@ _page _
    contentType=“text/html;charset=UTF-8” // 指定响应页面时的MIME resp.setContentType()
    language=“java” // 声明页面中的脚本语言为java
    import=" import=“com.qfedu.entity.Girl” // 导包
    pageEncoding=“utf-8” // 指定页面的编码类型 resp.setCharacterEncoding(“utf-8”)
    isErrorPage=“true|false”
    errorPage=“” //指定错误页面
    %>

    指定页面的一些配置信息

    5.2 include 指令★

    <%@ include file=“被包含文件的路径” %>

    指令包含,会把被包含的源代码与自己的代码合成。

    5.3 tagLib 指令★★

    在jsp中除了 html提供的标签以外,官方还提供了其他的标签,这些标签与静态标签不同 ,它们具备逻辑处理能力。比如判断,循环,格式化,等等。所以这个标签主要是引入其他第三方的标签库。
    JSTL:(Java server pages standarded tag library) (jsp的标准标签库,是一个基础的标签库)
    1. 导入jar
    2. 引入
    <%@ taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
    3. 使用

    这里只是会引入即可,后续学习标签库中的内容,jstl 要结合 el表达式才能发挥出功力。

    六、JSP动作[了解]

    jsp 动作可以增强页面行为功能的一组标签,目的为了简化脚本(消灭脚本),原本我们认为在jsp中可以编写java脚本代码,可以很方便的实现动态功能。在jsp 中引入动作可以简化一些脚本代码,使页面内容相对统一,方便维护和升级。
    什么是JavaBean :
    JAVAEE – java企业级版本(企业级应用技术栈 ) JavaBean(描述的就是一个类(泛指),这个类 具备get set 方法(狭义))

    6.1 useBean






    创建一个JavaBean , 简单的说就是java对象 id 对象名称 class 对象类类型 ,底层使用反射机制创建对象。

    6.2 setProperty


    为对象属性赋值 name 对象 property 对象的哪个属性 value 属性值

    6.3 getProperty


    取对象属性值 name 对象 property 对象的哪个属性

    6.4 forward

    等价于servlet中的转发

    6.5 param



    转发时候 携带参数

    6.6 include

    org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “simple.jsp”, out, false); 使用时去读去被包含页面的内容。

    七、JSP内置对象(9大)

    内置对象就是可以在jsp中直接使用的对象,如何理解内置对象呢?这些对象其实是已经声明好了的。因为我们编写的JSP最终会被转换成Servlet代码,那么我们所谓的内置对象其实就是将来Servlet里面的声明好的对象,只是提前使用而已。

    • application ServletContext
    • session HttpSession
    • request HttpServletRequest
    • response HttpServletResponse
    • page this(当前这Servlet 本身的实例 几乎没有使用价值)
    • pageContext PageContext(page域,管理其他3个域,访问其他内置对象)
    • config ServletConfig( jsp 几乎没有使用价值 )
    • out JspWriter (基本不用)
    • exception Throwable(异常对象,只有所谓错误页面才会有异常)

    这些内置对象几乎就是Servlet时期使用过类型对象,那么他们的使用方法,都掌握了。

    7.1 request
    <%-- 取数据 --%>
    <%
    String data =   request.getParameter("data");
    %>
    <%=data%> 
    <%--转发--%>
    <%
    request.getRequestDispatcher("/index.jsp").forward(request,response);
    %>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以前怎么用现在还是怎么用。

    7.2 response
    <%
    response.sendRedirect("http://www.baidu.com");
    %>
    
    • 1
    • 2
    • 3

    以前怎么用现在还是怎么用。

    7.3 exception
    1. 编写错误页面
    <%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
    <html>
    <head>
        <title>Titletitle>
    head>
    <body bgcolor="red">
      <h1>服务器内部异常,服务器离家出走了.......h1>
      <%
          if( exception instanceof NullPointerException ){
              //空指针
              response.sendRedirect("http://www.sina.com");
          }
          if(exception instanceof ArithmeticException){
              //算术异常
              response.sendRedirect("http://www.baidu.com");
          }
      %>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    isErrorPage=“true” 标记一个页面为错误页面

    1. 指定错误页面
    <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="server_error.jsp" %>
    <html>
      <head>
        <title>$Title$title>
      head>
      <body>
         <%
            // 模拟错误
            out.println("Hello world");
            int i = 1/0;
         %>
      body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    errorPage=“server_error.jsp”

    7.4 作用域使用

    在jsp中存在4个作用域其中3个是Servlet阶段学习过的(application session request page ) ,域的作用是为了传递数据,在开发中我们可以利用Servlet 处理请求 利用JSP渲染数据。而作用域起到的就是两者间的数据传递问题。

    需要注意的是 跳转方式不同,对域的选择就不同,比如重定向就不可以使用request 域。

    Servlet 接受请求-获得数据-跳转

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获得数据(可以从数据库查)
        User user1 = new User("王震",80);
        User user2 = new User("刘杰",90);
        User user3 = new User("天宇",100);
        //把数据存入域中
        req.setAttribute("req",user1);
        req.getSession().setAttribute("ses",user2);
        this.getServletContext().setAttribute("app",user3);
        //跳转到页面渲染(jsp 做跟方便)
        //req.getRequestDispatcher("/show.jsp").forward(req,resp);
        //resp.sendRedirect("/show.jsp");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    JSP 从域中获得数据-渲染页面

    req:
    <%=request.getAttribute("req") %>
    session:
    <%=session.getAttribute("ses") %>
    app:
    <%=application.getAttribute("app") %>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.5 pageContext

    理解 pageContext 页面上下文 -> 页面全局-> 可以管理页面上的全部作用域。

    • 1 本身可以表示 page域(当前jsp页面有效,最小的作用域)。
    • 2 可以管理其它域(可以用它操作他其他域)。
    pageContext.setAttribute("xx","罗志祥"); // 默认存入page域
    pageContext.setAttribute("nb","张柏芝",PageContext.SESSION_SCOPE);//把数据存入指定域
    pageContext.getAttribute("xx")  //默认从page域中获取数据
    pageContext.getAttribute("req", PageContext.REQUEST_SCOPE ) // 从指定域中获得数据
    
    • 1
    • 2
    • 3
    • 4

    特定域中获取数据可设置数据,通过 几个常量表示域类型:
    PageContext.PAGE_SCOPE
    PageContext.REQUEST_SCOPE
    PageContext.SESSION_SCOPE
    PageContext.APPLICATION_SCOPE

    7.6 pageContext搜索
    <%--pageContext搜索用法 :  --%>
    page:
    <%= pageContext.findAttribute("xx")  %>
    req:
    <%= pageContext.findAttribute("req") %>
    session:
    <%= pageContext.findAttribute("ses") %>
    app:
    <%= pageContext.findAttribute("app") %>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    只需要给key 他会依次从page request session application 搜索,找到则立即返回

    八、EL表达式★★★

    EL(Expression Language) 是为了使JSP写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。虽然通过jsp的内置对象,可以很容易管理各种域,但是始终会出现脚本,使用EL结合JSTL标签库就可以彻底消灭脚本。

    8.1 语法

    **${ 表达式|key } **

    8.1 数据类型

    整型 浮点型 字符串 对象 布尔 NULL

    ${123}  => 123
    ${3.14} => 3.14
    
    • 1
    • 2

    8.2 运算符

      • / * % > < >= <= == ! A ?B :C and、&&、or、||、!、not

    8.3 empty

    ${ empty 对象 }

    如果对象为空则返回true 否则false

    8.4 EL内置对象

    1. 获取请求相关
      | 名称 | 作用描述 |
      | — | — |
      | param | 将请求参数名称映射到单个字符串参数值(通过调用 ServletRequest.getParameter (String name) 获得)。getParameter (String) 方法返回带有特定名称的参数。表达式 ${param . name}相当于 request.getParameter (name)。 |
      | paramValues | 将请求参数名称映射到一个数值数组(通过调用 ServletRequest.getParameter (String name) 获得)。它与 param 隐式对象非常类似,但它检索一个字符串数组而不是单个值。表达式 ${paramvalues. name} 相当于 request.getParamterValues(name)。 |
      | header | 将请求头名称映射到单个字符串头值(通过调用 ServletRequest.getHeader(String name) 获得)。表达式 ${header. name} 相当于 request.getHeader(name)。 |
      | headerValues | 将请求头名称映射到一个数值数组(通过调用 ServletRequest.getHeaders(String) 获得)。它与头隐式对象非常类似。表达式 ${headerValues. name} 相当于 request.getHeaderValues(name)。 |
      | cookie | 将 cookie 名称映射到单个 cookie 对象。向服务器发出的客户端请求可以获得一个或多个 cookie。表达式 ${cookie. name .value} 返回带有特定名称的第一个 cookie 值。如果请求包含多个同名的 cookie,则应该使用 ${headerValues. name} 表达式。 |
      | initParam | 将上下文初始化参数名称映射到单个值(通过调用 ServletContext.getInitparameter(String name) 获得)。 |
    <%=request.getParameter("nb")%>
    等价
    ${param.nb}
    
    • 1
    • 2
    • 3

    这些el内置目前没有实际应用机会,这些都应该交给Servlet做。

    1. 获取域相关(重点)
      | pageScope | 将页面范围的变量名称映射到其值。例如,EL 表达式可以使用 ${pageScope.objectName} 访问一个 JSP 中页面范围的对象,还可以使用 ${pageScope .objectName. attributeName} 访问对象的属性。 |
      | — | — |
      | requestScope | 将请求范围的变量名称映射到其值。该对象允许访问请求对象的属性。例如,EL 表达式可以使用 ${requestScope. objectName} 访问一个 JSP 请求范围的对象,还可以使用 r e q u e s t S c o p e . o b j e c t N a m e . a t t r i b u t e N a m e 访问对象的属性。 ∣ ∣ s e s s i o n S c o p e ∣ 将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如: {requestScope. objectName. attributeName} 访问对象的属性。 | | sessionScope | 将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如: requestScope.objectName.attributeName访问对象的属性。∣∣sessionScope将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如:{sessionScope. name} |
      | applicationScope | 将应用程序范围的变量名称映射到其值。该隐式对象允许访问应用程序范围的对象。 |
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
      <head>
        <title>$Title$title>
      head>
      <body>
      <%
         pageContext.setAttribute("xx","小红");
      %>
      <%--使用EL表达式取值--%>
       page: ${pageScope.xx}
       <hr>
       req: ${requestScope.req}
       <hr>
       ses: ${sessionScope.ses}
       <hr>
       app: ${applicationScope.app}
      <%--使用EL表达式的搜索方式取值--%>
      <hr color="red">
      page: ${xx}
      <hr>
      req: ${req}
      <hr>
      ses: ${ses}
      <hr>
      app: ${app}
      body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    推荐直接使用 key 搜索。

    1. 提供了管理其他JSP内置对象的方法(了解)
      | 对象名 | 作用描述 |
      | — | — |
      | pageContext | JSP 页的上下文。它可以用于访问 JSP 隐式对象,如请求、响应、会话、输出、servletContext 等。例如,${pageContext.response} 为页面的响应对象赋值。 |
    <%-- 不同于jsp内置对象 --%>
    ${pageContext.session.id}
    
    • 1
    • 2

    它的作用是 获得jsp的内置对象,可以容易使用它们的api

    到这里重点是使用EL从域中取值${key}
    无标题.png

    九、JSTL标签库★★★

    仅仅使用EL还不足以完全消灭脚本,EL表达式需要配合标签才能发挥出最大开发效率。EL+JSTL 消除脚本。
    https://mvnrepository.com/artifact/javax.servlet/jstl/1.2

    9.1. 使用流程

    1. 拷贝jar到 WEB-INF/lib 同时设置类库 ( as Libary)
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
    • 1
    1. 使用标签

    9.2.forEach标签【列表渲染】

    <%--
        items="" 指定遍历的集合
        var=""  遍历出的每个元素
    --%>
    <c:forEach items="${list}" var="u"  >
        <tr>
            <td>${u.name}td>   
            <td>${u.age}td>
        tr>
    c:forEach>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    遍历集合功能 , 需要给对象get方法。

    9.3 if 标签【条件渲染】

    <%
        int level=3;
        pageContext.setAttribute("grade",level);
    %>
    <c:if test="${grade<=5 and grade>=1}">
           <h6>倔强青铜h6>
    c:if>
    <c:if test="${grade<=10 and grade>=6}">
           <h6>最强王者h6>
    c:if>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    9.4 choose-when

    <%
        int level=3;
        pageContext.setAttribute("grade",level);
    %>
    <c:choose>
        <c:when test="${grade==1}">
            倔强青铜
        c:when>
        <c:when test="${grade==2}">
            不屈清透
        c:when>
        <c:when test="${grade==3}">
            永恒钻石
        c:when>
        <c:when test="${grade==4}">
            最强王者
        c:when>
        <c:otherwise>
            垃圾电脑
        c:otherwise>
    c:choose>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    9.5 日期格式

    <%
        Date birthday = new Date();
        pageContext.setAttribute("birthday",birthday);
    %>
    <span> <f:formatDate value="${birthday}" pattern="dd/MM/yyyy"  /> span>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    十、MVC 设计模式

    10.1 MVC设计思想

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
    MVC 设计就是分层设计,__解耦。

    和3层架构(表示层 业务逻辑层 数据持久层)的关系?它们本身是不同的两种软件分层设计思想。在实际开发中,都使用了,宏观上使用MVC, 细化后也引入了三层架构。

    10.2 JavaWeb发展历史

    Model 1 模式

    JSP+**JavaBean **开发模式(直接在JSP中使用JAVABean) ,不符合MVC思想。js
    image.png

    Model 2 模式

    **Servlet **+ **JavaBean **+ **JSP **开发模式,已经符合MVC模式

    Servlet 作为控制器 Controller
    JSP 作为视图 View
    JavaBean 模型 Model

    image.png

    SpringMVC框架

    使用第三方 开发的软件产品 比如 Struts2 (淘汰) SpringMVC(主流) 它们是MVC实现的具体方案。

    SSH2:(Spring+Struts2+Hibernate) ssh SSM: (Spring + SpringMVC+ MyBatis)

    十一、分页查询

    当用户请求后台数据时,由于数据量非常大,不可能全部一次性给到前端,可以使用分页功能,把数据分块,给用户更好的使用体验。
    image.png

    11.1 编写分页模型类
    /**
     * 分页工具类,描述一页的数据
     */
    public class Pager<T> {
        //数据
        private List<T> list;
        //页码
        private Integer page;
        //大小
        private Integer size;
        //总记录数
        private Integer total;
        //总页数
        private Integer pages;
        //是否有上一下
        private boolean hasNext;
        //是否有上一页
        private boolean hasPrev;
        //指示按钮
        private int[] buttons;
    
        public Pager() { }
    
        public Pager(List<T> list, Integer page, Integer size, Integer total) {
            this.list = list;
            this.page = page;
            this.size = size;
            this.total = total;
            //计算总页数
            this.pages = total % size == 0 ? total / size : total / size + 1;
        }
    
        public List<T> getList() {
            return list;
        }
    
        public Integer getPage() {
            return page;
        }
    
        public Integer getSize() {
            return size;
        }
    
        public Integer getTotal() {
            return total;
        }
    
        public Integer getPages() {
            return pages;
        }
        public boolean isHasNext() {
            return !page.equals(pages);
        }
        public boolean isHasPrev() {
            return !page.equals(1);
        }
        public int[] getButtons() {
            //生成按钮数字 Stream
            if( pages < 10 ){
                buttons = IntStream.rangeClosed(1,page).toArray();
            }else{
               int start = page%10==0? ( (page-1)/10)*10  + 1 :  (page/10)*10  + 1;
               int end = start+9;
               if(end>pages){
                   end = pages;
               }
               buttons = IntStream.rangeClosed(start,end).toArray();
            }
            return buttons;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    11.2 Dao实现分页查询
     public Pager<Singer> findByPageWithCondition(int page, int size, Singer singer) throws Exception {
            //数据集合
            List<Object> params = new ArrayList<>();
    
            //查询数据SQL
            StringBuilder sqlData  = new StringBuilder(  " select * from tb_singer "  );
            //查询总记录数SQL
            StringBuilder sqlCount =  new StringBuilder( " select count(*) from tb_singer " );
            //片段
            StringBuilder whereSQL =  new StringBuilder(" where 1=1  " );
            StringBuilder limitSQL =  new StringBuilder( " limit ? ,? " );
    
            if( singer.getSno()!=null && singer.getSno()!=0 ){
                whereSQL.append( " and sno = ? " );
                params.add( singer.getSno()  );
            }
            if( singer.getSname()!=null && !"".equals(singer.getSname()) ){
                whereSQL.append( " and sname = ? " );
                params.add( singer.getSname() );
            }
            if( singer.getFen()!=null && singer.getFen()!=0 ){
                whereSQL.append( " and fen >= ? " );
                params.add( singer.getFen()  );
            }
            //limit 从句的数据
            params.add( (page-1)*size );
            params.add( size );
            //拼接好的查询数据SQL 和 统计SQL
            String  queryDataSql  = sqlData.append(whereSQL).append(limitSQL).toString();
            String  queryCountSql  = sqlCount.append(whereSQL).toString();
            //分别执行SQL
            List<Singer> list = queryRunner.query(queryDataSql, new BeanListHandler<>(Singer.class), params.toArray());
            Long count = queryRunner.query(queryCountSql, new ScalarHandler<Long>(), params.subList(0, params.size() - 2).toArray());
            //构造查询结果
            Pager<Singer> pager = new Pager<>(list,page,size, count.intValue());
            //返回
            return pager;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    11.3 控制器接受分页请求
    package com.qfedu.mvc.controller;
    
    import com.qfedu.mvc.entity.Singer;
    import com.qfedu.mvc.service.SingerService;
    import com.qfedu.mvc.service.impl.SingerServiceImpl;
    import com.qfedu.mvc.util.Pager;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebServlet("/singer/list2.do")
    public class SingerQueryPlusController extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             doPost(req, resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //接收用户分页请求( 页码   大小 )
            String page = req.getParameter("page");
            String size = req.getParameter("size");
            if(  page==null || "".equals(page)  ){
                page = "1";
            }
            if(  size==null || "".equals(size)  ){
                size = "10";
            }
            int pageNum = Integer.parseInt(page);
            int pageSize = Integer.parseInt(size);
            //用户过虑条件数据
            String sno = req.getParameter("sno");
            String sname = req.getParameter("sname");
            String fen = req.getParameter("fen");
            //封装查询条件对象
            Singer singer = new Singer();
            singer.setSno( sno==null||"".equals(sno)?null:Integer.parseInt(sno));
            singer.setFen( fen==null||"".equals(fen)?null:Integer.parseInt(fen));
            singer.setSname(sname);
            //数据回现
            req.setAttribute("cd",singer);
    
    
            //调用业务逻辑
            SingerService singerService = new SingerServiceImpl();
            try {
                Pager<Singer> pager = singerService.findByPageWithCondition(pageNum,pageSize,singer);
                //存在数据
                req.setAttribute("pager",pager);
                //跳转到JSP
                req.getRequestDispatcher("/singers.jsp").forward(req,resp);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    11.4 JSP 渲染分页数据
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
        <title>Title</title>
        <link href="/css/bootstrap.css" rel="stylesheet">
    </head>
    <body>
    
    <%--条件部分--%>
    <div style="width: 800px;margin: 10px auto">
        <form action="/singer/list2.do" method="post" class="form-inline" >
            ID
            <input name="sno" type="text" value="${cd.sno}"    placeholder="编号" class="form-control form-control-sm">
            Name
            <input name="sname" type="text" value="${cd.sname}"  placeholder="姓名" class="form-control form-control-sm" >
            Fen
            <select name="fen" class="form-control form-control-sm" >
                <option value="0"    ${cd.fen==0?'selected':''  }        >全部</option>
                <option value="10"  ${cd.fen==10?'selected':''  }    >10</option>
                <option value="50"  ${cd.fen==50?'selected':''  }    >50</option>
                <option value="100"  ${cd.fen==100?'selected':''  }    >100</option>
            </select>
            <input type="submit" value="查询" class="btn btn-primary btn-sm" >
    
        </form>
    
    </div>
    
    <%--数据显示部分--%>
    <table class="table table-sm " style="width: 800px;margin: 0 auto">
        <thead class="table-dark">
        <tr>
            <td>编号</td>
            <td>名字</td>
            <td>信息</td>
            <td>粉丝</td>
            <td>头像</td>
            <td>排名</td>
            <td>操作</td>
        </tr>
        </thead>
        <c:forEach items="${pager.list}" var="s">
            <tr>
                <td>${s.sno}</td>
                <td>${s.sname}</td>
                <td>${s.info}</td>
                <td>${s.fen}</td>
                <td><img src="${s.photo}" style="width: 30px;height: 30px"></td>
                <td>${s.ranks}</td>
                <td>
                    <a href="#" class="btn btn-sm  btn-link">删除</a>
                    <a href="#" class="btn btn-sm  btn-link">修改</a>
                </td>
            </tr>
        </c:forEach>
    </table>
    
    <%--分页按钮部分--%>
    <div  style="width: 800px;margin: 0 auto; text-align: center">
        <nav aria-label="Page navigation example">
            <ul class="pagination pagination-sm">
                <%--首页--%>
                <li class="page-item"><a class="page-link"  href="/singer/list.do?page=1">首页</a></li>
                <%--上一页--%>
                <c:if test="${pager.hasPrev}">
                    <li class="page-item"><a class="page-link"  href="/singer/list.do?page=${pager.page-1}">上一页</a></li>
                </c:if>
                <%--页码--%>
                <c:forEach items="${pager.buttons}" var="num">
                    <li class="page-item  ${pager.page==num?'active':'' }" ><a class="page-link" href="/singer/list.do?page=${num}">${num}</a></li>
                </c:forEach>
                <%--下一页--%>
                <c:if test="${pager.hasNext}">
                    <a></a>
                    <li class="page-item"><a class="page-link"   href="/singer/list.do?page=${pager.page+1}" >下一页</a></li>
                </c:if>
                <%--尾页--%>
                <li class="page-item"><a class="page-link"  href="/singer/list.do?page=${pager.pages}">尾页</a></li>
            </ul>
        </nav>
        当前<span>${pager.page}</span>/<span>${pager.pages}</span>,<span>${pager.total}条记录</span>
    </div>
    
    </body>
    </html>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    十二、文件上传下载

    12.1 文件上传

    文件上传下载是网站中十分常见的功能,比如上传头像,下载文件模板,这些功能都是网站的基础服务功能, JavaWeb中文件上传,通常是基于Servlet来接收请求并保存文件 ,上传页面可以是html 或者 jsp 都可以。基本流程如下:image.png

    扩展 对应文件保存路径,不一定保存在一个文件夹下,可以采用一些算法来拆分,比如,以日期为区分,不同日期的在一个文件夹。或者通过hashcode散列方式, 把文件保存到不同文件夹中。

    12.1.1 上传页面

    文件上传三要素

    1. post 方式提交 ( get 走url 只能提交简单是的字符信息, 且有限制 )
    2. enctype : multipart/form-data (表单数据编码格式,提交更为复杂的数据不限于字符,可以是文件 )
    3. input 的type 使用file (表明提交的我文件数据)
    <form action="/upload.do"  method="post" enctype="multipart/form-data">
              <input name="username" type="text">
              <input name="myfile1" type="file">
              <input name="myfile2" type="file">
              <input type="submit" value="上传">
    form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    12.1.2 commons-upload 方式[了解]

    接收控制器使用Serlvet ,但是在Servlet 3.0以前 ,对应文件上传支持不友好,通常使用 commons-fileupload-1.4.jar commons-io-2.5.jar 实现文件上传。
    引入相关jar包后,控制器编写如下;

    @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //第三方文件上上传库jar (Apache-commons-fileUpload)
            //接受文件 Servlet3.1 自带上功能
            //验证当前的请求是否为一个包含文件上传的请求
            if( !ServletFileUpload.isMultipartContent(req)  ){
                resp.getWriter().println("enctype使用multipart/form-data");
                return;
            }
            //确定保存位置
            String savePath = this.getServletContext().getRealPath("/upload");
            System.out.println(savePath);
            File saveDir = new File(savePath);
            //保存路径对应的文件夹不存在,则创建
            if(!saveDir.exists()){
                saveDir.mkdirs();
            }
            //文件上传工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //文件上传处理器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //解析上传请求
            try {
                List<FileItem> list = upload.parseRequest(req);
                for (FileItem item:list){
                    //区分类型
                    if( item.isFormField() ){ //普通的表单字段
                        // 接受普通字段
                        System.out.println(item.getString());
                    }else{
                        //非表单字段即是文件(保存文件)
                        item.write( new File( savePath+"/"+item.getName() ) );
                    }
                }
            } catch (FileUploadException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    12.1.3 @MutilpartConfig 方式[ 推荐 ]

    从Servlet3.0 开始 Servlet 对文件上传 实现了直接支持,不需要引入其他jar。 通过一个注解就可以方便实现上传。通过getPart( String name ) 直接可以获得表单中的文件对象。 如果是多文件上传后端使用 getParts( String name )。

    maxFileSize 单个文件最大 大小
    maxRequestSize 整个表单总大小

    @MultipartConfig
    @WebServlet("/upload.do")
    public class UploadController extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //接收文件
            Part photo = req.getPart("photo");
            //保存这个文件, 保存到发布目录中
            String path = req.getServletContext().getRealPath("/images/upload/");
            //确保上传目录存在
            File uploadDir = new File(path);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }
            //获得文件的名字
            String fileName = photo.getSubmittedFileName(); // jt.jpeg
            //文件后缀
            String suffix = fileName.split("\\.")[1];
            //重命名
            fileName = UUID.randomUUID().toString() + "." + suffix;
            //保存
            photo.write(path + fileName);
            //或取其他数据
            String sno = req.getParameter("sno");
            //调用业务方法
            SingerService singerService = new SingerServiceImpl();
            try {
                singerService.updatePhoto(   Integer.parseInt(sno)    ,  "/images/upload/"+fileName  );
            } catch (Exception e) {
                e.printStackTrace();
            }
            //响应
            resp.getWriter().println("upload success" + sno);
        }
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.getWriter().println("SB please use post!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    总结 , 目前这种文件上传方式为同步上传,就是把表单中的数据和图片一起上传提交。后面学习ajax后可以实现异步上传,异步上传就是可以单独先上传图片,然后立即获得上传路径, 最后表单只需要把这个路径和其他字段提交即可。

    12.2 文件下载

    12.2.1 超链接下载

    这种方式,使用一些固定资源文件,方便快捷,不需java编码,把文件通过 超链接 挂出来即可,用户点击超链接即可下载。

    问题: 1. 如果下载文件 能够被浏览器解析, 浏览器会自动打开,而不是下载。 2. 中文文件名可能乱码,无法下载。

    下载页面

    <a href="/download/bootstrap-4.6.0-dist.zip" > 下载zip笔记 </a>
    
    • 1

    12.2.2 控制器下载

    这种方式,需要编写控制器,任何下载需要通过控制器下载,把下载文件名字发给控制器,控制器取读取文件,然后以字节流方式响应可客户端,可以在响应头中添加 头信息,防止浏览器直接打开。

    @WebServlet("/download.do")
    public class DownloadController extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获得下载文件的名字
            String fname = req.getParameter("fname");
            //IO流读文件
            String realPath = req.getServletContext().getRealPath("/download/" + fname);
            //下载中文名乱码
            fname =  URLEncoder.encode(fname,"UTF-8");
            //设置响应头
            resp.setHeader("Content-Disposition", "attachment; filename="+fname);
            //字节流
            BufferedInputStream bis = new BufferedInputStream( new FileInputStream(realPath));
            byte[] bs = new byte[1024];
            int len;
            while( (len=bis.read(bs))!=-1){
                resp.getOutputStream().write(bs,0,len);
            }
            resp.getOutputStream().flush();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    resp.setHeader(“Content-Disposition”, “attachment; filename=”+fname); 这就代码比较关键,可以告诉浏览器,下载,而不打开。

    下载页面

    <a href="/download.do?fname=bootstrap-4.6.0-dist.zip" > 下载zip笔记 a>
    
    • 1

    十三、图片验证码

    图片验证码,是一种为了防止,用户恶意制造大量请求,消耗服务器运算资源,导致服务器卡顿或宕机,当然也可防止,用户暴力破解密码。但是随着图形图像识别技术也来越高,图片验证码基本也没什么用了,Google 已经放弃图片数字验证码。有还是比没有好。

    13.1 实现原理

    image.png

    13.2 第三方实现

    开源界有很多这方面的工具,案例中我们使用 easy-captcha-1.6.2.jar 演示。该工具提供了丰富的验证码类型
    // SpecCaptcha 普通验证码
    // GifCaptcha 动态gif验证码
    // ChineseCaptcha 中文验证码
    // ArithmeticCaptcha 数学公式验证码

    1. 引入jar

    获得 easy-captcha-1.6.2.jar 包添加到 WEB-INF/lib/easy-captcha-1.6.2.jar

    2. 编写控制器

    @WebServlet("/captcha.do")
    public class CaptchaController extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //创建一个图片对象 参数1 图片宽度  参数2 图片高度  参数3 字符个数  
            SpecCaptcha specCaptcha = new SpecCaptcha( 120,40 );
    
            //获得图片中的文字信息, 保存到Session
            req.getSession().setAttribute("code",specCaptcha.text());
    
            //图片字节流输出客户端
            specCaptcha.out( resp.getOutputStream() );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4. 网页中引用验证码

    网页中 通过 IMG 标签 把 控制器的访问地址给到 IMG 的src 属性即可

    <form method="post" action="/login.do" >
        <input type="text" name="username" placeholder="输入账号" > <br>
        <input type="password" name="password" placeholder="输入密码" > <br/>
        <input type="text" style="width: 100px;" name="userCode" placeholder="输入验证码" > 
        <img src="/captcha.do"><br>
        <input type="submit" value="登陆">
    form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    需要注意的是,确保获得图片验证码的控制器不要被拦截。

    5 校验验证码正确性

    在登陆的控制器中比较 用户输入的验证码 与 Session 中的验证码的 正确性。

    @WebServlet("/login.do")
    public class LoginController extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取用户输入的验证码
            String userCode = req.getParameter("userCode");
            //获得Session中正确验证码
            String code = req.getSession().getAttribute("code").toString();
            if( code.equalsIgnoreCase( userCode ) ){
                System.out.println("进一步做登陆");
            }else{
                System.out.println("验证码错误");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    完结 撒花~~~~~~

  • 相关阅读:
    如何使用Flask request对象处理请求
    不知道吧?未加工的食物可以帮助你减肥
    下载调试器 JTAG和SWD
    【Azure 事件中心】Azure Event Hub 新功能尝试 -- 异地灾难恢复 (Geo-Disaster Recovery)
    python字典按照 值进行排序 sorted
    《Python零基础入门》——关于PyCharm使用技巧及python基本概念
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    Android 13.0 屏蔽Launcher3桌面app图标的长按功能
    MySQL - 深入解析MySQL索引数据结构
    .NET周刊【8月第4期 2023-08-27】
  • 原文地址:https://blog.csdn.net/yc_Cabbage/article/details/126295427