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>

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/>");
       }
 %>

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

3.3 定义脚本

<%!
    String bookName="葵花宝典";
    public  String getBook(){
        return bookName;
    }
%>

用来定义变量或者方法

3.4 表达式脚本★

<div style="color: red">
    <%=bookName%>
</div>
<div style="color: red">
    <%=getBook()%>
</div>

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

四、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("<html>\r\n");
      out.write("<head>\r\n");
      out.write("    <title>Title</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("   <p>测试原理和生命周期2</p>\r\n");
      out.write("</body>\r\n");
      out.write("</html>\r\n");
  }
}

生命周期和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. 使用
<c:out value=“helloworld”/>

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

六、JSP动作[了解]

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

6.1 useBean

<jsp:useBean id=“obj” class=“com.qfedu.entity.User”>
<jsp:setProperty name=“obj” property=“name” value=“李硕”/>
<jsp:setProperty name=“obj” property=“age” value=“90”/>
</jsp:useBean>
<jsp:getProperty name=“obj” property=“name”/>
<jsp:getProperty name=“obj” property=“age”/>

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

6.2 setProperty

<jsp:setProperty name=“obj” property=“name” value=“李硕”/>
<jsp:setProperty name=“obj” property=“age” value=“90”/>

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

6.3 getProperty

<jsp:getProperty name=“obj” property=“name”/>
<jsp:getProperty name=“obj” property=“age”/>

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

6.4 forward

<jsp:forward page=“forward.jsp”/>

等价于servlet中的转发

6.5 param

<jsp:forward page=“forward.jsp”>
<jsp:param name=“age” value=“30”/>
</jsp:forward>

转发时候 携带参数

6.6 include

<jsp:include page=“simple.jsp”/>

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);
%>

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

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

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

7.3 exception
  1. 编写错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
    <title>Title</title>
</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>

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>

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");
}

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

req:
<%=request.getAttribute("req") %>
session:
<%=session.getAttribute("ses") %>
app:
<%=application.getAttribute("app") %>

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 ) // 从指定域中获得数据

特定域中获取数据可设置数据,通过 几个常量表示域类型:
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") %>

只需要给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

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}

这些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>

推荐直接使用 key 搜索。

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

它的作用是 获得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. 使用标签

9.2.forEach标签【列表渲染】

<%--
    items="" 指定遍历的集合
    var=""  遍历出的每个元素
--%>
<c:forEach items="${list}" var="u"  >
    <tr>
        <td>${u.name}</td>   <!-- 找u对象的getName()-->
        <td>${u.age}</td>
    </tr>
</c:forEach>

遍历集合功能 , 需要给对象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>

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>

9.5 日期格式

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

十、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;
    }
}

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;
    }

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();
        }
    }
}

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>


十二、文件上传下载

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>

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();
        }
}

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!");
    }
}

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

12.2 文件下载

12.2.1 超链接下载

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

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

下载页面

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

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();
    }
}

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

下载页面

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

十三、图片验证码

图片验证码,是一种为了防止,用户恶意制造大量请求,消耗服务器运算资源,导致服务器卡顿或宕机,当然也可防止,用户暴力破解密码。但是随着图形图像识别技术也来越高,图片验证码基本也没什么用了,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() );
    }
}

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>

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

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("验证码错误");
        }
    }
}

完结 撒花~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值