jsp教程

1. JSP 简介

  • JSP是简化 Servlet编码的一种技术,它是HTML与java混在一个文件中编写,动态的内容采用java,静态的内容采用HTML。

    jsp=html中嵌入Java代码

  • Java Server Page : Java服务器页面,感觉是在HTML中写Java代码,但本质上是Servlet

  • 原理:

    • 第一次请求JSP时,web容器会将它翻译成一个Servlet源码,接着把Servlet编译成.class,最后跟普通Servlet一样进行处理

      • web容器:HTTP服务器+Servlet服务器

      • tomcat就是一个Web容器

1.1. JSP页面的9大内置对象

​
 public void _jspService(final javax.servlet.http.HttpServletRequest request,       final javax.servlet.http.HttpServletResponse response)
       throws java.io.IOException, javax.servlet.ServletException {
    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;

内置对象:不需要new 可以直接使用的对象

域相关

  • pageContext 同一个页面

    • 作用:取得任何范围的参数

    • 获取jsp页面的out、request、response、session、application等对象

  • request 同一个请求

    • httpServletRequest类型对象

    • 用于接收通过HTTP协议传到服务器的数据

  • session 同一个会话

    • 用户请求相关对象,同一个用户用请求时共享一个Session对象

    • map类存储用户数据(键值对) 与cookie对应

  • application 同一个应用

    • 整个应用中有效,直到关闭服务器

//其它

  • config 配置

    • 获取服务器的配置信息

    • 通过pageContext对象中getServletConfig()方法获取

    • web.xml

    • 文件中配置信息和初始化参数

    <servlet>
        <servlet-name>jspservlet</servlet-name>
        <jsp-file>/config.jsp</jsp-file>
        <init-param>
            <param-name>uname</param-name>
            <param-value>admin</param-value>
        </init-param>
    </servlet>
    ​
    <servlet-mapping>
        <servlet-name>jspservlet</servlet-name>
        <url-pattern>/jspservlet</url-pattern>
    </servlet-mapping>
  • out 输出

    • 浏览器内输出信息

    • 管理服务器上的输出缓冲区

  • page 指jsp页面,相当this

    • 代表JSP本身,只有在jsp页面内才是合法的

  • response 响应

    • 封存传回客户端的数据

  • exception 异常

    • 页面中包含isErrorPage="true"才可以被使用

    <%@ page language="java" isErrorPage="true" import="java.util.*" pageEncoding="UTF-8"%>
    <%=exception.getMessage()%>
    ​

1.2. JSP组成:7大组成部门

  • 静态资源

    • html

    • js

    • css

    • img

  • 注释 : <%-- -- %>

    • <!-- --> HTML注释 ,会发送给客户端

    • <%-- -- %> JSP注释,不会发送给客户端【推荐 】

  • 指令 : <%@ %>

    • page指令

      • 相关属性

        • import 导包 import="java.util.*"

        • pageEncoding 编码 pageEncoding="UTF-8"

        • language 语言 language="java"

        • errorpage 需要跳转的错误页面

        • isErrorPage 是否允许捕捉异常跳转错误页面 isErrorPage="true"

    • include指令

      • 文件包含 jsp页面中引入另一个jsp文件

        <%@ include file=“path”%>
    • taglib指令

      • 导入自定义标签库

        <%@ taglib uri="uri" prefix = "prefixOfTag" >
            
            prefix属性通知容器什么位置的标记是自定义操比如prefix = "c"
            <c:if text=""></c:if>

  • 小脚本 : <% %>

    • 编译程序翻译后会放在Servlet的service方法中

    • 内置java代码 不同位置可拼接

    • 声明的是局部变量

    • 不可以声明方法

  • 表达式 : <%= 表达式 %>

    • 直接在页面输出表达式值

  • 声明 : <%! %>

    • Java语句

    • 声明的是全局变量

    • 可以声明方法

  • 动作 : <jsp:

2. 中文乱码问题,tomcat8.5

  • 在jsp页面有乱码,在page指令中加上如下属性

    pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"
  • 请求乱码

    • get

      request.setCharacterEncoding("UTF-8");
      //tomcat8之前
      String name = request.getParamater("uname");
      name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
    • post

      request.setCharacterEncoding("UTF-8");
  • 响应乱码

    response.setCharacter("UTF-8");
    response.setContentType("text/html");
    //可以简写成一句话
    response.setContentType("text/html;charset=UTF-8")
  • forward和include乱码

    request.setCharacterEncoding("UTF-8")

3. MVC模式

  • MVC

    • MVC是Model-View-Controller的简称,即模型-视图-控制器

    • 它是一个设计模式,它把应用程序分三个核心模块,各模块处理自己的任务

  • Model : java类

    • 包含业务数据和业务逻辑, 可以理解PO+Service

  • View : .jsp或.html

    • 视图与用户交互的界面,显示数据,接受用户数据,不要进行处理,可以理解成swing界面

  • Controller : servlet

    • 接受用户录入的数据,调用对应模型处理业务逻辑

    • 把业务逻辑的结果响应给view

4. 会话管理

4.1. 引出

  • HTTP:超文本传输协议,它是一个无状态的协议,web服务器本身不能识别出同一个客户端进行的访问,认为所有访问都是独立

  • 作用WEB服务器,必须能够彩一种机制来标注用户,记住用户状态

4.2. 会话和会话状态

  • 会话:客户端与服务端一系统请求和响应过程

  • 会话状态 :客户端与服务器会话过程中产生的状态,借助于这个状态,能系列会话关联起来

4.3. 会话ID

  • 服务端要从大量请求中筛选出哪些请求属性一个系列,即识出同一个浏览器的请求。要求同一个客户端访问时附属同个标识符,当然不同客户标识符不一样,这个标签符称为会话ID(SessionId)

4.4. 两种机制来实现会话管理

  • Cookie

  • Session

4.5. Cookie 机制

  • 原理

    • Cookie是客户端访问服务器时,服务器在响应头中生成一个Cookie带给了客户端,然后保存在客户端(不一定保存)

    • 以后客户端每次请求服务器,都会在请求头中带上这个Cookie信息,服务根据些Cookie记录一系列请求

    • Cookie是一种标识符,由名称(name)和值(value)组成

    • 当然服务器可以向客户端发送多个Cookie,保存在客户端

    • 不同浏览器Cookie个数和容量都有着不出限制

  • 会话Cookie和持久Cookie

    如果服务器创建了一个Cookie发送到客户端,默认情况下它是一个会话级Cookie,存储在客户端内存中,用户退出浏览器之后被删除。若希望存储在硬盘上,需要设置maxAge(),并给出一个以秒为单位 的时间

    • 设置了有效时间且不为零或没有设置有效时间都为持久Cookie,因为持久化到了文件中

    • 设置了有效时间为零,则为会话Cookie,保存在内存中

  • Cookie要么不保存,要么保存客户端

  • 使用API

    • Cookie类

      • Cookie(String name,String value)

      • getName()

      • setValue()和getValue()

      • setMaxAge()和getMaxAge()

        • 正数表示Cookie的存储时间,单位为秒 (持久化Cookie)

        • 负数表示一年过期(默认): (持久化Cookie)

        • 0 不存储,会话Cookie

      • setPath()和getPath()

        • setPath("/") 设置Cookie的访问路径 ,如果没有设置path(),默认同级别和下级别可以共享Cookie

    • Response

      • addCookie(Cookie)

    • Request

    • getCookies()

  • 应用场景

    • 自动登录

      • 第一次登录时,选择保存时间。登录成功后把用户名保存到Cookie中

      • 后面每次登录判断有无Cookie,有直接登录,无则重新登录

    • 显示最近浏览5书本

      • 获取所有Cookie

      • 筛选wanho开头的Cookie

      • 排序处理

        • 没有5本,直接加入Cookie

        • 超5本

          • 在5本内,把老的删除掉,加入新的

          • 不在5本,把最早的删除掉,加入新的

      • 代码

      • books.jsp

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    ​
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        
        <title>My JSP 'books.jsp' starting page</title>
    ​
      </head>
      
      <body>
        <a href="book.jsp?name=java">java</a> <br>
        <a href="book.jsp?name=java_web">java_web</a> <br>
        <a href="book.jsp?name=java_EE">java_EE</a> <br>
        <a href="book.jsp?name=javaScript">javaScript</a> <br>
        <a href="book.jsp?name=bootstrap">bootstrap</a> <br>
        <a href="book.jsp?name=jquery">jquery</a> <br>
        <a href="book.jsp?name=html">html</a> <br>
        <a href="book.jsp?name=xml">xml</a> <br>
        <a href="book.jsp?name=css">css</a> <br>
        
        <hr>
        <%
          //遍历所有cookie,获取所wanho_的cookie
            Cookie[] cs = request.getCookies();
            if(cs!=null){
                for(Cookie c:cs){
                    if(c.getName().startsWith("wanho_")){
                        out.println(c.getValue()+"<br>");
                    }
                }
            }
        %>
      </body>
    </html>
    ​
  • book.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String name = request.getParameter("name");
​
    Map<String,Cookie> map = new HashMap<>();
    boolean isFirst=true;
    String firstCookieName="";
​
    //遍历所有cookie,获取所wanho_的cookie
    Cookie[] cs = request.getCookies();
    if(cs!=null){
        for(Cookie c:cs){
            if(c.getName().startsWith("wanho_")){
                map.put(c.getName(),c);
                
                if(isFirst){
                    firstCookieName=c.getName();
                    isFirst=false;
                }
            }
        }
    }
    
    //没有5本,
            //且不存在,直接加入
            //存在,删除早,再加入
    //超过5本
            //且不存在,删除最早,加入新
            //存在,删除早,再加入
    if(map.size()<5){
        if(!map.keySet().contains("wanho_"+name)){
            response.addCookie(new Cookie("wanho_"+name,name));
        }else{
            //删除早的cookie
            Cookie cookie =  map.get("wanho_"+name);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            
            //再加入
            response.addCookie(new Cookie("wanho_"+name,name));
        }
    }else{
        if(!map.keySet().contains("wanho_"+name)){
            //删除最早的
            Cookie cookie = map.get(firstCookieName);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            map.remove(firstCookieName);
            
            //再加入
            response.addCookie(new Cookie("wanho_"+name,name));
        }else{
            //删除早的cookie
            Cookie cookie =  map.get("wanho_"+name);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            
            //再加入
            response.addCookie(new Cookie("wanho_"+name,name));
        }
    }
%>
<a href="books.jsp">books.jsp</a>

4.6. Session 机制

  • 原理

    • Session : 采用的是在服务器保存状态的解决方案

    • 当客户端请求服务器时,服务器首先先检查客户端请求里是否包一个session标识(JSESSIONID),如果已经包含了JSESSIONID,说明之前已经访问过

    • 如果不包含JSESSIONID,说明是第一次访问,服务会创建一个SESSION对象,把值保存在服务器中,同时再为客户端响应一个JSESSIONID,JSESSION保存在客户端

    • Session是保存在客户端和服务器的

  • JSESSIONID是保存在客户端,所以它又称Session Cookie,当然也分会话Session Cookie,也有持久化Session Cookie

  • session的生命周期

    • 创建时机

      • 对于JSP而言

        • 若当前JSP是客户访问的第一个页面,且JSP页面page指里的session属性值为false,访问JSP的时候不创建,用到Session时才创建

        • 若当前JSP是客户访问的第一个页面,且JSP页面page指里的session属性值为true(默认),访问时就创建

        • session="true|false"就是决定创建时机

      • 对于Servlet而言,只有调用下面的方法才创建

        • request.getSession(boolean create);

          • false:若当前没有session对象存在,返回null,若存在则返回session对象

          • true:若当前没有session对象存在,创建新的并返回,若存在则返回session对象

    • 消亡

      • session过期,默认30分钟

      • 服务器关闭

      • session.invalidate()使之失效

  • 应用场景

    • 记住登录用户

      login.jsp

      <% 
      String uname = (String) session.getAttribute("uname");
      if(uname!=null){
          out.println("<script>alert('已经登录');window.location.href='session/login_success.jsp';</script>");
      }
      %>
      ​
      <form action="LoginServlet2" method="post">
          用户名:<input type="text" name="uname">
          密码:<input type="text" name="upass">
          <input type="submit" value="登录">
      </form>

      LoginServlet.java

      String uname = request.getParameter("uname");
      String upass = request.getParameter("upass");
      if(uname.equalsIgnoreCase("admin") && upass.contentEquals("admin")){
          request.getSession().setAttribute("uname", uname);
          response.sendRedirect("session/login_success.jsp");
      }

login_success.jsp
​
```jsp
<% 
String uname = (String) session.getAttribute("uname");
if(uname==null){
    out.println("<script>alert('请先登录');window.location.href='session/login.jsp';</script>");
}
%>
login_success<br/>
​
<a href="LoginOutServlet2">注销</a>
```
​
LoginOutServlet.java
​
```java
request.getSession().invalidate();
response.sendRedirect("session/login.jsp");
```
  • 购物车

  • 避免表单重复提交

    • 场景还原

      • form请求时,当网络环境不好,出现延迟,用户不停点击提交按钮,就会有多次提交

      • 实现增加时,一般增加后用重定向跳转,如果网有延迟,还在增加页面上,刷新网页会重新增加

    • 解决原理

      • form放置一个隐藏域

      • 提交后给隐藏域赋值(随机值 ),当前页面的session的值设置成与隐藏域里的值一样

      • 一样则做核心业务,核心业务成功后把session值删除掉,

      • 重点提交后session没有了,则不相等,即不做多做执行核心业务

      login.jsp

      <%
          String value = new Date().getTime()+"";
          session.setAttribute("sessionToken", value);
        %>
        
        <body>
           <form action="LoginServlet3" method="post">
              <input type="hidden" value="<%=value%>" name="token"/>
              用户名:<input type="text" name="uname">
              密码:<input type="text" name="upass">
              <input type="submit" value="登录">
          </form>
        </body>
      • LoginServlet3.java

      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              
              HttpSession session = request.getSession();
              String sessionToken = (String) session.getAttribute("sessionToken");
              String token = request.getParameter("token");
              if(token.equalsIgnoreCase(sessionToken)){
                  System.out.println("import handler...");
                  
                  session.removeAttribute("sessionToken");
              }
              System.out.println("servlet handler...");
          }
  • 验证码

ValidatorCodeUtils.java

package net.wanho.utils;
​
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Random;
​
public class ValidatorCodeUtils {
    
    //name
    private static String[] names = {"仿宋","幼圆","方正姚体","等线"};
    //style
    private static int[] styles = {Font.ITALIC,Font.BOLD,Font.PLAIN};
    //size
    private static int[] sizes = {26,28,30,32,34,36};
    //内容库
    private static String chs="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKMLNOPQRSTUVWXYZ";
    
    //验证码结果
    private static StringBuffer sb = new StringBuffer();
    
    //随机类
    private static Random random = new Random();
    
    public static void init(Graphics g,int width,int height) {
        
        //画 矩形
        g.setColor(Color.lightGray);
        g.fill3DRect(5, 5,width, height, false);
        
        //画验证码
        randomCode(g);
        
        //画干扰线
        randomLine(g);
    }
​
    private static void randomLine(Graphics g) {
        for(int i=0;i<6;i++) {
            g.setColor(randomColor());
            g.drawLine(random.nextInt(125)+5, random.nextInt(45)+5, random.nextInt(125)+5, random.nextInt(45)+5);
        }
        
    }
​
    /**
     * //画验证码
     */
    private static void randomCode(Graphics g) {
        sb = new StringBuffer("");
        for(int i=0;i<4;i++) {
            g.setColor( randomColor());
            g.setFont(new Font(randomName(), randomStyle(), randomSize()));
            String c = randomChar();
            sb.append(c);
            g.drawString(c, 30+i*20, 40);
        }
    }
    
    /**
     * 返回结果
     * @return
     */
    public static String getValidatorCode() {
        return sb.toString();
    }
    
    private static String randomChar() {
        return chs.charAt(random.nextInt(chs.length()))+"";
    }
​
    private static int randomSize() {
        return sizes[random.nextInt(sizes.length)];
    }
​
    private static int randomStyle() {
        return styles[random.nextInt(styles.length)];
    }
​
    private static String randomName() {
        return names[random.nextInt(names.length)];
    }
​
    private static Color randomColor() {
        return new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
    }
​
    
}
​

login.jsp

<form action="LoginServlet4" method="post">
    用户名:<input type="text" name="uname">
    密码:<input type="text" name="upass">
    验证码:<input type="text" name="validateCode">  <img src="ValidateServlet">
    <input type="submit" value="登录">
</form>
​
<script>
    var img = document.getElementsByTagName("img")[0];
    img.οnclick=function(){
        img.src="ValidateServlet?now="+new Date();
    }
</script>

LoginServlet4.java

@WebServlet("/LoginServlet4")
public class LoginServlet4 extends HttpServlet {
​
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        response.setContentType("text/html;charset=UTF-8");
        
        String vc = request.getParameter("validateCode");
        String realVC = (String) request.getSession().getAttribute("realVC");
        if(!vc.equalsIgnoreCase(realVC)){
            response.getWriter().println("验证不正确");
        }else{
            response.getWriter().println("验证正确");
        }
        
    }
​
}

4.7. URL重写:为了解决JSESSIONID被禁用的问题,保证Session能继续使用

  • cookie是保存在客户端

  • session是保存在客户端(JSESSIONID)和服务器(对象)

  • 如果客户端禁用了cookie,那cookie就真的访问不到了

  • 但意味着sessionID也访问不到了,所以Session也不能正常使用了,为了获取服务器上Session的值,可以手动把JSESSIONID跟在请求URL后面,这个技术称为URL重写

  • 两个方法可以实现

    • encodeURL()

    • encodeRedirectURL()

a .jsp

 <%session.setAttribute("aaa", "1111"); %>
  <a href='<%=response.encodeUrl("session/session1.jsp")%>'>session1.jsp</a>

b.jsp

out.println(request.getSession().getAttribute("aaa"));

5. JavaBean

  • 符合javaBean的条件

    • 具有一个无参的构造方法

    • 属性访问修饰符是私有的,符号驼峰命名

    • 通过get或is和set公开,如果把get和set去了,首字母改小写,即为属性

    • 必须有包名,不能使用缺省包

  • 使用

    • <jsp:useBean>

      <jsp:useBean id="stu" class="net.wanho.vo.Student" scope="page"></jsp:useBean>
      <%
          //相当于
          Student stu2  = new Student();
          pageContext.setAttribute("stu2", stu2);
      %>
    • <jsp:setProperty>

    • <jsp:getProperty>

    <%@page import="net.wanho.vo.Student"%>
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <jsp:useBean id="stu" class="net.wanho.vo.Student" scope="page"/>
    <jsp:setProperty property="id" name="stu" value="1001"/>
    <jsp:setProperty property="name" name="stu" value="张三丰"/>
    <%
        //相当于
        Student stu2  = new Student();
        stu2.setId(1001);
        stu2.setName("张三丰");
        pageContext.setAttribute("stu2", stu2);
    %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>user javabean</title>
        
      </head>
      
      <body>
        <%=stu.getId() %> <br/>
        <%=stu2.getId() %>
      </body>
    </html>
    ​

6. EL

  • EL:Expression Language ,表达式语言。它本是JSTL1.0方便存取数据的表达式语言。当年只有在JSTL中才能应用,到了JSP2.0之后,EL已经可以在JSP 中使用。

  • 语法

    $(sessionScope.stu.name) | ${sessionScope.stu["name"]}
    //相当于
    Student stu = (Student) session.getAttribute("stu");
    stu.getName();
    ​
    //注
    //1.如果没有加上作用域,默认从pageScope requestScope sessionScope applicatScope 中逐次查询,如果加上作用域,则精确查询
    //2.[]更强大,可以放置字符串,放置变量,放置特殊符
  • 自动类型转换

    ${param.count+20} //count=10 结果为30
  • 运算符

    • 算术

      • + - * / %

      • div mod

    • 关系

      • < > >= <= lt gt ge le

      • == != eq ne

    • 逻辑

      • ! not

      • && and or

      • ||

    • 其它

      • true false

      • empty

  • 11 大内置对象

    • 作用域相关

      pageScope

      requestScope

      sessionScope

      applicationScope

    • url参数相关

      param

      paramValues

    • 其它

      cookie

      header

      headerValues

      initparam

      pageContext : 页面上下文件,获取其它Servlet中的对象

      ${pageContext.request}
      ${pageContext.session}
      ${pageContext.servletContext}
      ${pageContext.response}
      ${pageContext.servletConfig}
    • 示例

      <%@page import="net.wanho.vo.Student"%>
      <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
      ​
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
      <head>
      <title>el使用</title>
      </head>
      ​
      <body>
          <%
              Student stu2 = new Student();
              stu2.setId(1001);
              stu2.setName("张三丰");
              pageContext.setAttribute("stu", stu2);
      ​
              String var = "id";
              pageContext.setAttribute("var", var);
      ​
              pageContext.setAttribute("net.wanho.vo.Student", stu2);
          %>
          <h2>scope</h2>
          <%-- 1. EL的.或[]运算符    取值默认范围pageScope requestScope sessionScope applicationScope --%>
          ${pageScope.stu.id} ${stu["id"]} ${stu[var]}
          ${pageScope["net.wanho.vo.Student"]["id"]}
          <hr/>
      ​
          <h2>param</h2>
          <%-- 2.类型转换  http://localhost:8080/05_javabean_el/el.jsp?num1=1&num2=2&hobby=swim&hobby=music--%>
          ${pageScope.stu}- ${param.num1 +1}-${param.num2+1}-${paramValues.hobby[0]}-${paramValues.hobby[1]}
          <hr>
      ​
      ​
          <h2>other</h2>
          <%-- 3.其它内置对象 pageContext (cookie header headerValues initParam)了解 --%>
          request: ${pageContext.request}
          <br> session:${pageContext.session}
          <br> servletContext:${pageContext.servletContext}
          <br> response:${pageContext.response}
          <br> servletConfig:${pageContext.servletConfig}
          <br> cookie:${cookie.JSESSIONID.name}-${cookie.JSESSIONID.value}
          <br> header: ${header["User-Agent"]}
          <br> headerValues: ${headerValues["Accept"][0]}
          <br> ${initParam.encoding} <%-- 全局参数 --%>
          <hr>
      ​
          <h2>运算符</h2>
          <%-- 4. 运算符 --%>
          ${5+5}
          ${5-6}
          ${5*6}
          ${3/6}|
          ${3%6}|${3 mod 6}
          <br>
          ${5>6} | ${5 gt 6 }
          ${5>=6}| ${5 ge 6 }
          ${5<6}| ${5 lt 6 }
          ${5<=6}| ${5 le 6 }
          ${5==6}| ${5 eq 6 }
          ${5!=6}| ${5 ne 6 }
          <br>
          ${not false } |   ${!false } |  
          ${true and true } | ${true && true } | 
          ${true or false } | ${true || false } | 
          <br>
          ${not empty stu5}
          <br>
          ${param.num1==param.num2}
      ​
      </body>
      </html>

7. JSTL :

  • Java Server Pages Standard Tag Library JSP标准标签库,当前最新的版本1.2

  • 五类标签库

    • 核心标签

      • <c:foreach

    • I18N标签

    • SQL标签

    • XML标签

    • 函数标签

7.1. 核心标签库

要使用核心标签库,加入jar包且导taglib指令

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

该标签一共有14个,又分成4类

  • 通用标签

    • <c:out>

    • <c:set>

    • <c:remove>

    • <c:catch>

  • 条件标签 <c:if>

    • <c:choose>

      • <c:when>

      • <c:when>

      • <c:otherwise>

  • 循环标签

    • <c:foreach>

    • <c:forTokens>

  • URL相关标签

    • <c:import>

    • <c:url>

      • <c:param>

    • <c:redirect>

      • <c:param>

  • 通用标签示例

    <%@page import="net.wanho.po.Student"%>
    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>核心标签 </title>
      </head>
      <body>
      	<h2>通用标签</h2>
      	<%
      		Student stu  = new  Student();
      		request.setAttribute("stu", stu);
      	%>
      	<c:set property="name" target="${stu}" value="李四光"></c:set>
      	${stu.name }
      	
      	<%--http://localhost:8080/05_jstl/core.jsp?num1=100 --%>
      	<c:set var="num1" value="${param.num1}"  scope="page"></c:set>
      	
      	<c:set var="name" value="张三丰" scope="page"></c:set>
      	<c:remove var="name"/>
      	<c:out value="${num1}"></c:out>
      	<c:out value="${name}"></c:out>
      	${pageScope.name}
      	
      	<c:catch var="exception">
      	<%
      		int res= 10/0;
      	%>
      	</c:catch>
      ${exception}
      </body>
    </html>
    
  • 条件标签库示例

    <c:set var="count" value="110" ></c:set>
      <c:if test="${count>100 }">
      	大于100
      </c:if>
      
      <c:if test="${count<100 }">
      	小于100
      </c:if>
      
      <c:if test="${count==100 }">
      	等于100
      </c:if>
      
      <c:choose>
      	<c:when test="${count>100}">
      		大于100
      	</c:when>
      	<c:when test="${count<100}">
      		小于100
      	</c:when>
      	<c:otherwise >
      		等于100
      	</c:otherwise>
      </c:choose>
  • 循环的遍历

       <%
       		List<Student> stus = new ArrayList();
       		stus.add(new Student(new BigDecimal(1),"1",new BigDecimal(1),"1","1"));
       		stus.add(new Student(new BigDecimal(2),"2",new BigDecimal(2),"2","2"));
       		stus.add(new Student(new BigDecimal(3),"3",new BigDecimal(2),"3","3"));
       		stus.add(new Student(new BigDecimal(4),"4",new BigDecimal(2),"4","4"));
       		request.setAttribute("stus", stus);
       %>
      <h2>循环标签</h2>
      <c:forEach var="n" begin="1" end="10" step="2" varStatus="s">
      <%-- n:当前变量   变量所在下标(从1开始)  first:是否遍历的第一个元素   last:是否是遍历的最后一个元素--%>		  
      	${n}-${s.index}-${s.count}-${s.first}-${s.last} <br>
      </c:forEach>
      
      <c:forEach var="stu" items="${stus}">
      		${stu.id}-${stu.name}-${stu.age}-${stu.gender}-${stu.address}<br/>
      </c:forEach>
      
      
      <c:forTokens items="a,b,c,d" delims="," var="ch" >
      		${ch}
      </c:forTokens>
  • URL相关

    <hr>
    <h2>URL相关标签</h2>
    <c:import url="include.txt" charEncoding="UTF-8"></c:import> <br/>
    
    http://localhost:8080/system/user?id=1001&name=<%=URLEncoder.encode("张三丰","UTF-8") %><br/>
    <c:url var="addUserURL" value="http://localhost:8080/system/user" scope="page">
        <c:param name="id" value="1001"></c:param>
        <c:param name="name" value="张三丰"></c:param>
    </c:url>
    ${addUserURL}
    ${addUserURL2}
    <br>
    
    <%-- <c:redirect url="/index.jsp">
      		<c:param name="id" value="1001"></c:param>
      		<c:param name="name" value="张三丰"></c:param>
      	</c:redirect> --%>

7.2. 函数标签

要使用核心标签库,加入jar包且导taglib指令

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

和String类的功能类似

和String的方法类似,就是对String的一种封装。

No.函数标签名称描述
1${fn:contains()}查询某字符串是否存在,区分大小写
2${fn:containsIgnoreCase()}查询某字符串是否存在,忽略大小写
3${fn:startsWith()}判断是否以指定的字符串开头
4${fn:endsWith()}判断是否以指定的字符串结尾
5${fn:toUpperCase()}全部转为大写显示
6${fn:toLowerCase()}全部转为小写显示
7${fn:substring()}字符串截取
8${fn:split()}字符串拆分
9${fn:join()}字符串连接
10${fn:escapeXml()}将<、>、"、'等替换成转义字符
11${fn:trim()}去掉左右空格
12${fn:replace()}字符串替换操作
13${fn:indexOf()}查找指定的字符串位置
14${fn:substringBefore()}截取指定字符串之前的内容
15${fn:substringAfter()}截取指定字符串之后的内容
16${fn:length()}字符串的长度
  • 示例

    	<body>       
    		<%
              String a[] = {"aa","bb","cc","dd"};  
              request.setAttribute("array",a);  
              request.setAttribute("store","guomei8899");  
              request.setAttribute("user","u1,u2,u3,u4,u5");  
              request.setAttribute("test","aBcDeF   ");  
    		%> 
    		<c:if test="${fn:contains('guomeiddd','guoMei')}">ok</c:if><br>
    		<c:if test="${fn:containsIgnoreCase(store,'guoMei')}">ok ok</c:if><br>
    		<c:if test="${fn:endsWith(store,'99')}">end</c:if><br>
    		<c:out value="${fn:escapeXml('<>')}"/><br>
    		<c:out value="${fn:indexOf(store,'om')}"/><br>
    		<c:out value="${fn:join(array,'|')}"/><br>
    		<c:out value="${fn:length(array)}"/><br>
    		<c:out value="${fn:replace(store,'8','9')}"/><br>  
            <c:out value="${fn:split(user,',')}"/><br>  
            <c:out value="${fn:startsWith(store,'g')}"/><br>  
            <c:out value="${fn:substring(store,2,5)}"/><br>  
            <c:out value="${fn:substringAfter(store,'mei')}"/><br>  
            <c:out value="${fn:substringBefore(store,'mei')}"/><br>  
            <c:out value="${fn:toLowerCase(test)}"/><br>  
            <c:out value="${fn:toUpperCase(test)}"/><br>  
            <c:out value="${test}hoho"/><br>  
            <c:out value="${fn:trim(test)}hoho"/><br>  	
    	</body> 

8. 自定义标签和函数

8.1. 标签小结

  • 使用标签可以降低和简化JSP的开发,从HTML角度业说,可以使用HTML不用过多的关注复杂Java代码

  • 使用标签可以更合理分工UI和后端开发人员的工作

  • 将功能相同的代码放置到标签中,标签实现了代码重用性

    那么,虽然内置五种标签类型,我们是否也可以自定义标签呢,答案是肯定的

8.2. 标签的格式

  • 空标签(空属性+空体)

    <hr>
  • 带体空属性标签

    <sayhi>你好</sayhi>
  • 带属性空体标签

    <max num1="1" num2="2">
  • 带属性带体标签

     <lover amount="5">
       	i lover you
     </lover>

8.3.自定义标签接口

  • 编写自定义标签,在JSP2.0之前都需要实现Tag接口,这样的标签称为传统标签

  • 在JSP2.0及之后,可以实现SimpleTag接口,这样的标签称为简单标签

8.4. 实现自定义标签的步骤

  • 编写标签处理器,即实现了SimpleTag接口的java类,也可以继承于SImpleTagSupport

  • 编写标签库描述 (tld: Tag Library Description)文件

    • 后辍名*.tld

    • 多个标签合成一个标签库

    • 自定义的标签放置的地方: tld文件放置在web容器的WEB-INF目录及其子目录中。但不能放置在classes和lib下

    • 第三方标签库放置的地方: 在WEB-INF/lib目录下的jar包的META-INF目录中

    <tag>
    		<name>hello2</name>
    		<tag-class>net.wanho.po.HelloSimpleTag2</tag-class>
       		 <!-- 
    			empty:没有体  
    		 	scriptless:可包含el和jsp动作,但不能包jsp脚本 
    		 	tagdependent : 依赖标签
    		 -->
    		<body-content>scriptless</body-content>
    		
    		<attribute>
                <!-- 属性名:要与实现类中一致 -->
    			<name>time</name>
    			<!-- 属性必须-->
    			<required>true</required>
    			<!-- 是否支持EL表达式 -->
    			<rtexprvalue>true</rtexprvalue>
        	</attribute>
    </tag>
  • 在JSP引入taglib指令,指向自定义标签

8.4.1. 空标签

  • HelloSimpleTag

public class HelloSimpleTag implements SimpleTag {

	private JspContext jspContext;
	@Override
	public void doTag() throws JspException, IOException {
		jspContext.getOut().println("你好啊");
	}

	@Override
	public JspTag getParent() {
		System.out.println("getParent");
		return null;
	}

	@Override
	public void setJspBody(JspFragment arg0) {
		System.out.println("setJspBody");
	}

	@Override
	public void setJspContext(JspContext jspContext) {
		this.jspContext = jspContext;
	}

	@Override
	public void setParent(JspTag arg0) {
		System.out.println("setParent");
	}

}
  • WEB-INF/myTag.tld

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	
	<!-- 标签要使用的前辍,默认值 -->
	<short-name>wanho</short-name>
	<uri>http://www.wanho.net/mytag/core</uri>
	
	<tag>
		<name>hello</name>
		<tag-class>net.wanho.po.HelloSimpleTag</tag-class>
		<body-content>empty</body-content>
	</tag>
	
</taglib>
  • *.jsp

<%@ taglib uri="http://www.wanho.net/mytag/core" prefix="wanho" %>
<wanho:hello/>

8.4.2. 带属性

  • HelloSimpleTag2.java

public class HelloSimpleTag2 extends SimpleTagSupport {

	private int time;

	public int getTime() {
		return time;
	}

	public void setTime(int time) {
		this.time = time;
	}

	@Override
	public void doTag() throws JspException, IOException {
		JspWriter out = this.getJspContext().getOut();
		for (int i = 0; i < time; i++)
			out.println("hello<br>");
	}

}
  • WEB-INF/myTag.tld

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	
	<!-- 标签要使用的前辍,默认值 -->
	<short-name>wanho</short-name>
	<uri>http://www.wanho.net/mytag/core</uri>

	
	<tag>
		<name>hello2</name>
		<tag-class>net.wanho.po.HelloSimpleTag2</tag-class>
		<body-content>empty</body-content>
		
		<attribute>
			<!-- 属性名:要与实现类中一致 -->
			<name>time</name>
			<!-- 属性必须-->
			<required>true</required>
			<!-- 是否支持EL表达式 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
	
</taglib>
  • *.jsp

<%@ taglib uri="http://www.wanho.net/mytag/core" prefix="wanho" %>
<wanho:hello2 time="5"/>

8.4.3. 带属性带体

  • HelloSimpleTag4.java

public class HelloSimpleTag4 extends SimpleTagSupport {

	private int time;

	public int getTime() {
		return time;
	}

	public void setTime(int time) {
		this.time = time;
	}

	@Override
	public void doTag() throws JspException, IOException {
		System.out.println("doTag");
		JspWriter out = this.getJspContext().getOut();
		
		StringWriter sw = new StringWriter();
        
       	//直接输出体的内容
        //this.getJspBody().invoke(null);
        
		//把体的内容保存到内存流中
		this.getJspBody().invoke(sw);
		
		for(int i=0;i<time;i++){
			out.println(sw.toString()+"<br/>");
		}
	}
}
  • WEB-INF/myTag.tld

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	
	<!-- 标签要使用的前辍,默认值 -->
	<short-name>wanho</short-name>
	<uri>http://www.wanho.net/mytag/core</uri>
	
	
	
	
	<tag>
		<name>hello3</name>
		<tag-class>net.wanho.po.HelloSimpleTag4</tag-class>
		<!-- 
			empty:没有体  
		 	scriptless:可包含el和jsp动作,但不能包jsp脚本 
		 	tagdependent : 依赖标签
		 -->
		<body-content>scriptless</body-content>
		
		<attribute>
			<!-- 属性名:要与实现类中一致 -->
			<name>time</name>
			<!-- 属性必须-->
			<required>true</required>
			<!-- 是否支持EL表达式 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
	
	
	
</taglib>
  • *.jsp

<%@ taglib uri="http://www.wanho.net/mytag/core" prefix="wanho" %>
<wanho:hello3 time="3">xxoo</wanho:hello3>

8.4.4. 带父标签

模拟

<wanho:choose>
	<wanho:when test="${}">语句1</wanho:when>
    <wanho:when test="${}">语句2</wanho:when>
    <wanho:otherwise>其它语句</wanho:otherwise>
</wanho:choose>

开个三个标签

  • choose

  • when

  • otherwise

流程:

  • choose定义一个全局的boolean类型的flag=true,用于判断choose是否继续

  • 若when的test为true且父标签flag也为true,则输出标签体的内容,把choose的flag设置成false

  • 若有了成功的的结果,因为父标签flag为false,则其它内容都不会输出

  • 若都不成功,则执行otherwise且父标签的flag还是为true,所以能够输otherwise的内容

示例

  • ChooseTag.java

package net.wanho.po;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class ChooseTag extends SimpleTagSupport {
	private boolean flag=true;

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	
	@Override
	public void doTag() throws JspException, IOException {
		//直接输出体的内容
		this.getJspBody().invoke(null);
	}

}
  • WhetTag.java

    public class WhenTag extends SimpleTagSupport {
    	private boolean test;
    
    	public boolean isTest() {
    		return test;
    	}
    
    	public void setTest(boolean test) {
    		this.test = test;
    	}
    	
    	@Override
    	public void doTag() throws JspException, IOException {
    		ChooseTag pt = (ChooseTag) getParent();
    		if(test && pt.isFlag()){
    			this.getJspBody().invoke(null);
    			pt.setFlag(false);
    		}
    	}
    }
  • Otherwise.java

    public class OtherwiseTag extends SimpleTagSupport {
    	@Override
    	public void doTag() throws JspException, IOException {
    		ChooseTag pt = (ChooseTag) getParent();
    		if (pt.isFlag()) {
    			this.getJspBody().invoke(null);
    		}
    	}
    }
    
  • WEB-INF/myTag.tld

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
	<tlib-version>1.0</tlib-version>
	<jsp-version>1.2</jsp-version>
	
	<!-- 标签要使用的前辍,默认值 -->
	<short-name>wanho</short-name>
	<uri>http://www.wanho.net/mytag/core</uri>
	

	
	<tag>
		<name>choose</name>
		<tag-class>net.wanho.po.ChooseTag</tag-class>
		<body-content>scriptless</body-content>
	</tag>
	<tag>
		<name>when</name>
		<tag-class>net.wanho.po.WhenTag</tag-class>
		<body-content>scriptless</body-content>
		
		<attribute>
			<name>test</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
	<tag>
		<name>otherwise</name>
		<tag-class>net.wanho.po.OtherwiseTag</tag-class>
		<body-content>scriptless</body-content>
	</tag>
	
	
	
	
</taglib>
  • *.jsp

<%@ taglib uri="http://www.wanho.net/mytag/core" prefix="wanho" %>	
<wanho:choose>
    <wanho:when test="${1 == 1}">第一个when</wanho:when>
    <wanho:when test="true">第二个when</wanho:when>
    <wanho:otherwise>otherwise</wanho:otherwise>
</wanho:choose>

8.5. 自定义函数

  • 实现三步曲

    • 定一个实现类,普通类但要求方法是公开的且静态方法

    • 描述文件

    • 在指令导入

  • 示例

  • MyFun.java

public class MyFun {
	public static String concat(String s1,String s2){
		return s1+s2;
	}
}
  • WEB-INF/myfn.tld

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
      version="2.0">
        
      <tlib-version>1.0</tlib-version>
      
      <short-name>wanho</short-name>
      <uri>http://www.wanho.net/myfn/core</uri>
    
      <function>
        <name>concat</name>
        <function-class>net.wanho.po.MyFun</function-class>
        <function-signature>java.lang.String concat(java.lang.String, java.lang.String)</function-signature>
      </function>
    
    </taglib>
    
  • *.jsp

    <%@ taglib uri="http://www.wanho.net/myfn/core" prefix="wanho2"%>
    ${wanho2:concat(param.s1,param.s2)}

9. 路径要使用相对于容器而言的约对路径

  • myeclipse的解决方案

    <%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    <base href="<%=basePath%>">
  • 我的解决方案

    <base href="${pageConext.request.contextPath}/">
    或
    <img src="${pageConext.request.contextPath}/img/xx.jpg">

10. 指令 和动作

10.1. 指令

10.1.1. page指令

作用:

  • 页面语言

  • 页面编码

  • 内容类型

  • session创建时机

  • 导包

  • 是否是错误页面

  • 有错跳转到的页面

<%@ page language="java" session="true" import="java.util.*" pageEncoding="UTF-8" errorPage="error.jsp" contentType="text/html; charset=UTF-8" %>

error.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isErrorPage="true"%>
这样就可以使用jsp的exception的内置对象

除了在当前jsp页面配置错误页面外,还可以配置全局错误页面,在web.xml中进行配置

 <error-page>
  	<exception-type>java.lang.ArithmeticException</exception-type>
  	<location>/error.jsp</location>
  </error-page>
  
  <error-page>
  	<error-code>404</error-code>
  	<location>/404.jsp</location>
  </error-page>
  
  <error-page>
  	<error-code>500</error-code>
  	<location>/500.jsp</location>
  </error-page>

10.1.2. taglib指令

作用 : 引入标签

  • 短路径

  • 前辍

  • 使用时: <前辍:标签名 属性=值>标签体</前辍:标签名>

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

10.1.3. include 指令

作用:用于通知web引擎将其它jsp内容合并到当前JSP页面中,生成一个Servlet源文件,这种包含的方式称为静态引入

  • file要静态包含的文件名

<%@ include file="/include/b.jsp" %>

10.2 . 动作:

<jsp:include>request.getReqeuestDispatcher("/").include(requet,response);

<jsp:param>

<jsp:forward>request.getReqeuestDispatcher("/").forward(requet,response);

<jsp:plugin>加载Applet java小程序,现在几乎不用

<jsp:useBean>

<jsp:setProperty>

<jsp:getProperty>

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<jsp:useBean id="user" class="net.wanho.po.User" scope="request"/>
<jsp:setProperty property="name" name="user" value="aa"/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>My JSP 'action.jsp' starting page</title>
    

  </head>
  
  <body>
    <jsp:getProperty name="user" property="id"/>
    <jsp:getProperty name="user" property="name"/>
    <jsp:getProperty name="user" property="pwd"/>
    <hr>
    ${user.id}
    ${user.name}
    ${user.pwd}
  </body>
</html>

11. include指令和include动作的区别

相同点:

  • 都可以包含一个页面

不同点:

  • include指令包含的属性为file,而include动作包含的属性为page

  • include属性于静态包含,而include动作属性动态包含

  • include是在编译成servlet之前,把两个jsp合并成了一个jsp,所以最终生成了一个servlet,这种方式称为静态合并

    而include动作是编译成两个servlet,只在运行的内容把内容合并过来,这种方式称为动态合并

  • include指令只能包含页面,不能传参给页面

    而include动作是两个servlet,所以可以能过<jsp:param>赋参给包含页面,注如果是中文,要在包含的页面处理乱码

12. session复习

12.1. 生命周期

  • 创建时机

    • 在jsp中

      • 第一次请求jsp页面,查看page指令中是否包session属性

        • 如果session属性的值为true(默认),则在第一次请求的时候,创建session对象

        • 如果session属性的值为false,则在第一次使用 request.getSession().createSession(true)的时候创建session

    • 在servlet中

      • 只能在调用request.getSession(xxx)时才有可能创建

        • request.getSession(true),默认为true,有session直接返回session,没有session对象会创建新的session对象并返回

        • request.getSession(false),不会创建session对象,有session直接返回session,没有session对象直接返回null

  • 消亡时机

    • 服务终止

    • 会话过时

    • session.invalidate()

  • 判断是否是新的session

    • session.isNewSession()

    • session.getMaxInactiveInterval() 获取session最大的不活动的间隔时间,以秒为单位秒。

      • Servlet中API设置 > 程序/web.xml设置 > Tomcat/conf/web.xml设置

13. 过滤器

13.1. 简介

  • JavaWEB有三大组件 : Servlet、Filter和Listener。Filter就是其中之一。

  • 它理解可以跟Servlet很像

    • Filter的类是实现Filter的接口

    • Filter要么在web.xml中进行配置,要么使用注解

    • Filter的生命周期具体是由WEB容器进行操作的,跟客户端也有关联。

13.2. 第一个Filter,基于XML

  • FirstFilter.java实现类

public class FirstFilter implements Filter {
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("init...");
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("doFilter...");
	}
	public void destroy() {
		System.out.println("destroy....");
	}
}
  • 配置文件

WEB-INF/web.xml

<filter>
 	<filter-name>fFilter</filter-name>
 	<filter-class>net.wanho.filter.FirstFilter</filter-class>
 </filter>
 
 <filter-mapping>
 	<filter-name>fFilter</filter-name>
     <!-- 请求任何资源前都会过滤,即都会先调用此Filter -->
 	<url-pattern>/*</url-pattern>
 </filter-mapping>

13.3. 第二个Filter,基于annotation

  • FirstFilter.java

@WebFilter("/FirstFilter")
public class FirstFilter implements Filter {
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("init...");
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("doFilter...");
	}
	public void destroy() {
		System.out.println("destroy....");
	}
}

13.4. 如果请求匹配到多个过滤器的URL映射,怎么办?

  • 如果请求匹配到多个过滤器的URL映射,只会执行最早匹配那个过滤器,如果放行后能匹配所有的

  • 最早匹配那个过滤器是看<filter-mapping>的出现位置,在最先出现,即为最早匹配的,

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>08_FIlter</display-name>
 
 <filter>
 	<filter-name>tFilter</filter-name>
 	<filter-class>net.wanho.filter.ThirdFilter</filter-class>
 </filter>
 
 
  <filter>
 	<filter-name>sFilter</filter-name>
 	<filter-class>net.wanho.filter.SecondFilter</filter-class>
 </filter>
 
 <filter>
 	<filter-name>fFilter</filter-name>
 	<filter-class>net.wanho.filter.FirstFilter</filter-class>
 </filter>
 
 <filter-mapping>
 	<filter-name>sFilter</filter-name>
     <!--这个情况,只会匹配第二个过滤器,后面执行不到,因为些过滤器最早且能满足所有url映射,除非前面的过滤放行-->
 	<url-pattern>/*</url-pattern>
 </filter-mapping>
 
 
 <filter-mapping>
 	<filter-name>fFilter</filter-name>
 	<url-pattern>*.jsp</url-pattern>
 </filter-mapping>
 
  <filter-mapping>
 	<filter-name>tFilter</filter-name>
 	<url-pattern>/c</url-pattern>
 </filter-mapping>
 
</web-app>

13.5. Filter的生命周期

  • 创建,随着WEB容器的启动而启动,这一点与Servlet有区别

    void init(FilterConfig)

  • 过滤 : 每次请求时都会执行

    doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

  • 消亡:随着WEB容器的结束而结束

    destroy( )

13.6. API

  • FilterChain(ServletRequest request, ServletResponse response)

  • FilterConfig

    • String getInitParameter(name)

    • Enumeration getInitParamaterNames()

    • ServletContext getServletContext()

    • String getFilterName

13.7. 配置详解 <filter-mapping>下<dispatcher>标签

  • 值,可以同时加入多个dispatcher

    • request : 当用户直接访问页面时,会调用过滤器。如果是转发的,则不调用过滤器

    • include : 转发且为include的转发方式,会调用过滤器。其它情况不调用

    • forward : 转发且为forward的转发方式,会调用过滤器。其它情况不调用

    • error : 假如web.xml配置了 <error-page>,有错且跳转到errorpage后,会调用器

    • async : 异步处理时会调用过滤器

  • annotation配置

    @WebFilter(urlPatterns={"*.jsp"},dispatcherTypes = {
    		DispatcherType.REQUEST, 
    		DispatcherType.FORWARD, 
    		DispatcherType.INCLUDE, 
    		DispatcherType.ERROR})
    public class MyFilter implements Filter {

13.8. 过滤器的优先级

  • 在xml中,<filter-mapping>配置在前面的先执行

  • 在注解中,实现类的类名排序的,类名早的优先执行

  • 即有xml,又有注解,xml优先。所以可以把登录的过滤放置到xml中,其它配置可以放置在注解中。

13.9. 应用场景

  • 乱码处理

<!-- 全局参数:编码格式(乱码过滤器中使用) -->
	<context-param>
		<param-name>Encoding</param-name>
		<param-value>UTF-8</param-value>
	</context-param>

EncodingFilter.java

package net.wanho.filter;

import java.io.IOException;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter(urlPatterns={"/*"},dispatcherTypes = {
		DispatcherType.REQUEST, 
		DispatcherType.FORWARD, 
		DispatcherType.INCLUDE})
public class EncodingFilter implements Filter {
	
	private FilterConfig config;
	
    public EncodingFilter() {}

	public void destroy() {}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//从全局变量文件中获取编码格式 
		String encodingName= config.getServletContext().getInitParameter("Encoding");
		if(encodingName==null || encodingName.isEmpty()){
			encodingName="UTF-8";
		}
		//处理请求导致的乱码
		request.setCharacterEncoding(encodingName);
		//处理响应导致的乱码
		response.setContentType("text/html;charset="+encodingName);
		
		//放行
		chain.doFilter(request, response);
	}
	public void init(FilterConfig fConfig) throws ServletException {
		this.config=fConfig;
	}

}
  • 登录验证

    <!-- 全局参数:不需要登录过滤的页面(登录过滤器中使用) -->
    	<context-param>
    		<param-name>unCheckUrls</param-name>
    		<param-value>/login.jsp,/loginServlet,/register.jsp,/loginout.jsp</param-value>
    	</context-param>
    	<!-- 全局参数:session名(登录过滤器中使用) -->
    	<context-param>
    		<param-name>sessionKey</param-name>
    		<param-value>currentUser</param-value>
    	</context-param>
    	<!-- 全局参数:未登录跳转的页面(登录过滤器中使用) -->
    	<context-param>
    		<param-name>jumpUrl</param-name>
    		<param-value>login.jsp</param-value>
    	</context-param>

    LoginFilter.java

    package net.wanho.filter;
    
    import java.io.IOException;
    import java.util.Arrays;
    
    import javax.servlet.DispatcherType;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebFilter(urlPatterns={"/*"},dispatcherTypes = {
    		DispatcherType.REQUEST, 
    		DispatcherType.FORWARD, 
    		DispatcherType.INCLUDE})
    public class LoginFilter implements Filter {
    	
    	private FilterConfig config;
    	
        public LoginFilter() {}
    
    	public void destroy() {}
    
    	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
    		
    		ServletContext application  = config.getServletContext();
    		//获取不需要登录判断的页面 unCheckUrls : 登录,登出,注册
    		String unCheckUrls =application.getInitParameter("unCheckUrls");
    		//获取登录成功后的session名称    sessionKey
    		String sessionKey =application.getInitParameter("sessionKey");
    		//获取未登录要登录的页面:   jumpUrl
    		String jumpUrl =application.getInitParameter("jumpUrl");
    		
    		HttpServletRequest request = (HttpServletRequest) req;
    		HttpServletResponse response = (HttpServletResponse) resp;
    		
    		//1.获取请求页面
    		// /MyServlet  /index.jsp  /a/b/index.jsp
    		String path = request.getServletPath();
    		
    		//2.如果在不需要判断的页面中,则直接放行
    		if(Arrays.asList(unCheckUrls.split(",")).contains(path)){
    			//放行
    			chain.doFilter(request, response);
    			return;
    		}
    		
    		// 3. 其它页面,需要验证
    		//从session拿值,有值,如果登录过,放行
    		Object loginObj = request.getSession().getAttribute(sessionKey);
    		if(loginObj !=null){
    			//放行
    			chain.doFilter(request, response);
    			return;
    		}else{ //没有值,则跳转到对应的页面
    			response.sendRedirect(jumpUrl);
    		}
    	
    	}
    	public void init(FilterConfig fConfig) throws ServletException {
    		this.config=fConfig;
    	}
    }
  • 权限验证

  • Authority.java

package net.wanho.po;

public class Authority {	
	private String name;
	private String url;
	
	public Authority() {
		super();
	}

	public Authority(String name, String url) {
		super();
		this.name = name;
		this.url = url;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((url == null) ? 0 : url.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Authority other = (Authority) obj;
		if (url == null) {
			if (other.url != null)
				return false;
		} else if (!url.equals(other.url))
			return false;
		return true;
	}
}
  • User.java

package net.wanho.po;

import java.util.List;

public class User {
	private String uname;
	private String upass;
	private List<Authority> authorities ;
	
	public User() {
		super();
	}

	public User(String uname, String upass, List<Authority> authorities) {
		super();
		this.uname = uname;
		this.upass = upass;
		this.authorities = authorities;
	}

	public String getUname() {
		return uname;
	}

	public void setUname(String uname) {
		this.uname = uname;
	}

	public String getUpass() {
		return upass;
	}

	public void setUpass(String upass) {
		this.upass = upass;
	}

	public List<Authority> getAuthorities() {
		return authorities;
	}

	public void setAuthorities(List<Authority> authorities) {
		this.authorities = authorities;
	}
	
	
}

  • AuthorityService.java

package net.wanho.service;

import java.util.ArrayList;
import java.util.List;

import net.wanho.po.Authority;

public class AuthorityService {
	
	public List<Authority> findAll(){
		 List<Authority> authorities = new ArrayList<Authority>();
		 authorities.add(new Authority("userList", "/user/list.jsp"));
		 authorities.add(new Authority("userEdit", "/user/edit.jsp"));
		 authorities.add(new Authority("authList", "/auth/list.jsp"));
		 authorities.add(new Authority("authEdit", "/auth/edit.jsp"));
		 return authorities;
	}
}
  • UserService.java

package net.wanho.service;

import java.util.ArrayList;
import java.util.List;

import net.wanho.po.Authority;
import net.wanho.po.User;

public class UserService {

	public User login(User user) {

		// 登录业务
		// 成功后查询当前用户的权限
		List<Authority> authorities = new ArrayList<>();
		authorities.add(new Authority("userList", "/user/list.jsp"));
		authorities.add(new Authority("userEdit", "/user/edit.jsp"));
		authorities.add(new Authority("authEdit", "/auth/edit.jsp"));

		user.setAuthorities(authorities);
		
		return user;
	}
}
  • AutorityFilter.java

package net.wanho.filter;

import java.io.IOException;
import java.util.List;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.wanho.po.Authority;
import net.wanho.po.User;
import net.wanho.service.AuthorityService;

@WebFilter(urlPatterns={"/*"},dispatcherTypes = {
		DispatcherType.REQUEST, 
		DispatcherType.FORWARD, 
		DispatcherType.INCLUDE, 
		DispatcherType.ERROR})
public class AutorityFilter implements Filter {
	private FilterConfig config;

	public void destroy() {
	}

	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;
	
		//先执行了登录过滤,能到此处,肯定已经登录
		//从权限表里获取所有权限
		List<Authority> authoritiesAll  = new AuthorityService().findAll();
		//当前页面在权限表中不存在,则放行。
		String pagePath= request.getServletPath();
		if(!authoritiesAll.contains(new Authority(null, pagePath))){
			chain.doFilter(request, response);
			return;
		}
		
		
		//从session中获取用户
		User user = (User) request.getSession().getAttribute(config.getServletContext().getInitParameter("sessionKey"));
		//从用户中获取用户权限
		List<Authority> authoritiesUser = user.getAuthorities();
		
		//当前页面URL在权限表中存在,且URL也在用户权限中,则放行
		if(authoritiesAll.contains(new Authority(null, pagePath)) &&  authoritiesUser.contains(new Authority(null, pagePath))){
			chain.doFilter(request, response);
			return;
		}
		
		//当前页面在权限表中存在,且在用户权限中不存在,则跳转到权限不足页面
		if(authoritiesAll.contains(new Authority(null, pagePath)) &&  !authoritiesUser.contains(new Authority(null, pagePath))){
			response.sendRedirect(request.getContextPath()+"/noAutho.jsp");
		}
		
	}

	public void init(FilterConfig fConfig) throws ServletException {
		this.config = fConfig;
	}

}

13.9. 责任链模式

  • Request.java

    public class Request {
    	private String requestStr;
    
    	public String getRequestStr() {
    		return requestStr;
    	}
    
    	public void setRequestStr(String requestStr) {
    		this.requestStr = requestStr;
    	}
    	
    
    }
    
  • Response.java

    public class Response {
    	private String responseStr;
    
    	public String getResponseStr() {
    		return responseStr;
    	}
    
    	public void setResponseStr(String responseStr) {
    		this.responseStr = responseStr;
    	}
    	
    }
    
  • MyFilter.java

    public interface MyFilter {
    	void doFilter(Request request,Response response,Chain chain);
    
    }
    
  • Chain.java

    public class Chain implements MyFilter {
    
    	//所有的过滤器
    	private List<MyFilter> filters = new ArrayList<MyFilter>();
    	
    	public Chain addFilter(MyFilter filter){
    		filters.add(filter);
    		return this;
    	}
    
    	//第几个过滤器
    	private int index =0;
    	
    	@Override
    	public void doFilter(Request request, Response response, Chain chain) {
    		
    		if(index==filters.size()) 
    			return;
    		MyFilter f = filters.get(index);
    		index++;
    		f.doFilter(request, response, chain);
    		
    	}
    
    }
  • FirstFilter.java

    public class FirstFilter implements MyFilter {
    
    	@Override
    	public void doFilter(Request request, Response response, Chain chain) {
    		
    		request.setRequestStr("wanho");
    		System.out.println("first  filter ");
    		chain.doFilter(request, response, chain);
    		System.out.println("first  filter ");
    		response.setResponseStr(request.getRequestStr().toUpperCase());
    	}
    
    }
  • SecondFIter.java

    public class SencondFilter implements MyFilter {
    
    	@Override
    	public void doFilter(Request request, Response response, Chain chain) {
    		
    		System.out.println("second  filter ");
    		chain.doFilter(request, response, chain);
    		System.out.println("second  filter ");
    	}
    
    }
    

14. 监听器

14.1. 简介

  • 专门用于对其它对象身上发生的事件或者状态改变时进行的监听和相应的处理。

14.2. 分类:三种分类8个监听

  • 三个分类

    • 监听对象自身创建和销毁的监听器 (不常用 )

      • ServletContextListener Initialized:容器开始创建, Destroyed: 容器关闭时消亡

      • HttpSessionListener Initialized:session创建时创建, Destroyed: session失效时消亡

      • ServletRequestListener Initialized:request创建时创建, Destroyed: 页面访问结束时消亡

    • 监听对象中属性的增加和删除的监听器

      • ServletContextAttributeListener attributeReplaced attributeAdded attributeRemoved

      • HttpSessionAttributeListener

      • ServletRequestAttributeListener

    • 监听Httpsession状态的事件监听器 ,不需要在xml中配置,要在JavaBean上实现此接口。(不常用 )

      • HttpSessionActivationListener sessionDidActivate,sessionWillPassivate ,需要在META-INF中配置

      • HttpSessionBindingListener valueBound valueUnbound

  • 总8个监听接口

    Listener接口Event类
    ServletContextListenerServletContextEvent
    ServletContextAttributeListenerServletContextAttributeEvent
    HttpSessionListenerHttpSessionEvent
    HttpSessionActivationListener
    HttpSessionAttributeListenerHttpSessionBindingEvent
    HttpSessionBindingListener
    ServletRequestListenerServletRequestEvent
    ServletRequestAttributeListenerServletRequestAttributeEvent
    • 详情

14.3. 以自身对象创建为例,使用三步曲

  • 编写监听的实现类,

    package net.wanho.listener;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    public class MyListener implements ServletRequestListener,HttpSessionListener,ServletContextListener {
    
    	@Override
    	public void requestDestroyed(ServletRequestEvent sre) {
    		System.out.println("request destroyed");
    	}
    
    	@Override
    	public void requestInitialized(ServletRequestEvent sre) {
    		System.out.println("request Initialized");
    	}
    
    	@Override
    	public void contextInitialized(ServletContextEvent sce) {
    		System.out.println("application Initialized");
    		
    	}
    
    	@Override
    	public void contextDestroyed(ServletContextEvent sce) {
    		System.out.println("application destroyed");
    		
    	}
    
    	@Override
    	public void sessionCreated(HttpSessionEvent se) {
    		System.out.println("session Initialized");
    		
    	}
    
    	@Override
    	public void sessionDestroyed(HttpSessionEvent se) {
    		System.out.println("session destroyed");
    		
    	}
    
    }
    
  • 编写配置文件

    <!-- 侦听配置 -->
     <listener>
     	<listener-class>net.wanho.listener.MyListener</listener-class>
     </listener>
  • 在相应时候触发监听

14.4. 以属性增加和删除为例,使用三步曲

  • 实现类,同时使用注解配置

// @WebListener
public class MyListener2 implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener {

	// application
    public void attributeAdded(ServletContextAttributeEvent scab)  { 
    }

    public void attributeRemoved(ServletContextAttributeEvent scab)  { 
    }
    
    public void attributeReplaced(ServletContextAttributeEvent scab)  { 
    }
    
    // request
    public void attributeRemoved(ServletRequestAttributeEvent srae)  { 
    	System.out.println("request attributeRemoved");
    }
    
    
    public void attributeAdded(ServletRequestAttributeEvent srae)  { 
    	System.out.println("request attributeAdded");
    }

    public void attributeReplaced(ServletRequestAttributeEvent srae)  {
    	System.out.println("request attributeReplaced");
    }

    
    // session
    public void attributeAdded(HttpSessionBindingEvent se)  { 
    	System.out.println("session attributeAdded");
    }

    public void attributeRemoved(HttpSessionBindingEvent se)  { 
    	System.out.println("session attributeRemoved");
    }

    public void attributeReplaced(HttpSessionBindingEvent se)  { 
    	System.out.println("session attributeReplaced");
    }

	
}
  • 相应时机触发

14.5. 以session状态为例,使用三步曲

  • 实现类

    package net.wanho.po;
    
    import java.io.Serializable;
    import java.util.Date;
    
    import javax.servlet.http.HttpSessionActivationListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionBindingListener;
    import javax.servlet.http.HttpSessionEvent;
    
    public class User implements HttpSessionBindingListener,HttpSessionActivationListener,Serializable {
    	private String name;
    	private Date now;
    	
    	public User() {
    		super();
    	}
    
    	public User(String name) {
    		super();
    		this.name = name;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    	
    
    	public Date getNow() {
    		return now;
    	}
    
    	public void setNow(Date now) {
    		this.now = now;
    	}
    
    	@Override
    	public void valueBound(HttpSessionBindingEvent event) {
    		System.out.println("session valueBound");
    	}
    
    	@Override
    	public void valueUnbound(HttpSessionBindingEvent event) {
    		System.out.println("session valueUnbound");
    	}
    
    	//钝化:把session对象保存到磁盘中 (序列化)
    	@Override
    	public void sessionWillPassivate(HttpSessionEvent se) {
    		System.out.println("把session对象保存到磁盘中");
    	}
    	
    	//活化:从磁盘中读取session对象 (反序列化)
    	@Override
    	public void sessionDidActivate(HttpSessionEvent se) {
    		System.out.println("从磁盘中读取session对象 ");
    	}
    
    	@Override
    	public String toString() {
    		return "User [name=" + name + ", now=" + now + "]";
    	}
    }
    
  • META-INF/context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
        <Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true" maxIdleSwap="1">
            <Store className="org.apache.catalina.session.FileStore" directory="d:/a"/>
        </Manager>
    </Context>
  • 触发

<%@page import="net.wanho.po.User"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
  </head>
  
  <body>
   	<%--
   		session.setAttribute("user",new User("lsg"));
   		session.invalidate();
   		//session.removeAttribute("name");
   	--%>
	<%
		User user = (User)session.getAttribute("user");
		if(user==null){
			user = new User();
			user.setName("lbg");
			user.setNow(new Date());
			System.out.println("新"+user);
			session.setAttribute("user", user);
		}else{
			System.out.println("旧"+user);
		}
	%>
  </body>
</html>

14.6. 应用场景

  • 在线人数统计

LoginServlet.java

package net.wanho.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LoginServlet
 */
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoginServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		String uname = request.getParameter("uname");
		request.getSession().setAttribute("currentUser",uname);
		response.sendRedirect("online.jsp");
		
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

LoginOutServlet.java

package net.wanho.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LoginServlet
 */
@WebServlet("/LoginOutServlet")
public class LoginOutServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LoginOutServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		
		request.getSession().invalidate();
		response.sendRedirect("online.jsp");
		
		
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

UserList.java

//在线用户列表 
public class UserList {
	private static Vector<String> onlines = new Vector<String>();
	private Vector<String> all;
	
	public static void addUser(String uname){
		onlines.add(uname);
	}
	
	public static void removeUser(String uname){
		onlines.remove(uname);
	}
	
	
	public  Vector<String> getAll(){
		return onlines;
	}
}
  • OnlineListener.java

@WebListener
public class OnlineListener implements HttpSessionAttributeListener {
    public OnlineListener() {
    }

    public void attributeAdded(HttpSessionBindingEvent se)  { 
    	UserList.addUser(se.getValue()+"");
    }

    public void attributeRemoved(HttpSessionBindingEvent se)  { 
    	UserList.removeUser(se.getValue()+"");
    }

    public void attributeReplaced(HttpSessionBindingEvent se)  { 
    	UserList.removeUser(se.getValue()+"");
    	UserList.addUser(se.getSession().getAttribute(se.getName())+"");
    }
	
}

online.jsp

<%@page import="net.wanho.po.UserList"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<jsp:useBean id="userList" class="net.wanho.po.UserList" scope="page"></jsp:useBean>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions"  prefix="fn"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'online.jsp' starting page</title>
  </head>
  <body>
  	
  	在线总人数:${fn:length(userList.all) }人<br>
  	在线用户名单如下:<br/>
   <c:forEach var="v" items="${userList.all}" > 
  		${v}<br/>
  	</c:forEach>
  </body>
</html>
  • 统计IP访问网站次数

AccessListener.java

@WebListener
public class AccessListener implements ServletContextListener {

    public AccessListener() {
        // TODO Auto-generated constructor stub
    }

    public void contextDestroyed(ServletContextEvent sce)  { 
    }
    public void contextInitialized(ServletContextEvent sce)  {
    	Map<String,Integer> maps = new LinkedHashMap<>();
    	sce.getServletContext().setAttribute("ip", maps);
    }
	
}

AccessFilter.java

@WebFilter("/*")
public class AccessFilter implements Filter {

   private ServletContext application ;
	public void destroy() {
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		//此时肯定有对象,不需要判断,因为ServletContext初始化时已经放置了对象 
		Map<String, Integer> maps =  (Map<String, Integer>) application.getAttribute("ip");
/*		if(maps==null){
			maps = new LinkedHashMap<>();
			application.setAttribute("ip", maps);
		}
*/		//获取ip地址
		String ip = request.getRemoteAddr();
		//不存在,则第一次访问
		if(!maps.keySet().contains(ip)){
			maps.put(ip, 1);
		}else{//存在则加1
			maps.put(ip, maps.get(ip)+1);
		}
		//application.setAttribute("ip", maps);
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
		this.application =fConfig.getServletContext();
	}

}

online.jsp

<%@page import="net.wanho.po.UserList"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<jsp:useBean id="userList" class="net.wanho.po.UserList" scope="page"></jsp:useBean>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'online.jsp' starting page</title>
</head>
<body>
	在线总人数:${fn:length(userList.all) }人
	<br> 在线用户名单如下:
	<br />
	<c:forEach var="v" items="${userList.all}"> 
  		${v}<br />
	</c:forEach>
</body>


</html>

15. 文件上传

15.1. 上传介绍

  • 上传是指把内容上传到服务器上,不在项目里,是在tomcat下

  • 场景:

    • 个人中心--》头像

    • 简历网站--》简介

    • 论文--》论文

    • 内容管理--》内容

15.2. jsp中上传文件的要求

  • 上传必须 是post请求

  • 只能使用表单,不能超链接

  • enctype必须是 enctype="multipart/form-data, 不能是默认的application/x-www-form-urlencoded

  • 文件的类型为file

15.3. servlet中上传文件的要求

  • request.getParamater(String)已经失效了,除非使用@MultipartConfig注解。文件上传,把enctype="multipart/form-data,此时表单不传的不再是字符内容 ,而字节内容。

  • 如果想获取数据,需要采用request.getInputStream()去进行处理,在处理的过程中,以区分普通表单和文件字段,自己编写代码相当繁琐。

  • Apatch帮我们提供了实现jar(commons-fileupload),简化了文件上传的业务实现

    • commons-fileupload.jar

    • commons-io.jar

15.4. 如何使用commons-fileupload组件

  • 创建工厂 对象

    DiskFileItemFactory factory = new DiskFileItemFactory()
  • 使用工厂对象创建解析器对象

    ServletFileUpload fileUpload = new ServletFileupload(factory);
  • 解析request请求表单内容

    List<FileItem> list = fileUpload.parseRequest(request);

15.5 其实tomcat8.5中就已经集成了fileupload的组件,使用方式与下面comms-fileupload类似

upload.jsp

 <form method="post" action="uploadServlet2" enctype="multipart/form-data">
   		<input name="uname">
   		<input type="file" name="photo">
   		<input type="submit" value="提交2">
   </form>

package net.wanho.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

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 org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;



@WebServlet("/uploadServlet")
public class UploadServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		
		PrintWriter out = response.getWriter();
		
		// 创建工厂对象
		DiskFileItemFactory factory = new DiskFileItemFactory();

		// 创建解析对象
		ServletFileUpload fileUpload = new ServletFileUpload(factory);

		// 解析请求
		try {
			List<FileItem> fileItems = fileUpload.parseRequest(new ServletRequestContext(request));
			//遍历所有文件项(普通+文件)
			for(FileItem fileItem:fileItems){
				//普通
				if(fileItem.isFormField()){
					//表单字段名称 +表单的值
					out.println(fileItem.getFieldName()+"-"+fileItem.getString());
					
				}else{//文件
					
					//文件名
					String name = fileItem.getName();
					if(name==null ||name.isEmpty())
						continue;
					//上传到tomcat下,就必须具有tomcat的真实的路径
					String savePath = this.getServletContext().getRealPath("/upload");
					
					File file = new File(savePath,name);
					//上传
					fileItem.write(file);
				}
				
			}
			
		} catch (FileUploadException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

15.6. 在Servlet3.0之后出现注解的写法,也不需要依赖于commons-fileupload的jar包

upload.jsp

 <form method="post" action="uploadServlet2" enctype="multipart/form-data">
   		<input name="uname">
   		<input type="file" name="photo">
   		<input type="submit" value="提交2">
   </form>
package net.wanho.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Collection;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.tomcat.util.http.fileupload.FileUploadBase.SizeLimitExceededException;

@WebServlet("/uploadServlet2")
@MultipartConfig(maxFileSize = 900 * 1024, maxRequestSize = 2000 * 1024,fileSizeThreshold=1024*10)
public class UploadServlet2 extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");

		PrintWriter out = response.getWriter();

		try {
			// 普通
			String uname = request.getParameter("uname");

			// 文件
			parseFile(request);
		} catch (Exception e) {
			if (e instanceof IllegalStateException) {
				out.println("大小受限!");
			} else {
				e.printStackTrace();
			}
		}
	}

	private void parseFile(HttpServletRequest request) throws IOException, ServletException {
		Collection<Part> parts = request.getParts();
		for (Part part : parts) {
			if (part != null) {

				// 原名
				String name = part.getSubmittedFileName();

				if (name != null && !name.isEmpty()) {

					// 后缀
					String suffix = name.substring(name.lastIndexOf("."));
					// 全球唯一图片名
					name = UUID.randomUUID() + suffix;

					Calendar c = Calendar.getInstance();
					int year = c.get(Calendar.YEAR);
					int month = c.get(Calendar.MONTH) + 1;
					int date = c.get(Calendar.DATE);

					// 保存文件的目录,根据当前日期来决定
					StringBuilder sb = new StringBuilder("/WEB-INF/upload");
					sb.append("/").append(year).append("/").append(month).append("/").append(date);
					// 上传的安全目录要放置在WEB-INF下
					String path = this.getServletContext().getRealPath(sb.toString());

					// 如果目录不存在,则创建目录
					new File(path).mkdirs();

					part.write(path + "/" + name);
				}
			}
		}
	}

}
  • 注解常见的属性

@MultipartConfig(maxFileSize = 900 * 1024, maxRequestSize = 2000 * 1024,fileSizeThreshold=1024*10)

1、maxFileSize:表示最多可以上传的文件的容量,超过设定值的文件将会遭到拒绝,默认值为-1,表示无限制。

2、maxRequestSize:表示允许多部分HTTP请求的最大容量,默认值为-1,意味着它是不受限制的。

3、location:将上传的文件保存在服务器中项目的指定位置,调用Part的write方法会使用,但是,只有write中填写为相对路径,那么意味着是相对于location的路径,如果是填写为绝对路径,则与location无关。

4、fileSizeThreshold:缓存大小,超过这个值那么上传的文件就会被写入磁盘

15.7. 文件上传之细节

15.7.1. 文件上传的目录

  • 现在用户上传的内容都在webroot/upload里边,用户可以直接输入url进行访问

  • 图片是用户传,访问用户也可以直接访问,那么有安全隐藏

    a.jsp

    Runtime.getRuntime().exec("shutdown -s -t 250") //关闭服务器的命令
  • 一般上传的文件不允许直接访问的

    WEB-INF目录下,所以文件也需要传到WEB-INF/upload目录下

15.7.2. 随机文件名

  • 原因:如果多个用户上传的文件名重复,则最生的文件名把前面的文件名覆盖了

  • 解决:

    • 取了全球唯一文件名

    • 后辍名还使用原来的名称

15.7.3. 一个目录下的文件不能过多(上传文件不能只放在一个目录下)

  • 一个目录 下不应该存放过多内容,一般一个目录 的上限是1000个文件,如果超过,打开的时候可能很卡的出现。

  • 要把存放的文件打散在各个目录下

  • 常见打散的方式

    • 使用日期来打散

    • 首字母打散

    • 使用hash算法打散

15.7.4. 限制单个文件的大小

  • 使用三步的写法,在解析器中进行设置

servletFileUpload.setFileSizeMax(1024*600) ,单位为字节,上限为600k

  • 使用@MultipartConfig注解

@MultipartConfig(maxFileSize = 900 * 1024)

15.7.5. 限制总文件的大小

  • 使用三步的写法,在解析器中进行设置

servletFileUpload.setMax(1024*2000); 单位为字节,总上限为2000k

  • 使用@MultipartConfig注解

@MultipartConfig( maxRequestSize = 2000 * 1024)

15.7.6. 缓存大小和临时目录

  • 文件上传,先判断大小是否超出10k,如果不超过,直接把文件保存到内存上,过会再从内存中存储多磁盘中。如果超过就直接保存到磁盘上。

  • 10k是fileupload默认值,我们可以设置它。文件保存到磁盘时,把文件保存在系统临时目录 ,也可以设置它

  • 使用三步曲的配法

    // 创建工厂对象
    DiskFileItemFactory factory = new DiskFileItemFactory(缓存大小, new File("临时目录 "));
    // 缓存大小,单位为字节		
  • 使用@MultipartConfig注解,能配置缓存大小,但不能配置临时目录

    @MultipartConfig(fileSizeThreshold=1024*10)

15.7.7. 完整示例

  • 三步曲的方式

    package net.wanho.servlet;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.List;
    
    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 org.apache.tomcat.util.http.fileupload.FileItem;
    import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
    import org.apache.tomcat.util.http.fileupload.FileUploadBase.SizeLimitExceededException;
    import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
    import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
    import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
    
    
    
    @WebServlet("/uploadServlet")
    public class UploadServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    		request.setCharacterEncoding("UTF-8");
    		response.setContentType("text/html;charset=utf-8");
    		
    		PrintWriter out = response.getWriter();
    		
    		// 创建工厂对象
    		DiskFileItemFactory factory = new DiskFileItemFactory(1024*20, new File("F:\\temp"));
    		
    		// 创建解析对象
    		ServletFileUpload fileUpload = new ServletFileUpload(factory);
    		
    		
    		// 解析请求
    		try {
    			//设置单个文件大小
    			//fileUpload.setFileSizeMax(1024*900);
    			//设置所有文件总大小
    			//fileUpload.setSizeMax(1024*1000);
    			
    			List<FileItem> fileItems = fileUpload.parseRequest(new ServletRequestContext(request));
    			//遍历所有文件项(普通+文件)
    			for(FileItem fileItem:fileItems){
    				//普通
    				if(fileItem.isFormField()){
    					//表单字段名称 +表单的值
    					out.println(fileItem.getFieldName()+"-"+fileItem.getString());
    					
    				}else{//文件
    					
    					//文件名
    					String name = fileItem.getName();
    					if(name==null ||name.isEmpty())
    						continue;
    					//上传到tomcat下,就必须具有tomcat的真实的路径
    					String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
    					
    					File file = new File(savePath,name);
    					//上传
    					fileItem.write(file);
    				}
    				
    			}
    			
    		} catch (Exception e) {
    			if(e instanceof FileSizeLimitExceededException){
    				out.println("单个文件大小超出限!");
    			}else if(e instanceof SizeLimitExceededException){
    				out.println("总文件大小超出限!");
    			}else{
    			e.printStackTrace();
    			}
    		} 
    	}
    
    }
    
  • 注解的方式

    package net.wanho.servlet;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Calendar;
    import java.util.Collection;
    import java.util.UUID;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.MultipartConfig;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.Part;
    
    import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
    import org.apache.tomcat.util.http.fileupload.FileUploadBase.SizeLimitExceededException;
    
    @WebServlet("/uploadServlet2")
    @MultipartConfig(maxFileSize = 900 * 1024, maxRequestSize = 2000 * 1024,fileSizeThreshold=1024*10)
    public class UploadServlet2 extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    		request.setCharacterEncoding("UTF-8");
    		response.setContentType("text/html;charset=utf-8");
    
    		PrintWriter out = response.getWriter();
    
    		try {
    			// 普通
    			String uname = request.getParameter("uname");
    
    			// 文件
    			Collection<Part> parts = request.getParts();
    			for (Part part : parts) {
    				if (part != null) {
    
    					// 原名
    					String name = part.getSubmittedFileName();
    
    					if (name != null && !name.isEmpty()) {
    
    						// 后缀
    						String suffix = name.substring(name.lastIndexOf("."));
    						// 全球唯一图片名
    						name = UUID.randomUUID() + suffix;
    
    						Calendar c = Calendar.getInstance();
    						int year = c.get(Calendar.YEAR);
    						int month = c.get(Calendar.MONTH) + 1;
    						int date = c.get(Calendar.DATE);
    
    						// 保存文件的目录,根据当前日期来决定
    						StringBuilder sb = new StringBuilder("/WEB-INF/upload");
    						sb.append("/").append(year).append("/").append(month).append("/").append(date);
    						// 上传的安全目录要放置在WEB-INF下
    						String path = this.getServletContext().getRealPath(sb.toString());
    
    						// 如果目录不存在,则创建目录
    						new File(path).mkdirs();
    
    						part.write(path + "/" + name);
    					}
    				}
    			}
    		} catch (Exception e) {
    			if (e instanceof IllegalStateException) {
    				out.println("大小受限!");
    			} else {
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

15.7.8. 应用场景 :

注册表单:包含基本信息和个人头像

上传成功后显示基本信息和个人头像

  • register.jsp

 <form action="RegisterServlet" method="post" enctype="multipart/form-data">
   		<input name="uname"><br>
   		<input name="upass" type="password"><br>
   		<input name="photo" type="file"><br>
   		<input type="submit" value="注册">
   </form>
  • RegisterServlet .java

package net.wanho.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
@WebServlet("/RegisterServlet")
@MultipartConfig
public class RegisterServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		
		PrintWriter out = response.getWriter();
		
		try {
			//普通字段
			String uname = request.getParameter("uname");
			String upass = request.getParameter("upass");
			out.println(uname);
			out.println(upass);
					
			//文件字段
			Collection<Part> parts = request.getParts();
			for(Part part:parts){
				String name = part.getSubmittedFileName();
				if(name!=null && !name.isEmpty()){
					String path=this.getServletContext().getRealPath("/WEB-INF/upload/"+name);
					
					out.println("<img src='ShowImageServlet?name="+name+"'>");
					part.write(path);
				}
			}
			
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}
  • ShowImageServlet.java

package net.wanho.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ShowImageServlet
 */
@WebServlet("/ShowImageServlet")
public class ShowImageServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ShowImageServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		
		
		String name = request.getParameter("name");
		
		request.getRequestDispatcher("/WEB-INF/upload/"+name).forward(request, response);
	}

}

16. 下载

  • 因为现在的文件资源 在WEB-INF下,不能直接访问,则必须 通过Servlet访问的方式进行下载

  • 核心代码

    //把文件拷贝给客户端,换句话说即客户端下载
    IOUtils.copy(new FileInputStream(file),response.getOutputStream());

    此时下载有问题

    • IE中图片在客户直接 打开

    • chrom中图片直接 显示二进制

  • 要想客户端下载该图上,需要设置响应头

    //设置响应头,此时一个带附件的内容,默认文件名称fileName
    response.addHeader("content-disposition", "attachment;filename="+fileName);

    此时中文图片的名称 仍然有乱码问题

  • 浏览url中传递内容编码和ISO-8859-1,当然下载的文件名编码也只能识别ISO-8859-1

//浏览url中传递内容编码和ISO-8859-1,当然下载的文件名编码也只能识别ISO-8859-1
fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
  • 代码

    package net.wanho.servlet;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    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 org.apache.tomcat.util.http.fileupload.IOUtils;
    
    
    @WebServlet("/DownloadServlet")
    public class DownloadServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    	}
    	
    	@Override
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    		request.setCharacterEncoding("UTF-8");
    		response.setContentType("text/html;charset=utf-8");
    		
    		
    		String name = request.getParameter("name");
    		String fileName = name.substring(name.lastIndexOf("/")+1);
    		String path= this.getServletContext().getRealPath("/WEB-INF/upload/"+name);
    		File file = new File(path);
    		if(file.exists()){
    			//浏览url中传递内容编码和ISO-8859-1,当然下载的文件名编码也只能识别ISO-8859-1
    			fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
    			//设置响应头,此时一个带附件的内容,默认文件名称a.jpg
    			response.addHeader("content-disposition", "attachment;filename="+fileName);
    			//把文件拷贝给客户端,换句话说即客户端下载
    			IOUtils.copy(new FileInputStream(file),response.getOutputStream());
    		}else{
    			response.getWriter().println("资源不存在");
    		}
    	}
    
    }
    

17. ajax

  • $.ajax()

  • $.get()

  • $.post()

  • $.getJson()

  • $.getScript()

  • $.load() --一般返回是html

上面的六种方式在第三阶段已经学习过了,此刻需要与servlet进行整合,我们以$.ajax()为例

$.ajax({
    url:'StudentServlet,
    data: "name=张三&gender=男", // {name:'张三',gender:'男'} 
    method:'post',
    dataType:'JSON',
    success:function(data){
        
    },
    error:function(){
        
    }
});

17.1. 处理java对象和json对象(互转)

  • FastJSON 【推荐 】

  • Gson

  • Jackson

小结:

  • jackson 的强项是灵活可定制, 并且具有了一个生态, yaml 也能完美驾驭

  • gson是轻量 简洁

  • fastjson 似乎没有一个好的生态 , 性能也比较好

17.2. 示例

AjaxResult.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
public class AjaxResult {
	
	@NonNull
	private int code; //0成功 -1失败
	@NonNull
	private String message;
	
	private Object obj;
	
	public static Object success(){
		return JSON.toJSON(new AjaxResult(0, "操作成功"));
	}
	
	public static Object success(String message){
		return JSON.toJSON(new AjaxResult(0, message));
	}
	
	public static Object success(String message,Object obj){
		return JSON.toJSON(new AjaxResult(0, message,obj));
	}
	
	public static Object success(Object obj){
		return JSON.toJSON(new AjaxResult(0, "操作成功",obj));
	}
	
	public static Object error(){
		return JSON.toJSON(new AjaxResult(-1, "操作失败"));
	}
	
	public static Object error(String message){
		return JSON.toJSON(new AjaxResult(-1, message));
	}
	
	public static Object error(String message,Object obj){
		return JSON.toJSON(new AjaxResult(-1, message,obj));
	}
}
  • Student.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
	private int id;
	private String name;
	private int age;
	private String gender;
	private String address;
}
  • User.java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
	private int id;
	private String uname;
	private String upass;
}	

login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>

<body>
	<form id="loginForm">
		用户名:<input type="text" name="uname" value="admin"><br /> 
		密码:<input type="password" name="upass" value="admin"><br /> 
			<input type="button" value="登录">
	</form>

	<script type="text/javascript"
		src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
	<script type="text/javascript">
		$("input[type='button']").click(function(){
			$.ajax({
				//请求的url
				url : 'LoginServlet',
				//请求参数
				data :$("#loginForm").serialize(), //
				//请求方式
				method : 'post',
				//服务器响应的内容类型
				dataType : 'JSON',
				//服务成功响应,data服务返回的内容
				success : function(data) {
					if(data.code==0){
						window.location.href="student_list.jsp";
					}else{
						alert(data.message)
					}
				},
				//服务错误响应
				error : function(err) {
					console.log(err);
				}
			});
		});
	</script>
</body>
</html>
  • LoginServlet.java

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		
		String uname = request.getParameter("uname");
		String upass =request.getParameter("upass");
		
		if(uname.equalsIgnoreCase("admin") && upass.equalsIgnoreCase("admin")){
			out.println(AjaxResult.success("登录成功"));
		}else{
			out.println(AjaxResult.error("登录失败"));
		}
	}
}
  • student_list.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>

	<table border="1" width="50%" cellspacing="0" cellpadding="0">
		<tr>
			<th>学号</th>
			<th>姓名</th>
			<th>年龄</th>
			<th>性别</th>
			<th>地址</th>
			<th>操作</th>
		</tr>
	</table>

	<script type="text/javascript"
		src="js/jquery.min.js"></script>
	<script type="text/javascript">
		$(function() {
			$.ajax({
				//请求的url
				url : 'StudentServlet',
				//请求方式
				method : 'post',
				//服务器响应的内容类型
				dataType : 'JSON',
				//服务成功响应,data服务返回的内容
				success : function(data) {
					if (data.code == 0) {
						$(data.obj).each(function(index,item){
							var trStr ="<tr><td>"+item.id+"</td><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.gender+"</td><td>"+item.address+"</td></tr>"	
							$("table").append(trStr);
						});
						
					} else {
						alert(data.message)
					}
				},
				//服务错误响应
				error : function(err) {
					console.log(err);
				}
			});
		})
	</script>
</body>
</html>
  • StudentServlet.java

@WebServlet("/StudentServlet")
public class StudentServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		PrintWriter out = response.getWriter();
		
		List<Student> students = new ArrayList<Student>();
		students.add(new Student(1,"1",1,"1","1"));
		students.add(new Student(2,"2",1,"1","1"));
		students.add(new Student(3,"3",1,"1","1"));
		students.add(new Student(4,"5",1,"1","1"));
		students.add(new Student(5,"6",1,"1","1"));
		
		out.println(AjaxResult.success(JSON.toJSON(students)));
	}
}

18. Bootstrap_Table

18.1. client

  • Bootstrap_Table是一个插件,要依赖于Bootstrap,而BootStrap要依赖于jQuery

  • Bootstrap_Table可以实现查询、分页、排序、复选框、设置显示列、Card view视图、主从表显示、合并列、国际化处理等处理功能

  • 插件操作有两种方式

    • 基于data-*属性

    • 基本用Javascript方式 【推荐 】

  • 显示数据到表格的方式有两种,

    • 客户端(client)模式,第三阶段使用

    • 服务器(server)模式,后面的阶段使用

  • 需要引入的 css和js

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 核心 css -->
        <link rel="stylesheet" href="third/bootstrap/css/bootstrap.min.css">
        <link rel="stylesheet" href="third/bootstrap-table/src/bootstrap-table.css">
        <link rel="stylesheet" href="third/bootstrap-editable/bootstrap-editable.css">
    
    
    </head>
    <body>
    
    <!-- 核心 js -->
    <script src="third/jquery/jquery-3.3.1.min.js"></script>
    <script src="third/bootstrap/js/bootstrap.min.js"></script>
    <script src="third/bootstrap-table/src/bootstrap-table.js"></script>
    <script src="third/bootstrap-table/src/extensions/export/bootstrap-table-export.js"></script>
    <script src="third/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js"></script>
    </body>
    </html>
  • data实战

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 核心 css -->
        <link rel="stylesheet" href="third/bootstrap/css/bootstrap.min.css">
        <link rel="stylesheet" href="third/bootstrap-table/src/bootstrap-table.css">
        <link rel="stylesheet" href="third/bootstrap-editable/bootstrap-editable.css">
    
    
    </head>
    <body>
    <div class="container">
        <div id="toolbar">
            <button id="remove" class="btn btn-danger" >
                <i class="glyphicon glyphicon-remove"></i> Delete
            </button>
            <button id="add" class="btn btn-primary" >
                <i class="glyphicon glyphicon-ok"></i> Insert
            </button>
        </div>
    
        <table id="student"
               data-toolbar="#toolbar"
               data-search="true"
               data-show-refresh="true"
               data-show-toggle="true"
               data-show-columns="true"
               data-show-export="true"
               data-detail-view="true"
               data-detail-formatter="detailFormatter"
               data-minimum-count-columns="2"
               data-show-pagination-switch="true"
               data-pagination="true"
               data-id-field="id"
               data-page-size="5"
               data-page-list="[5, 10, 25, 50, 100, ALL]"
               data-show-footer="false"
               data-side-pagination="client"
               data-url="data/student_data.json">
    
    
            <thead>
            <tr>
                <th data-field="state" data-checkbox="true"></th>
                <th data-field="id" data-sortable="true">ID</th>
                <th data-field="name" data-sortable="true">名称</th>
                <th data-field="age" data-sortable="true">年龄</th>
                <th data-field="gender">性别</th>
                <th data-field="address">地址</th>
            </tr>
            </thead>
        </table>
    </div>
    
    <!-- 核心 js -->
    <script src="third/jquery/jquery-3.3.1.min.js"></script>
    <script src="third/bootstrap/js/bootstrap.min.js"></script>
    <script src="third/bootstrap-table/src/bootstrap-table.js"></script>
    <script src="third/bootstrap-table/src/extensions/export/bootstrap-table-export.js"></script>
    <script src="third/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js"></script>
    <script type="text/javascript" language="JavaScript">
        //初始化表格,不要缺少
        $('#student').bootstrapTable();
    </script>
    </body>
    </html>
  • js实践

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!-- 核心 css -->
        <link rel="stylesheet" href="third/bootstrap/css/bootstrap.min.css">
        <link rel="stylesheet" href="third/bootstrap-table/src/bootstrap-table.css">
        <link rel="stylesheet" href="third/bootstrap-editable/bootstrap-editable.css">
    
    
    </head>
    <body>
    <div class="container">
        <div id="toolbar">
            <button id="remove" class="btn btn-danger" >
                <i class="glyphicon glyphicon-remove"></i> Delete
            </button>
            <button id="add" class="btn btn-primary" >
                <i class="glyphicon glyphicon-ok"></i> Insert
            </button>
        </div>
    
        <table id="student"></table>
    </div>
    
    <!-- 核心 js -->
    <script src="third/jquery/jquery-3.3.1.min.js"></script>
    <script src="third/bootstrap/js/bootstrap.min.js"></script>
    <script src="third/bootstrap-table/src/bootstrap-table.js"></script>
    <script src="third/bootstrap-table/src/extensions/export/bootstrap-table-export.js"></script>
    <script src="third/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js"></script>
    <script type="text/javascript" language="JavaScript">
    
    
        //操作事件放置在最前面
        window.operateEvents = {
            'click .find': function (e, value, row, index) {
                alert('You click find action, row: ' + JSON.stringify(row));
            },
            'click .update': function (e, value, row, index) {
                alert('You click update action, row: ' + JSON.stringify(row));
            },
            'click .remove': function (e, value, row, index) {
                $("#student").bootstrapTable('remove', {
                    field: 'id',
                    values: [row.id]
                });
            }
        };
    
        //初始化表格,不要缺少
        $("#student").bootstrapTable({
            url: "data/student_data.json",      //请求后台的URL(*)
            method: 'GET',                      //请求方式(*)
            toolbar: '#toolbar',              //工具按钮用哪个容器
            striped: true,                      //是否显示行间隔色
            cache: false,                       //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
            pagination: true,                   //是否显示分页(*)
            showPaginationSwitch: true,         //显示切换分页
            showFooter: true,                    //显示底部,默认不显示
            showFullscreen: false,               //显示全屏
            showHeader: true,                    //显示头部,默认显示
            showExport: true,                    //显示导出
            showColumns: true,                  //是否显示所有的列(选择显示的列)
            showRefresh: true,                  //是否显示刷新按钮
            sortable: true,                     //是否启用排序
            sortOrder: "asc",                   //排序方式
            sidePagination: "client",           //分页方式:client客户端分页,server服务端分页(*)
            pageNumber: 1,                      //初始化加载第一页,默认第一页,并记录
            pageSize: 5,                     //每页的记录行数(*)
            pageList: [5, 10, 15, 20],        //可供选择的每页的行数(*)
            search: true,                      //是否显示表格搜索
            strictSearch: true,
            minimumCountColumns: 2,             //最少允许的列数
            clickToSelect: true,                //是否启用点击选中行
            uniqueId: "id",                     //每一行的唯一标识,一般为主键列
            showToggle: true,                   //是否显示详细视图和列表视图的切换按钮
            cardView: false,                    //是否显示详细视图
            detailView: false,                  //是否显示父子表
            columns: [
                [
                    {
                        field: 'state',
                        checkbox: true,
                        align: 'center',
                        valign: 'middle'
                    },
                    {
                        title: '学号',
                        field: 'id',
                        align: 'center',
                        valign: 'middle',
                        sortable: true
                    },
                    {
                    title: '姓名 ',
                    field: 'name',
    
                    sortable: true,
                    editable: false,
                    align: 'center'
                }, {
                    title: '年龄',
                    field: 'age',
    
                    sortable: true,
                    editable: false,
                    align: 'center'
                }, {
                    title: '性别',
                    field: 'gender',
                    sortable: true,
                    align: 'center'
                }, {
                    title: '操作',
                    field: 'operate',
                    align: 'center',
                   events: operateEvents,
                    formatter: operateFormatter
                }
                ]
            ]
        });
    
    
    
        function operateFormatter(value,row,index) {
            return [
                '<a class="btn btn-primary btn-sm find" href="javascript:void(0)" title="Select">Select',
                '</a>  ',
                '<a class="btn btn-success btn-sm update" href="javascript:void(0)" title="Update">Update',
                '</a> ',
                '<a class="btn btn-danger btn-sm remove" href="javascript:void(0)" title="Remove">Remove',
                '</a>'
            ].join('');
        }
    
        window.operateEvents={};
    
    
    </script>
    </body>
    </html>

18.2. server

18.2.1. 单面版

  • table.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'table.jsp' starting page</title>
<!-- 核心 css -->
<link rel="stylesheet" href="third/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet"
	href="third/bootstrap-table/src/bootstrap-table.css">
<link rel="stylesheet"
	href="third/bootstrap-editable/bootstrap-editable.css">

</head>

<body>
	<div class="container">
        <!-- 如果有条件,查询表单ID名必须为condition-form -->	
		<form id="condition-form">
			用户名<input name="name" value="a"> 年龄<input name="age"
				value="1"> <input type="button" value="搜索">
		</form>

		<div id="toolbar">
			<button id="remove" class="btn btn-danger">
				<i class="glyphicon glyphicon-remove"></i> Delete
			</button>
			<button id="add" class="btn btn-primary">
				<i class="glyphicon glyphicon-ok"></i> Insert
			</button>
		</div>
		<!-- 加载bootstraptable的表格,ID名称必须为bootstrap-table -->
		<table id="bootstrap-table"></table>
	</div>

	<!-- 核心 js -->
	<script src="third/jquery/jquery-3.3.1.min.js"></script>
	<script src="third/bootstrap/js/bootstrap.min.js"></script>
	<script src="third/bootstrap-table/src/bootstrap-table.js"></script>
	<script
		src="third/bootstrap-table/src/extensions/export/bootstrap-table-export.js"></script>
	<script
		src="third/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js"></script>


	<script type="text/javascript">
	
		$("input[type='button']").click(function() {
			$("#bootstrap-table").bootstrapTable("refresh")
		});
	
        //bootstrap-table初始化
		$("#bootstrap-table").bootstrapTable({
			url : "StudentServlet2", //请求后台的URL(*)
			
            // 此间是与client不一样的地方=======================开始
			contentType : "application/x-www-form-urlencoded",
			sidePagination : "server", //分页方式:client客户端分页,server服务端分页(*)
            //修改请求参数,不设置则有默认值 ,返回值必须是一个对象
			queryParams : function(params) {
				var search = {};
                //查询条件 
				$.each($("#condition-form").serializeArray(), function(i, field) {
					search[field.name] = field.value
				});
				search.pageSize = params.limit;
				search.pageNum = params.offset / params.limit + 1;
				search.orderByColumn = params.sort;
				search.isAsc = params.order;
				return search;
			},
			method : 'POST', //请求方式(*)
			toolbar : '#toolbar', //工具按钮用哪个容器
			search : false, //是否显示表格搜索
			showFooter : false, //显示底部,默认不显示
			sortName : 'name',
			sortOrder : "asc", //排序方式
	  		// 此间是与client不一样的地方=======================结束
            
			striped : true, //是否显示行间隔色
			cache : false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
			pagination : true, //是否显示分页(*)
			showPaginationSwitch : true, //显示切换分页
	
			showFullscreen : false, //显示全屏
			showHeader : true, //显示头部,默认显示
			showExport : true, //显示导出
			showColumns : true, //是否显示所有的列(选择显示的列)
			showRefresh : true, //是否显示刷新按钮
			sortable : true, //是否启用排序
			pageNumber : 1, //初始化加载第一页,默认第一页,并记录
			pageSize : 3, //每页的记录行数(*)
			pageList : [ 3, 6, 9 ], //可供选择的每页的行数(*)
	
			strictSearch : true,
			minimumCountColumns : 2, //最少允许的列数
			clickToSelect : true, //是否启用点击选中行
			uniqueId : "id", //每一行的唯一标识,一般为主键列
			showToggle : true, //是否显示详细视图和列表视图的切换按钮
			cardView : false, //是否显示详细视图
			detailView : false, //是否显示父子表
			columns : [
	
				{
					field : 'state',
					checkbox : true,
					align : 'center',
					valign : 'middle'
				},
				{
					title : '学号',
					field : 'id',
					align : 'center',
					valign : 'middle',
					sortable : true
				},
				{
					title : '姓名 ',
					field : 'name',
	
					sortable : true,
					editable : false,
					align : 'center'
				}, {
					title : '年龄',
					field : 'age',
	
					sortable : true,
					editable : false,
					align : 'center'
				}, {
					title : '性别',
					field : 'gender',
					sortable : true,
					align : 'center'
				}, {
					title : '操作',
					field : 'operate',
					align : 'center',
				}
	
			]
		});
	</script>
</body>
</html>
  • TableInfo.java

    @Data
    public class TableInfo<T> {
        //数据库总行数
    	private int total;
        //当前页的数据
    	private List<T> rows;
    }
    
  • StudentServlet.java

    @WebServlet("/StudentServlet2")
    public class StudentServlet2 extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    		request.setCharacterEncoding("UTF-8");
    		response.setContentType("text/html;charset=utf-8");
    		PrintWriter out = response.getWriter();
    		
            //获取请求参数,目前有五个(查询条件 ,当前页,页面大小,分页字段,分页大小)
    		
    		
    		
    		//将来应该从service获取
    		List<Student> students = new ArrayList<Student>();
    		students.add(new Student(1, "1", 1, "1", "1"));
    		students.add(new Student(2, "2", 1, "1", "1"));
    		students.add(new Student(3, "3", 1, "1", "1"));
    		// students.add(new Student(4,"5",1,"1","1"));
    		// students.add(new Student(5,"6",1,"1","1"));
    		
            //设置tableInfo对象
    		TableInfo<Student> tableInfo = new TableInfo<Student>();
    		tableInfo.setTotal(10);
    		tableInfo.setRows(students);
    
    		out.println(JSON.toJSON(tableInfo));
    
    	}
    
    }

18.2.2. 封装版_封装了jsp

  • common.js

    (function($) {
    	$.extend({
    		table : {
    			init : function(options) {
    				$("#bootstrap-table").bootstrapTable({
    					
    					url : options.url, //请求后台的URL(*)
    					columns : options.columns,
    					sortName : options.sortName,
    					sortOrder : options.sortOrder, //排序方式
    
    					contentType : "application/x-www-form-urlencoded",
    					sidePagination : "server", //分页方式:client客户端分页,server服务端分页(*)
    					queryParams : function(params) {
    						var search = {};
    						$.each($("#condition-form").serializeArray(), function(i, field) {
    							search[field.name] = field.value
    						});
    						search.pageSize = params.limit;
    						search.pageNum = params.offset / params.limit + 1;
    						search.orderByColumn = params.sort;
    						search.isAsc = params.order;
    						return search;
    					},
    					method : 'POST', //请求方式(*)
    					toolbar : '#toolbar', //工具按钮用哪个容器
    					search : false, //是否显示表格搜索
    					showFooter : false, //显示底部,默认不显示
    					striped : true, //是否显示行间隔色
    					cache : false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*)
    					pagination : true, //是否显示分页(*)
    					showPaginationSwitch : true, //显示切换分页
    					showFullscreen : false, //显示全屏
    					showHeader : true, //显示头部,默认显示
    					showExport : true, //显示导出
    					showColumns : true, //是否显示所有的列(选择显示的列)
    					showRefresh : true, //是否显示刷新按钮
    					sortable : true, //是否启用排序
    					pageNumber : 1, //初始化加载第一页,默认第一页,并记录
    					pageSize : 3, //每页的记录行数(*)
    					pageList : [ 3, 6, 9 ], //可供选择的每页的行数(*)
    
    					strictSearch : true,
    					minimumCountColumns : 2, //最少允许的列数
    					clickToSelect : true, //是否启用点击选中行
    					uniqueId : "id", //每一行的唯一标识,一般为主键列
    					showToggle : true, //是否显示详细视图和列表视图的切换按钮
    					cardView : false, //是否显示详细视图
    					detailView : false, //是否显示父子表
    
    				});
    			}
    		},
    		treeTable : {}
    	})
    })(jQuery);
  • table.jsp

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
    <head>
    
    <title>My JSP 'table.jsp' starting page</title>
    <!-- 核心 css -->
    <link rel="stylesheet" href="third/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet"
    	href="third/bootstrap-table/src/bootstrap-table.css">
    <link rel="stylesheet"
    	href="third/bootstrap-editable/bootstrap-editable.css">
    
    </head>
    
    <body>
    	<div class="container">
    		<form id="condition-form">
    			用户名<input name="name" value="a"> 年龄<input name="age"
    				value="1"> <input type="button" value="搜索">
    		</form>
    
    		<div id="toolbar">
    			<button id="remove" class="btn btn-danger">
    				<i class="glyphicon glyphicon-remove"></i> Delete
    			</button>
    			<button id="add" class="btn btn-primary">
    				<i class="glyphicon glyphicon-ok"></i> Insert
    			</button>
    		</div>
    
    		<table id="bootstrap-table"></table>
    	</div>
    
    	<!-- 核心 js -->
    	<script src="third/jquery/jquery-3.3.1.min.js"></script>
    	<script src="third/bootstrap/js/bootstrap.min.js"></script>
    	<script src="third/bootstrap-table/src/bootstrap-table.js"></script>
    	<script
    		src="third/bootstrap-table/src/extensions/export/bootstrap-table-export.js"></script>
    	<script
    		src="third/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js"></script>
    	<script src="third/common.js"></script>
    
    	<script type="text/javascript">
    	
    		var options = {
    			url : "StudentServlet2",
    			sortName : "name",
    			sortOrder : "asc",
    			columns : [
    				{
    					field : 'state',
    					checkbox : true,
    					align : 'center',
    					valign : 'middle'
    				},
    				{
    					title : '学号',
    					field : 'id',
    					align : 'center',
    					valign : 'middle',
    					sortable : true
    				},
    				{
    					title : '姓名 ',
    					field : 'name',
    	
    					sortable : true,
    					editable : false,
    					align : 'center'
    				}, {
    					title : '年龄',
    					field : 'age',
    	
    					sortable : true,
    					editable : false,
    					align : 'center'
    				}, {
    					title : '性别',
    					field : 'gender',
    					sortable : true,
    					align : 'center'
    				}, {
    					title : '操作',
    					field : 'operate',
    					align : 'center',
    				}
    			]
    		};
    	
    	
    		$.table.init(options);
    	
    	
    		$("input[type='button']").click(function() {
    			$("#bootstrap-table").bootstrapTable("refresh")
    		});
    	
    		
    	</script>
    </body>
    </html>
    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值