当前位置:   article > 正文

JavaWeb简讲_java-web

java-web

JavaWeb

Tomcat

1.1、官方链接https://tomcat.apache.org/download-80.cgi

在这里插入图片描述

1.2、启动:bin->startup

1.3、乱码解决

在这里插入图片描述

conf->logging.properties找到如下配置项:
java.util.logging.ConsoleHandler.encoding = UTF-8
将 UTF-8 修改为 GBK,修改后的效果为:
java.util.logging.ConsoleHandler.encoding = GBK
保存后,重启tomcat!
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

通过http://localhost:8080/访问服务器页面

访问页面时乱码:https://blog.csdn.net/weixin_39626745/article/details/110496357

1.4、关闭

正常关闭

bin->startup

强制关闭

点击启动窗口的×

1.5、配置

部署项目的方式

  1. 直接将项目放到webapps下即可

  2. 重新启动Tomcat服务器

  3. 在这里插入图片描述

  4. 分别为端口号->目录->网页名

  5. 简化部署:

    • war包方式

      • 将项目打成一个war包,再将war包放置到webapps目录下。
      • war包会自动解压缩
    • 配置conf/server.xml文件

      • 在这里插入图片描述
        docDase:项目存放的路径 path:虚拟目录
    • 在conf\Catalina\localhost创建任意名称的xml文件

      • 在文件中去编写以上图片中标签的内容(注,无path,虚拟目录为xml文件名)

1.6、项目部署

静态项目和动态项目

  • ​ 目录结构
    • java动态项目的目录结构
      • 项目的根目录
        • WEB-INF目录:
          • web.xml:web项目的核心配置文件
          • classes目录:放置字节码文件的目录
            • lib目录:放置依赖的jar包

1.7、IDEA集成创建Web项目

将Tomcat集成到IDEA中,并且创建JavaEE的项目,部署项目

  1. 选择IDEA的Run菜单->Edit Configurations…

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.8-IDEA与tomcat的相关配置

IDEA会为一个tomcat部署的项目单独建立一份配置文件

  • 查看控制台的log:
    • Using CATALINA_BASE: “C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\bee10957-0d37-4952-ba05-24f7c7f17d88” – 项目配置文件路径
  • 工作空间项目 和 tomcat部署的web项目
    • tomcat真正访问的是tomcat部署的web项目 ,它对应着工作空间项目的web目录下的所有资源
    • WEB-INF目录下的资源不能被浏览器直接访问
  • 断点调试
    • 在代码行左侧打断点,点击小爬虫或shift+f9使用debug模式启动tomcat

在这里插入图片描述

Servlet

2.1、Servlet概述

概念:运行在服务器端的小程序

  • Servlet就是一个接口,,定义了Java类被浏览器访问到(tomcat识别)的规则
  • 将来我们自定义一个类,实现Servlet接口,复写方法

2.2、Servlet入门

  1. 创建JavaEE项目
  2. 定义一个类,实现Servlet接口
  3. 实现接口中的抽象方法
  4. 在service(提供服务的方法)写入内容
  5. 配置Servlet(在WEB-INF下的web.xml文件)

实现Servlet接口,创建类

package com.example.javaWeb_Servlet;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author 玫瑰到了花期
 * @data 2022/3/21 14:19
 * @love 又偷偷看你了
 * @V: CBWR-K
 */
public class Servlet01 implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

编写web.xml配置文件

    <!--配置Servlet-->
    <servlet>
        <servlet-name>demo1</servlet-name>
        <servlet-class>com.example.javaWeb.ServletDemo01</servlet-class>
    </servlet>>
    <!--映射Servlet-->
    <servlet-mapping>
        <servlet-name>demo1</servlet-name>
        <url-pattern>/demo1</url-pattern>
    </servlet-mapping>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

通过url-pattern映射的路径来访问

2.3、Servlet执行原理

  1. ​ 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,是否有对应的标签体内容
  3. 如果有,则在找到对应的全类名
  4. tomcat会将字节码文件加载进内存,并且创建其对象
  5. 调用其方法

2.4、Servlet生命周期

简述

  1. 被创建:执行init方法,只执行一次
    • Servlet什么时候被创建
      • 默认情况下,第一次被访问时,Servlet被创建
      • 可以配置执行Servlet的创建时机
        • 在Servlet标签下配置
          1. 第一次被访问时,创建
            • 的值为负数
          2. 在服务器启动时,创建
            • 的值为0或正整数
      • Servlet的init方法只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的
        • 多个用户同时访问时,可能出现线程安全问题
          • 解决:尽量不要在Servlet中定义成员变量,即使定义了成员变量,也不要修改值
  2. 提供服务:执行service方法,执行多次
    • 每次访问Servlet方法,执行多次
  3. 被销毁:执行destroy方法,执行一次
    • Servlet被销毁时执行,服务器关闭时,Servlet被销毁
    • 只有服务器正常关闭时才会执行destroy方法
    • destroy方法在Servlet被销毁之前执行,一般用于释放资源

init()

    /**
     * 初始化方法
     * 在Servlet被创建时执行,只会执行一次
     * 执行在控制台
     * */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init....");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

getServletConfig()

     /**
     * 获取ServletConfig对象
     * ServletConfig:Servlet的配置对象
     * */
	@Override
    public ServletConfig getServletConfig() {
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

service()

    /**
     * 提供服务方法
     * 每一次Servlet被访问时,执行,执行多次
     * 执行在控制台
     * */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("servlet....");
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

getServletInfo()

    /**
     * 获取Servlet的一些信息,版本,作者等等。
     * */
	@Override
    public String getServletInfo() {
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

destroy()

    /**
     * 销毁方法
     * 在服务器正常关闭,执行,执行一次
     * */
    @Override
    public void destroy() {
        System.out.println("destroy....");
    }       

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

load-on-startup – xml

<load-on-startup>-5</load-on-startup>        
		<!--指定Servlet的创建时机
            1.第一次被访问时,创建
                <load-on-startup>的值为负数
            2.在服务器启动时,创建
                <load-on-startup>的值为0或正整数
        -->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.5、Servlet注解配置

简述

  • 好处
    • 支持注解配置。可以不需要web.xml了
  • 步骤
    1. 创建javaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
    2. 定义一个类,实现Servlet接口
    3. 复写方法
    4. 在类上使用@WebServlet注解,进行配置

使用

@WebServlet

//uelpatterns="访问路径" -- 注解写在类前	
@WebServlet(urlPatterns = "/demo")
//写法2
@WebServlet("/demo2")
//写法3,多条访问路径
@WebServlet({"/demo3","/demo4"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.6、Servlet体系结构

Servlet – 接口

​ |

GenericServlet – 抽象类

​ |

​ HttpServlet – 抽象类

  • GenericServlet: 默认将除了service方法外的所有方法都给实现了,只把service方法抽象化,这样servlet类在继承GenericService时只用重写service方法即可
  • ⭐HttpServlet:对http协议的一种封装,简化手动写判断请求方式的操作
    • 定义类继承HttpServlet

    • 复写doGet/doPost方法

    • doGet->

    • 在这里插入图片描述

    • doPost->

    • 在这里插入图片描述

2.7、快速创建Servlet类

在这里插入图片描述

2.8、Servlet路径配置

  • /xxx

    • 资源目录,访问此路径

    • 在这里插入图片描述

    • 在这里插入图片描述

  • /xxx/xxx

    • 多层路径,目录结构,需要输全路径才能访问

    • 在这里插入图片描述

    • 在这里插入图片描述

  • *.do

    • ==万能路径,表示==后面的路径可以随意输入

    • .do表示必须以do结尾

    • 在这里插入图片描述

    • 在这里插入图片描述

HTTP

3.1、概念

  • Hyper Text Transfer Protocol 超文本传输协议
  • 传输协议
    • 定义了,客户端和服务端通信时,发送数据的格式
  • 特点
    • 基于TCP/IP的高级协议
    • 默认端口号:8080
    • 基于请求/响应模型的
      • 一次请求对应一次响应
    • 无状态的
      • 每次请求之间相互独立的,不能交互数据
  • 历史版本
    • 1.0
      • 每次使用,都会重新建立连接
    • 1.1
      • 复用连接

3.2、请求消息与响应消息

请求消息的数据格式

3.2.1、请求行

请求方式请求url请求协议版本
GET/login.htmlHTTP1.1
  • HTTP协议游7种请求方式,常用的有2种
    • GET:
      • 请求参数在请求行中,在url后
      • 请求的url长度有限制
      • 不安全
    • Post:
      • 请求参数在请求体中、
      • 请求的url长度没有限制
      • 相对安全

3.2.2、请求头

请求头名称:请求头值

客户端浏览器告诉服务器一些信息

请求头名称使用
Host:localhost端口
User-Agent:浏览器告诉服务器,访问你使用的浏览器版本信息
可以在服务端获取该头的信息,解决浏览器兼容问题
Referer:告诉服务器,我(当前请求)从哪里来
作用:防盗链
统计
Connection:keep-alive 可以被复用

在这里插入图片描述

3.2.3、请求空行

空行,就是用于分割POST请求的请求头请求体

空行

3.2.4、请求体(正文)

  • 封装POST请求消息的请求参数

Request – 请求

4.1、继承体系

  • request对象和response对象的原理
    • request和response对象是由服务器创建的,我门来使用它
    • request对象是来获取请求消息
    • response对象是来设置响应消息
  • request:获取请求消息
    • 在这里插入图片描述

4.2、Request对象

获取请求消息数据

获取请求行数据 -F

  • Get /day1/demo1?name=傻逼胡子洋 HTTP/1.1

  • 方法:

    功能方法
    获取请求方式:GETString getMethod()
    ==*==获取虚拟目录:/day1String getContextPath()
    获取Servlet路径:/demo1String getServletPath()
    获取get方式请求参数:name=…String getQueryString()
    ==*==获取请求的url:/day1/demo1String getRetURL():/day1/demo1
    StringBuffer getRequestURL():http://localhost/day1/demo1
    获取协议及版本:HTTP1.1String getProtocol()
    获取客户机的IP地址String getRemoteAddr()
    URL:统一资源定位符 – 中华人民共和国
    URI:统一资源标识符 – 共和国
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1、获取请求方式
            String method = request.getMethod();
            System.out.println("请求方式为:"+method);
            //2、获取虚拟目录
            String contextPath = request.getContextPath();
            System.out.println("虚拟目录为:"+contextPath);
            //3、获取Servlet路径
            String servletPath = request.getServletPath();
            System.out.println("Servlet路径为:"+servletPath);
            //4、获取get方式请求参数
            String queryString = request.getQueryString();
            System.out.println("get方式请求参数为:"+queryString);
            //5、获取请求的url
            StringBuffer requestURL = request.getRequestURL();
            System.out.println("URL为:"+requestURL);
            //6、获取协议及版本
            String protocol = request.getProtocol();
            System.out.println("协议版本为:"+protocol);
            //7、获取客户机的IP地址
            String remoteAddr = request.getRemoteAddr();
            System.out.println("客户机的IP地址为"+remoteAddr);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

获取请求头数据 -F

功能方法
String contains(“”)request对象判断字符串
String getHeader(String name)==*==通过请求头的名称获取请求头的值
Enumeration getHeaderNames()获取所有请求头的值
hasMoreElements()测试此枚举是否包含更多的元素(迭代器)
nextElement()如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素

获取所有请求头以及值

在这里插入图片描述

在这里插入图片描述

S-user-agent

获取指定请求头的值

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

S-referer

在这里插入图片描述

获取请求体数据

  • 请求体

    • 只有POST请求方式才有请求体,在请求体中封装了POST请求的请求参数
  • 步骤

    1. 获取流对象

    2. 从流对象中拿数据

      • 方法说明
        BufferedReader getReader()获取字符输入流,只能操作字符数据
        ServletInputStream getInputStream()获取字节输入流,可以操作所有类型数据
        在文件上传后讲解
        .readLine()读取数据 以字符串形式返回这一行的数据
            @Override
            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //获取请求消息体 -- 请求参数
                //1.获取字符流
                BufferedReader br = request.getReader();
                //2.读取数据
                String line = null;
                while ((line = br.readLine())!=null){
                    System.out.println(line);
                }
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12

在这里插入图片描述

获取请求参数通用方式(s/get)-- F
方法说明
String getParameter(String name)根据参数名称获取参数值
password=123456参数传递"password"返回123456
String[] getParameterValues(String name)根据参数名称获取参数值的数组(多个数值)
name=“1”&name=“2”参数传递"name"返回所有name值
getParameterNames()获取所有请求的参数名称
Map<String,String[]> getParameterMap()获取所有参数的map集合
.keySet()输出键集合
getParameter
        //post 获取请求参数
        //根据参数名称获取参数值
        String user = request.getParameter("User");
        String password = request.getParameter("password");
        System.out.println(user+password);
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

String[] getParameterValues(String name)
//post 获取请求参数
//根据参数名称获取参数数组
String[] names = request.getParameterValues("hobby");
for (String name:names) {
    System.out.println(name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

在这里插入图片描述

Map<String,String[]> getParameterMap()
//post 获取请求参数
//获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keySet = parameterMap.keySet();
for (String name : keySet) {
    //获取键获取值
    String[] values = parameterMap.get(name);
    System.out.println(name);
    for(String value : values){
        System.out.println(value);
    }
    System.out.println("-------------");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述

在这里插入图片描述

获取请求参数中文乱码的问题
  • get方式:tomcat 8 已经将get方式乱码问题解决了

  • post方式:会乱码

    • 解决:在获取参数前,设置request的编码

    • request.setCharacterEncoding("utf-8");
      
      • 1
请求转发 --F

一种在服务器内部的资源跳转方式

  • 方法 request说明
    getRequestDispatcher(String path)通过request对象获取请求转发器对象
    forward(ServletRequest request,ServletResponse response)使用request对象进行转发
    /*Demo6*/
    System.out.println("Demo6666被访问了");
    //转发到Demo7资源
    RequestDispatcher requestDemo7 = request.getRequestDispatcher("requestDemo7");
    requestDemo7.forward(request,response);
    /*Demo7*/
    System.out.println("Demo7777被访问了");
    
    /*写法2*/  request.getRequestDispatcher("requestDemo7").forward(request,response);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    • 特点:
      1. 浏览器地址栏路径不会变
      2. 只能转发到当前服务器内部资源中
      3. 转发是一次请求
共享数据
  • 域对象:一个有作用范围的对象,可以在范围内共享数据

  • request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据

  • 方法:

    • 方法说明
      void setAttribute(String name,Object obj)存储数据
      Object getAttitude(String name)通过键获取值
      void removeAttribute(String name)通过键移除键值对
      /*Demo6*/
      System.out.println("Demo6666被访问了");
      //存储数据到request域中
      request.setAttribute("msg","hello");
      //转发到Demo7资源
      request.getRequestDispatcher("requestDemo7").forward(request,response);
      
      /*Demo7*/
      //获取request域的值
      Object msg = request.getAttribute("msg");
      System.out.println(msg);
      System.out.println("Demo7777被访问了");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

在这里插入图片描述

获取ServletContext对象
方法说明
ServletContext getServletContext()获取ServletContext对象
//获取servletContext对象
ServletContext servletContext = request.getServletContext();
System.out.println(servletContext);
  • 1
  • 2
  • 3

在这里插入图片描述

案例

需求

用户登录案例:

  1. 编写login.html登录页面
    • username & password 两个输入框
  2. 使用数据库,操作mysql的数据表
  3. 使用jdbc连接数据库
  4. 登录成功跳转到SuccessServlet展示:登陆成功!用户名:欢迎您
  5. 登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

分析

在这里插入图片描述

文件提取

链接:https://pan.baidu.com/s/1ApJyMdsI8sqJU6bQRXAeJw
提取码:1011

Response – 响应

5.1、HTTP协议

响应消息:服务器端发送给客户端的数据

数据格式

响应行
  • 组成

    • 协议/版本 响应状态码 状态码描述

    • 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态

    • 状态码都是3位数字

      • 分类:

      • 状态码(百)说明
        1xx服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多的状态码。
        2xx成功。代表:200
        3xx重定向。代表:302(重定向),304(访问缓存)
        4xx客户端错误。代表:
        404(请求路径没有对应的资源)
        405:请求方式没有对应的doxxx方法
        5xx服务器端错误。代表:500(服务器内部出现异常)
      • 在这里插入图片描述

      • 在这里插入图片描述

响应头
  • 格式:头名称:值

  • 常见的响应头

    • 响应头说明
      Content - Type:服务器告诉客户端本次响应体数据格式以及编码格式
      text/html;charset=UTF-8
      Content - disposition:服务器告诉客户端以什么格式打开响应体数据
      in - line:默认值,在当前页面内打开
      attachment;filename=xxx:以附件形式打开响应体。文件下载
响应空行
响应体
  • 传输的消息

5.2、Response对象

功能-设置响应消息

设置响应行
  • 格式:HTTP/1.1 200 OK

  • 方法说明
    setStatus(int sc)设置状态码
设置响应头
  • 方法说明
    setHeader(String name,String value)设置响应头
设置响应体
  • 使用步骤:

    1. 获取输出流

      • 方法说明
        PrintWriter getWriter()字符输出流
        ServletOutputStream getOutputSteam()字节输出流
    2. 使用输出流,将数据输出到浏览器

5.3、完成重定向

  • 资源跳转的方式

  • 在这里插入图片描述

    方法说明
    sendRedirect(String value)简单的重定向方法
  • //index
    System.out.println("index");
    //访问/ResponseIndex的时候自动跳转到/ResponseIndex2资源
    //1.设置状态码为302
    response.setStatus(302);
    //2.设置响应头location
    response.setHeader("location","ResponseIndex2");
    
    //index2
    System.out.println("index2");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    //简单的重定向方法
    response.sendRedirect("ResponseIndex2");
    //可访问其他站点
    response.sendRedirect("http://www.itcast.cn");
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

重定向的特点:redirct

  • 地址栏发生变化
  • 重定向可以访问其他站点(服务器)的资源、
  • 重定向是两次请求,不能使用request对象来共享数据
  • 他的对比项是"转发"

转发的特点:forward

  • 转发地址栏路径不变

  • 转发只能访问当前服务器下的资源

  • 转发是一次请求,可以使用request对象来共享数据

  • forward 和 redirct 区别

路径写法

相对路径:通过相对路径不可以确定唯一资源
  • 如:./index.html

  • 不以 / 开头,以 . 开头的路径称之为相对路径

  • 规则:找到访问当前资源和目标资源之间的相对位置关系

  • 方法说明
    /根目录
    ./当前目录
    …/上一级目录
  • 在这里插入图片描述

绝对路径:通过绝对路径可以确定唯一资源
方法说明
String n = request.getContextPath()动态获取虚拟目录
response.sendRedirect(n+“ResponseIndex2”)重定向方法
  • 如:http://localhost:8080/javaWeb_Response_Test_war_exploded/ResponseIndex2
  • / 开头的路径称之为绝对路径
  • 规则:判断定义的路径是给谁用的?判断请求将来从那发出
    • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
      • 虚拟目录动态获取
    • 给服务器使用:不需要加虚拟目录
      • 转发路径
  • 在这里插入图片描述

5.4、服务器输出字符数据到浏览器

  • 方法说明
    pw = getWriter()获取字符输出流
    pw.write(“

    hello response

    ”)
    输出数据
  • 中文乱码问题解决

    • 在这里插入图片描述

    • //获取流对象之前,设置流的默认编码:IOS-8859-1 设置为:GBK
      response.setCharacterEncoding("GBK");
      //告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
      response.setHeader("content-type","text/html;charset=utf-8");
      
      //简单的形式,设置编码
       response.setContentType("text/html;charset=utf-8");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

5.5、服务器输出字节数据到浏览器

方法说明
sos = response.getOutputStream()获取字节输出流
sos.write(“htllo”.getBytes(“utf-8”))输出数据
获取字节对象之前,设置字节的默认编码:IOS-8859-1 设置为:GBK
response.setContentType("text/html;charset=utf-8");
//获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//输出
sos.write("<h1>你好,baga</h1>".getBytes("utf-8"));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5.6、验证码

  • 本质:图片

  • 目的:防止恶意表单注册

    • 方法说明
      width=100验证码画布的宽
      height=50验证码画布的高
      bImg = BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);创建验证码图片对象
      g = bImg.getGraphics()画笔对象
      颜色获取
      g.setColor(Color.pink)设置画笔颜色
      填充背景色
      g.fillRect(0,0,width,height)填充画布颜色
      画边框
      g.drawRect(0,0,width-1,height-1)设置边框颜色
      验证码
      g.drawString(字符,x坐标,y坐标)写入验证码
      干扰线
      g.drawLine(x1,x2,x3,x4)设置干扰线
      输出
      ImageIO.write(bImg,“jpg”,response.getOutputStream())将图片输出到页面展示
    • int width = 100;
      int height = 50;
      //1.创建一对象,在内存中图片(验证码图片对象)
          BufferedImage bImg = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
      //2.美化图片
      //2.1、填充背景色
      Graphics g = bImg.getGraphics();//画笔对象
      g.setColor(Color.pink); //设置画笔颜色
      g.fillRect(0,0,width,height);
      //2.2、画边框
      g.setColor(Color.BLUE);
      g.drawRect(0,0,width-1,height-1);
      Random ram = new Random();
      //随机验证码
      for (int i = 1; i <= 4; i++) {
          String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
          //生成随机角标
          int index = ram.nextInt(str.length());
          //获取字符
          char ch = str.charAt(index);//随机字符
          //2.3、写入验证码
          g.drawString(ch+"",width/5*i,25);
      }
      //2.4、画干扰线
      g.setColor(Color.GREEN);
      //随机生成坐标点
      for (int i = 0; i < 4; i++) {
          int x1 = ram.nextInt(width);
          int x2 = ram.nextInt(width);
          int y1 = ram.nextInt(height);
          int y2 = ram.nextInt(height);
          g.drawLine(x1,x2,y1,y2);
      }
      //3.将图片输出到页面展示
      ImageIO.write(bImg,"jpg",response.getOutputStream());
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      
      </head>
      <body>
      <img src="CheckCodeServlet" alt="">
      <a href="">看不清换一张</a>
      </body>
      <script>
          /*
      *   分析:
      *       点击链接或者图片,需要换一张
      *       1.给图片和链接绑定单击事件
      *       2.重新设置图片的src属性值
      * */
          window.onload = function () {
              let img = document.getElementsByTagName("img")[0];
              img.onclick = function () {
                  let date = new Date().getTime();
                  //加时间戳
                  img.src = "CheckCodeServlet?" + date
              }
              let a = document.getElementsByTagName("a")[0];
              a.onclick = function () {
                  let date = new Date().getTime();
                  img.src = "CheckCodeServlet?" + date
              }
          }
      </script>
      </html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33

5.7、ServletContext

  • 概念:代表整个web应用,可以和程序的容器(服务器)来通信

  • 方法说明
    request.getServletContext()通过request对象获取
    this.getServletContext()通过HttpServlet获取
    //ServletContext对象获取
    //1.通过request对象获取
    ServletContext servletContext = request.getServletContext();
    //2.通过HttpServlet获取
    ServletContext servletContext1 = this.getServletContext();
    System.out.println(servletContext);
    System.out.println(servletContext1);
    System.out.println(servletContext==servletContext1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

获取MIME类型

  • MIME类型:在互联网通信过程中定义的一种文件数据类型
    • 格式: 大类型/小类型 text/html image/jpg
方法说明
String getMimeType(String file)获取
//ServletContext功能
//MIME类型:在互联网通信过程中定义的一种文件数据类型
//2.通过HttpServlet获取
ServletContext servletContext = this.getServletContext();
//3.定义文件名
String fileName = "a.jpg";
//4.获取MIME类型
String mimeType = servletContext.getMimeType(fileName);
System.out.println(mimeType);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

域对象:共享数据

方法说明
setAttribute(String name,Object value)设置共享数据
getAttribute(String name)获取共享数据
removeAttribute(String name)删除共享数据
  • ServletContext对象范围:所有用户所有请求的数据
demo3
//2.通过HttpServlet获取
ServletContext servletContext = this.getServletContext();
//设置数据
servletContext.setAttribute("msg","hello");
  • 1
  • 2
  • 3
  • 4
  • 5
demo4
//2.通过HttpServlet获取
ServletContext servletContext = this.getServletContext();
//获取数据
Object msg = servletContext.getAttribute("msg");
System.out.println(msg);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

分别访问后:

在这里插入图片描述

获取文件的真实(服务器)路径

方法说明
String getRealpath(String path)获取文件路径
//通过HttpServlet获取
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/b.txt");//web目录下资源访问
System.out.println(realPath);
//File file = new File(realPath);
String c = servletContext.getRealPath("/WEB-INF/c.txt");//web-inf目录下的资源访问
System.out.println(c);
String a = servletContext.getRealPath("/WEB-INF/classes/a.txt");//src路径下的资源访问
System.out.println(a);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

文件下载

概念需求

  • 超链接指向的资源如果能够被浏览器解析,则在浏览器中国展示,如果不能解析,则弹出下载提示框
  • 任何资源都必须淡出下载提示框 – 实现
  • 使用响应头设置资源的打开方式:
  • 步骤:
  1. 定义页面,编辑超链接href属性,指向Servlet,传递资源的名称
  2. 定义Servlet
    1. 获取文件名称
    2. 使用字节输入流加载文件进内存
    3. 指定response的响应头:content-disposition:attachment;filename=xxx
    4. 将数据写出到response输出流

代码

//1.获取请求参数,文件名称
String filename = request.getParameter("filename");
//2.使用字节输入流加载文件进内存
//2.1、找到文件服务器路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/"+filename);
//2.3、用字节流关联
FileInputStream file = new FileInputStream(realPath);
//3.设置response响应头
//3.1设置response响应头类型:content-type
//获取文件的MIME类型
String mimeType = servletContext.getMimeType(filename);
response.setHeader("content-type",mimeType);
//3.2设置响应头打开方式:content-disposition
response.setHeader("content-disposition","attachment;filename="+filename);
//4.将输入流的数据写出到输出流中
ServletOutputStream outputStream = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((file.read(buff)!=-1)){
    outputStream.write(buff,0,len);
}
file.close();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/javaWeb_Response_Test_war_exploded/img/1.jpeg">图片1</a>
<a href="/javaWeb_Response_Test_war_exploded/video/1.mp4">视频1</a>
<hr>
<a href="/javaWeb_Response_Test_war_exploded/downLoadServlet?filename=1.jpeg">图片1</a>
<a href="/javaWeb_Response_Test_war_exploded/downLoadServlet?filename=2.webp">图片2</a>
<a href="/javaWeb_Response_Test_war_exploded/downLoadServlet?filename=1.mp4">视频</a>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

中文文件名的解决

解决思路:

  1. 获取客户端使用的浏览器版本信息
  2. 根据不同的版本信息,设置filename的方式不同
//解决中文文件乱码问题
//1.获取User-agent请求头
String agent = request.getHeader("user-agent");
//2.工具类方法编码文件名即可
filename= DownLoadUtils.getFileName(agent, filename);
  • 1
  • 2
  • 3
  • 4
  • 5
        //1.获取请求参数,文件名称
        String filename = request.getParameter("filename");
        //2.使用字节输入流加载文件进内存
        //2.1、找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/"+filename);
        System.out.println("===>>"+realPath);
        //2.3、用字节流关联
        FileInputStream file = new FileInputStream(realPath);
        //3.设置response响应头
        //3.1设置response响应头类型:content-type
        //获取文件的MIME类型
        String mimeType = servletContext.getMimeType(filename);
        response.setHeader("content-type",mimeType);
        //3.2设置响应头打开方式:content-disposition

        //解决中文文件乱码问题
        //1.获取User-agent请求头
        String agent = request.getHeader("user-agent");
        //2.工具类方法编码文件名即可
        filename= DownLoadUtils.getFileName(agent, filename);

        response.setHeader("content-disposition","attachment;filename="+filename);

        //4.将输入流的数据写出到输出流中
        ServletOutputStream outputStream = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        while (((len = file.read(buff))!=-1)){
            outputStream.write(buff,0,len);
            outputStream.flush();
        }
        outputStream.close();
        file.close();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

DownLoadServlet类

package utils;

import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class DownLoadUtils {
    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Cookie会话 – 客户端会话技术

  • 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开位置

  • 在一次会话的范围内的多次请求间,共享数据

  • 概念:客户端会话技术,将数据保存到客户端

6.1、使用

方法说明
new Cookie(String name,String value)创建Cookie对象,绑定数据
response.addCookie(Cookie cookie)发送Cookie对象
Cookie[] request.getCookies()获取Cookie,拿到数据
/*demo1*/
//1.创建Cookie对象
Cookie cookie = new Cookie("msg","hello");
//2.发送Cookie
response.addCookie(cookie);
  • 1
  • 2
  • 3
  • 4
  • 5

Cookie遍历

/*demo2*/
//3.获取Cookie
Cookie[] cookies = request.getCookies();
//获取数据,遍历cookies
if (cookies!=null){
    for (Cookie c : cookies){
        String name = c.getName();
        String value = c.getValue();
        System.out.println(name+": "+value);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

实现原理

  • 基于响应头set-Cookie和请求头cookie实现
  • 在这里插入图片描述

6.2、发送多个Cookie

//1.创建Cookie对象
Cookie c1 = new Cookie("msg","hello");
Cookie c2 = new Cookie("name","word");
Cookie c3 = new Cookie("live","你听得到");
//2.发送Cookie
response.addCookie(c1);
response.addCookie(c2);
response.addCookie(c3);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

6.3、Cookie存活的时间

  • 默认情况下,当浏览器关闭后,Cookie数据被销毁

  • 以此可知,Cookie存在浏览器内存里面

    持久化存储

    方法说明
    setMaxAge(int seconds)持久化存储
    参数 单位/s:
    正数将Cookie数据写到硬盘的文件中。持久化储存。Cookie存活时间
    负数默认值
    删除cookie信息
    //1.创建Cookie对象
    Cookie c = new Cookie("live","setMaxAge");
    //2.设置cookie的存活时间
    c.setMaxAge(30);//将cookie持久化到硬盘,30秒后自动删除cookie文件
    c.setMaxAge(-1);
    c.setMaxAge(0);
    //3.发送Cookie
    response.addCookie(c);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

6.4、Cookie存储中文

tomcat8之后,cookie支持中文数据

tomcat8之前,cookie不支持中文数据,需要将中文数据转码:

一般采用URL编码(%E3)

//URL编码
date = URLEncoder.encode(date,"utf-8");
//URL解码
value = URLEncoder.encode(value,"utf-8");
  • 1
  • 2
  • 3
  • 4

6.5、Cookieo共享

  • 默认情况下cookie不能共享

  • 方法说明
    setPath(String path)设置cookie的获取范围。默认情况下设置当前的虚拟目录
    如果要共享,要将path值设置为 “/”
    //1.创建Cookie对象
    Cookie c = new Cookie("live","你听得到");
    //设置path,让当前服务器下部署的所有项目共享cookie信息
    c.setPath("/");
    //3.发送Cookie
    response.addCookie(c);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 不同的tomcat服务器间cookie共享问题

  • 方法说明
    setDomain(String path)如果设置一级域名相同,那么多个服务器之间cookie可以共享
    举例
    setDomain(“.baidu.com”)那么tieba.baidu.com和news.baidu.com中cookie可以共享

6.6、Cookie的特点和作用

  • cookie存储数据在客户端浏览器
  • 浏览器对于单个cookie的大小有限制(4KB),一级对同一个域名下的总cookie数量也有限制(2个)

作用:

  • cookie一般用于存储少量的不太敏感的数据
  • 在不登录的情况下完成服务器符客户端的身份识别

案例

记住上一次访问时间

需求:

  • 访问一个Servlet,如果是第一次访问,则提示:你好欢迎首次访问网页
  • 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串

分析:

  1. 要采用Cookie来完成
  2. 服务器中的Servlet判断是否有一个名为lastTime的cookie
    1. 有:不是第一次访问
      1. 响应数据:欢迎回来,您上次访问时间为:lastTime时间
      2. 写回Cookie:lastTime=当前时间
    2. 没有:是第一次访问
      1. 响应数据:您好,欢迎您首次访问
      2. 写回Cookie:lastTime=当前时间

代码

package com.example.javaWeb_CookieServlet;
/**
 * @author 玫瑰到了花期
 * @data 2022/3/29 17:57
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

@WebServlet("/ServletCookieIndex")
public class ServletCookieIndex extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        boolean isNaN = true;
        //设置中文乱码问题
        response.setContentType("text/html;charset=utf-8");
        //获取所有的cookie
        Cookie[] cookies = request.getCookies();
        if (cookies!=null){
            for (Cookie c : cookies){
                String name = c.getName();
                if ("listTime".equals(name)){
                    //有该cookie
                    isNaN = false;
                    //设置cookie的value
                    //获取当前时间,重新定义cookie的值,重新发送cookie
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
                    String str_date = sdf.format(date);
                    c.setValue(str_date);
                    //设置cookie一个月的存活时间
                    c.setMaxAge(60*60*24*30);
                    response.addCookie(c);
                    //响应数据
                    String value = c.getValue();
                    response.getWriter().write("欢迎回来,上次访问时间:"+value);
                    break;
                }
            }
        }
        if (cookies!=null|| isNaN == true){
            //没有,第一次访问
            //获取时间
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
            String str_date = sdf.format(date);
            //创建cookie,存储数据
            Cookie cookie = new Cookie("listTime",str_date);
            //设置cookie的存活时间
            cookie.setMaxAge(60*60*24*30);
            response.addCookie(cookie);
            //响应数据
            response.getWriter().write("你好,欢迎首次访问");
        }
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

JSP

7.1、概念

  • Java Server Pages : java服务器端页面

  • 可以理解为一个特殊的页面

    • 其中既可以直接定义html标签

    • 还可以定义java代码

  • 用于简化书写

7.2、原理

  • jsp本质上就是个servlet
  • 在这里插入图片描述

7.3、JSP脚本

JSP定义JAVA代码的方式

方法说明
<% java代码 %>在%内写入java代码,也称之为jsp的脚本定义的java代码,在service方法中。
service方法中可以定义什么,该脚本就可以定义什么
<%! java代码 %>第二种脚本,定义的java代码,在jsp转换后的java类的成员位置
<%= java代码 %>第三种脚本,定义的java代码,会输出到页面上
输出语句中可以定义什么,该脚本就可以定义什么

7.4、JSP内置对象

说明

  • 在jsp页面中不需要获取和创建,可以直接使用的对象
  • 在这里插入图片描述

关于jsp无法使用内置对象的方法

  • https://blog.csdn.net/weixin_43383406/article/details/94203696

JSP一共有9个内置对象

对象说明
request请求
response响应
out字符输出流对象,可以将数据输出到页面上。
和response.getWriter()类似
out和response.getWriter()的区别

在这里插入图片描述

改造Cookie案例

<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2022/3/31
  Time: 8:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    boolean isNaN = true;
    //设置中文乱码问题
    response.setContentType("text/html;charset=utf-8");
    //获取所有的cookie
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie c : cookies) {
            String name = c.getName();
            if ("listTime".equals(name)) {
                //有该cookie
                isNaN = false;
                //设置cookie的value
                //获取当前时间,重新定义cookie的值,重新发送cookie
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
                String str_date = sdf.format(date);
                c.setValue(str_date);
                //设置cookie一个月的存活时间
                c.setMaxAge(60 * 60 * 24 * 30);
                response.addCookie(c);
                //响应数据
                String value = c.getValue();
%>
<h1>"欢迎回来,上次访问时间:"<%=value%>
</h1>
<%
                break;
            }
        }
    }
    if (cookies != null && isNaN) {
        //没有,第一次访问
        //获取时间
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
        String str_date = sdf.format(date);
        //创建cookie,存储数据
        Cookie cookie = new Cookie("listTime", str_date);
        //设置cookie的存活时间
        cookie.setMaxAge(60 * 60 * 24 * 30);
        response.addCookie(cookie);
        //响应数据
%>
<h1>"你好,欢迎首次访问"</h1>
<%
    }
%>

</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

Session会话 – 服务器

8.1、概念

  • 服务器段会话技术,再一次会话的多次请求间共享数据,将数据保存在服务器段的对象中。Httpsession

8.2、HttpSession对象

方法说明
request.getSession()获取HttpSession对象
使用
void setAttribute(String name,Object value)存储数据
Object getAttribute(String name)获取session
void removeAttribute(String name)删除数据

8.3、原理

  • Session的实现是依赖于Cookie的
  • 在这里插入图片描述

客户端关闭,服务器不关闭,两次获取session是同一个么?

  • 默认情况下,不是

    • 在这里插入图片描述
  • 客户端关闭后,session也能相同

  • 如果需要相同,可以创建Cookie,键位JSESSIONID,设置最大存活时间,让cookie持久化存储

    • //1.获取session
      HttpSession session = request.getSession();
      System.out.println(session);
      //期望客户端关闭后,session也能相同
      Cookie c = new Cookie("JSESSIONID",session.getId());
      //持久化储存
      c.setMaxAge(60*60);
      //发送cookie
      response.addCookie(c);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

客户端不关闭,服务器关闭后么,两次获取的session是同一个么?

  • 不是同一个

    • 在这里插入图片描述

    • 在这里插入图片描述

  • session地址不一样,但要确保数据不丢失

  • session的钝化系列化

    • 在服务器正常关闭之前,将session对象系列化到硬盘上
  • session的活化反序列化

    • 在服务器启动后,将session文件转化为内存中的session对象即可
  1. 找到本项目的out包下的文件

    • 在这里插入图片描述
  2. 解压并修改后缀名为.war

    • 在这里插入图片描述
  3. 将war包复制到tomcat下的webapps下

    • 在这里插入图片描述
  4. 关闭之前的服务器

    • 在这里插入图片描述
  5. 手动开启tomcat下的启动服务器文件

    • 在这里插入图片描述
  6. 打开浏览器,访问session文件

session什么时候被销毁

  1. 服务器关闭

  2. session对象调用

    • 方法说明
      invalidate()自销毁
  3. session默认失效时间

    • 30分钟,选择性配置修改
    • 在这里插入图片描述

8.4、session的特点

  • session用于存储一次会话的多次请求的数据
    • 存在服务器端
  • session可以存储任意类型,任意大小的类型

session与Cookie的区别

  1. session存储数据在服务器端,Cookie在客户端
  2. session没有数据大小限制,cookie有
  3. session数据安全,cookie相对于不安全

案例:验证码

案例需求:

  1. 访问带有验证码的登陆页面Login.jsp
  2. 用户输入用户名,密码以及验证码
    • 如果用户名和密码输入错误,跳转登陆页面,提示:用户名或密码错误
    • 如果验证码输入有误,跳转登陆页面,提示:验证码输入错误
    • 如果全部输入正确,则跳转到主页success.jsp,显示:用户名:欢迎您

分析

在这里插入图片描述

代码

在这里插入图片描述

LoginServlet
package sessionServlet.Servlet;
/**
 * @author 玫瑰到了花期
 * @data 2022/4/1 16:17
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import sessionServlet.UserDao.Dao.UserDao;
import sessionServlet.UserDao.Dao.UserDaoImpl;
import sessionServlet.UserDao.User;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        UserDao userDao = new UserDaoImpl();
        boolean isCo = false;
        //从浏览器获取参数
        String userName = request.getParameter("userName");
        String password = request.getParameter("password");
        String verification = request.getParameter("verification");
        //获取验证码
        HttpSession session = request.getSession();
        String yzm = (String) session.getAttribute("yzm");
        //删除验证码,防止后退后验证码被复用
        session.removeAttribute("yzm");
        System.out.println(yzm);
        System.out.println(verification);
        //判断验证码是否正确
        User login = userDao.login(new User(userName, password));
        if (yzm!=null&&yzm.equalsIgnoreCase(verification)){
            if (login!=null){
                //登陆成功
                //储存信息,用户信息
                session.setAttribute("userName",userName);
                //重定向到success.jsp
                response.sendRedirect(request.getContextPath()+"success.jsp");
            }else {
                //登陆失败
                //储存信息到request
                request.setAttribute("login_error","用户名或密码错误");
                //转发到登陆页面
                request.getRequestDispatcher("index.jsp").forward(request,response);
            }
        }else {
            //验证码不一致
            //储存信息到request
            request.setAttribute("cc_error","验证码错误");
            //转发到登陆页面
            request.getRequestDispatcher("index.jsp").forward(request,response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
verifyServlet
package sessionServlet.Servlet;
/**
 * @author 玫瑰到了花期
 * @data 2022/4/1 14:22
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/verifyServlet")
public class verifyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //验证码制作
        int width = 100;
        int height=50;
        BufferedImage bImg = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR);
        Graphics g = bImg.getGraphics();
        Graphics g1 = bImg.getGraphics();
        g.setColor(Color.pink);
        //设置背景颜色
        g.fillRect(0,0,width,height);
        g.setColor(Color.black);
        //设置边框线
        g.drawRect(0,0,width-1,height-1);
        //生成验证码
        String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        String str_verification = "";
        for (int i = 0; i < 4; i++) {
            Random r = new Random();
            int index = r.nextInt(str.length());
            char c = str.charAt(index);
            //储存验证码
            str_verification += c;
            //写入验证码
            g.drawString(String.valueOf(c),width/4*i,25);
            //干扰线
            g1.setColor(Color.GREEN);
            int x1 = r.nextInt(width);
            int x2 = r.nextInt(width);
            int y1 = r.nextInt(height);
            int y2 = r.nextInt(height);
            g1.drawLine(x1,x2,y1,y2);
        }
        //验证码图片输出至浏览器
        ImageIO.write(bImg,"jpg",response.getOutputStream());
        System.out.println("验证码:"+str_verification);
        HttpSession session = request.getSession();
        session.setAttribute("yzm",str_verification);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
index.jsp
<%@ page import="java.awt.*" %>
<%@ page import="java.util.Random" %>
<%@ page import="javax.imageio.ImageIO" %>
<%@ page import="java.awt.image.BufferedImage" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2022/4/1
  Time: 14:21
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <style>
        span {
            color: red;
        }
    </style>
</head>

<body>
<script type="text/javascript">
    window.onload = function () {
        document.getElementById("img").src = "/verifyServlet";
        document.getElementById("img").onclick = function () {
            this.src = "/verifyServlet?time=" + new Date().getTime();
            document.getElementsByName("verification")[0].value = "";
        }
    }
</script>
<form action="/LoginServlet" method="post">
    用户名:<input type="text" placeholder="输入用户名" name="userName"><span><%=request.getAttribute("login_error")==null? "":request.getAttribute("login_error")%></span><br>
    密码:<input type="password" placeholder="输入密码" name="password"><span><%=request.getAttribute("login_error")==null? "":request.getAttribute("login_error")%></span><br>
    验证码:<input type="text" placeholder="输入验证码" name="verification"><span><%=request.getAttribute("cc_error")==null? "":request.getAttribute("cc_error")%></span><br>
    <img src="" alt="" id="img" οnclick="">
    <input type="submit" value="登陆">
</form>
</body>
</html>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
seccexx.jsp
<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2022/4/1
  Time: 17:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1><%=request.getSession().getAttribute("userName")%>,登陆成功</h1>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

JSP其余内容

9.1、指令

  • 作用:用于配置JSP页面,导入资源文件

  • 方法说明
    <%@指令名称 属性名1=属性值1 …%>指令基本定义格式

page – 配置JSP页面的

方法说明
contentType等同于response.setContentType()
1设置响应体的mime类型以及字符集
2设置当前jsp页面的编码(高级IDE才能生效如果使用低级工具需要设置pageEncoding属性设置当前页面的字符集编码)
import导包
errorPage当前页面发生异常后,会自动跳转到指定的错误页面
isErrorPage标识当前页面是否是错误页面
true:是,可以使用内置对象exception
false:否,默认值,不可以使用内置对象exception
isErrorPage
<%@ page contentType="text/html;charset=UTF-8" errorPage="500.jsp" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
      <%
        int i = 3/0;
      %>
  </body>
</html>
||||||||||||||||||||||||||||||||||||||||||
<%@ page contentType="text/html;charset=UTF-8" isErrorPage="true" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>服务器正忙....</h1>
    <%
        System.out.println(exception.getMessage());
    %>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在这里插入图片描述

include – 页面包含的,导入页面的资源文件

方法说明
file导入页面

taglib – 导入资源

导入依赖
<!--jstl依赖-->
<dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.servlet.jsp</groupId>
                    <artifactId>jsp-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/taglibs/standard -->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
方法说明
prefix前缀:自定义的
uri对应标签名,资源路径
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  • 1

9.2、注释

html注释

<!-- -->:只能注释html代码片段
  • 1

jsp注释

<%-- --%>:可以注释所有
  • 1

9.2、内置对象

  • 在jsp页面中不需要创建,直接使用的对象

  • 一共有9个‘

    • 变量名真实类型作用
      域对象
      pageContextPageContext当前页面共享数据,还可以获取其他八个内置对象
      requestHttpServletRequest一次请求访问的多个资源(转发)
      sessionHttpSession一次会话的多个请求间
      applicationServletContext所有用户间共享数据
      响应对象
      responseHttpServletResponse响应对象
      pageObject当前页面(Servlet)的对象 this
      outJspWriter输出对象,数据输出到页面上
      配置对象
      configServletConfigServlet的配置对象
      exceptionThrowable异常对象

MVC开发模式

jsp演变历史

  • 早期只有servlet,只能使用response输出标签数据,非常麻烦
  • 后来有了jsp,简化了Servlet的开发
  • 如果过度使用jsp,在jsp中即写大量的java代码,又写html标签,会造成难于维护难于分工协作
  • 再后来,java的web开发,借鉴Mvc模式,使得程序的设计更加合理性

MVC

  • M:Mdel,模型 – javaBean

    • 完成具体的业务操作,如查询数据库,封装对象
  • V:View,视图 – JSP

    • 展示数据
  • C:Controller:控制器 – Servlet

    • 获取用户的输入
    • 调用模型
    • 将数据交给视图进行展示

在这里插入图片描述

优点

  • 耦合性低,方便维护,利于分工协作
  • 重用性高

缺点

  • 使得项目架构变得复杂,对开发人员要求高

EL表达式

概念

  • Expression Language表达式语言
  • 替换和简化jsp页面中java代码的编写

使用

jsp中是默认支持el表达式的

  • 语法
    ${表达式}
    防止jsp默认解析el表达式
    配置jsp中page指令:isELIgnored=“true”忽略所有的el表达式
    ${表达式}忽略单个el表达式
    替换和简化jsp页面中Java代码的编写
运算
运算符
+ - * /(div) %(mod)算术运算符
> < >= <= == !=比较运算符
&&(and) ||(or) !(not)逻辑运算符
empty空运算符
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${1>3}
    \${1>3}
    <hr>
<h3>算术运算符</h3>
${3+4}<br>
${3/4}<br>
${3 div 4}<br>
${3%4}<br>
${3 mod 4}<br>
    <hr>
<h3>比较运算符</h3>
${1==1}
${1==2}
    <hr>
<h3>逻辑运算符</h3>
${1>2 && 1<2}
${1>2 and 1<2}
${1>2 || 1<2}
${1>2 or 1<2}
${!(1>2 and 1<2)}
${not(1>2 and 1<2)}
<h3>空运算符</h3>
<p>判断字符串、集合、对象、数组是否为Null并且长度为0</p>
${empty list}
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
获取值
  • el表达式只能从域对象中获取值
语法
方式一:
${域名城.键名}从指定域中获取指定键的值
域名城pageScope – > pageContext
requestScope --> request
sessionScope --> session
applicationScope --> application(ServletContext)
方式二:
${键名}表示依次从最小的域中查找是否有该键对应的值,直到找到为止

方式一 举例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>el获取域中的数据</title>
</head>
<body>
<%
     request.setAttribute("name","胡子洋");
     session.setAttribute("age","18");
%>
${requestScope.name}
${sessionScope.age}
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

方式二 举例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>el获取域中的数据</title>
</head>
<body>
<%
     request.setAttribute("name","胡子洋");
     session.setAttribute("name","乌鸦做胃镜");
%>
${name}  <!--胡子洋-->
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
获取对象、LIst集合、Map集合的值
对象
语法
${域名城.键名.属性名}本质上会去调用对象的getter方法
package ytzl;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 玫瑰到了花期
 * @data 2022/4/4 13:48
 * @love 又偷偷看你了
 * @V: CBWR-K
 */
public class User {
    private String name;
    private int age;
    private Date time;

    /**
    * 逻辑视图
    * @return
    * */
    public String getBitStr(){
        if (time!=null){
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return simpleDateFormat.format(time);
        }else {
            return "";
        }
    }

    public User() {
    }

    public User(String name, int age, Date time) {
        this.name = name;
        this.age = age;
        this.time = time;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date time) {
        this.time = time;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
<%@ page import="java.util.Date" %>
<%@ page import="ytzl.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>el获取对象数据</title>
</head>
<body>
    <%
        User user = new User();
        user.setName("周杰伦");
        user.setAge(18);
        user.setTime(new Date());

        request.setAttribute("u",user);
    %>
    <h3>获取对象中的值</h3>
${requestScope.u}
<%--
    通过的是对象的属性来获取
        *setter getter方法,去掉set或get然后再将剩余部分,首字母变小写
        *setName -->Name -->name
--%>
${requestScope.u.name}<br>
${u.age}<br>
${u.time}<br>
${u.time.month+1}<br>
${u.bitStr}
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
集合
语法
list
${域名城.键名[索引]}
map
${域名城.键名.key名称}
${域名城.键名[“key名称”]}
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="ytzl.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>el获取集合</title>
</head>
<body>
    <%
        User user = new User();
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(user);
        user.setName("周杰伦");
        request.setAttribute("list",list);
    %>
    <h3>获取list集合</h3>
    ${list}<br>
    ${list[0]}<br>
    ${list[1]}<br>
    ${list[2].name}
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
空运算符
  • 用于判断字符串、集合、数据对象是否为Null或者长度是否为0
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>空运算符</title>
</head>
<body>
    <%
        String str = "123456";
        String str1 = "";
        request.setAttribute("str",str);
        request.setAttribute("str1",str1);
        List list = new ArrayList();
        request.setAttribute("list",list);
    %>
<%--${empty str} 判断字符串、集合、数组对象是否为null,或者长度为0--%>
    ${empty str}
<%--${not empty str1} 判断字符串、集合、数组对象是否不为null,并且长度>0--%>
    ${not empty str1}
    ${not empty list}
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
隐式对象
  • el表达式中有11个隐式对象

  • 对象说明
    pageContext获取jsp其他8个内置对象
    ${pageContext.request.contextPath}动态获取虚拟目录

JSTL标签

概念

  • JavaServer Pages Tag Library JSP标准标签库

    • 是由Apache组织提供的开源的免费的jsp标签
  • 用于简化和替换jsp页面的java代码

使用

  • 导入jstl相关jar包
  • 引入标签库:taglib指令:<%@ taglib%>\
  • 使用标签

常用的JSTL标签

标签说明
if相当于java代码的if语句
test必须属性,接收boolean表达式,如果为true则显示标签体内容
<c:if test=“${not empty list}”>
c:if标签没有else,想要else情况,则可以再定义一个c:if标签
choose相当于java代码的switch语句
when相当于case,必须有test属性
otherwise相当于default
foreach相当于java代码的for语句
普通for循环
begin开始值
end结束值
var临时变量
step自增控制
varStatus循环状态对象
index: 当前下标,从0开始
count: 循环次数,从1开始
<c:forEach begin=“0” end=“9” var=“i” step=“1” varStatus=“s”>
遍历容器
items容器对象
var容器中元素的临时变量
varStatus循环状态对象
index: 当前下标,从0开始
count: 循环次数,从1开始
<c:forEach items=“${list}” var=“number” varStatus=“s”>
if
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
    <title>if</title>
</head>
<body>
<%--
    c:if标签
        1.属性:
            test 必须属性,接受boolean表达式
                如果为true则显示标签体内容
                如果为false则不显示标签体内容
                一般情况下,test属性值会结合el表达式一起使用
        2.注意:
            c:if标签没有else,想要else情况,则可以再定义一个c:if标签
--%>
<%--判断一个集合是否为空,不为空的话输出--%>
<%
    List list = new ArrayList();
    list.add("1");
    list.add("2");
    list.add("3");
    request.setAttribute("list",list);
    request.setAttribute("number",3);
%>

    <c:if test="${not empty list}">
        <h1>遍历集合</h1>
    </c:if>
<br>
    <c:if test="${number/2!=3}">
        <h1>number/2!=3</h1>
    </c:if>
<br>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
choose
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
    <title>choose</title>
</head>
<body>
<%
    request.setAttribute("number",2);
%>
    <c:choose>
        <c:when test="${number==1}">星期一</c:when>
        <c:when test="${number==2}">星期二</c:when>
        <c:when test="${number==3}">星期三</c:when>
        <c:otherwise>我是傻逼</c:otherwise>
    </c:choose>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
foreach
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
    <title>foreach</title>
</head>
<body>
<%
    List list = new ArrayList();
    list.add("a");
    list.add("b");
    list.add("c");
    request.setAttribute("list",list);
%>
    <c:forEach begin="0" end="9" var="i" step="1" varStatus="s">
        <h${i+1}>周杰伦</h${i+1}>
        <h3>${s.index}<h3>
            <h4>${s.count}<h4>
    </c:forEach>

    <c:forEach items="${list}" var="number" varStatus="s">
        ${s.index} ${s.count} ${number}
    </c:forEach>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

练习

  • 在request与中有一个User对象的List集合。
  • 需要使用jstl+el将list集合数据展示到jsp页面的表格table中。
package ytzl;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 玫瑰到了花期
 * @data 2022/4/4 13:48
 * @love 又偷偷看你了
 * @V: CBWR-K
 */
public class User {
    private String name;
    private int age;
    private Date time;

    /**
    * 逻辑视图
    * @return
    * */
    public String getBitStr(){
        if (time!=null){
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return simpleDateFormat.format(time);
        }else {
            return "";
        }
    }

    public User() {
    }

    public User(String name, int age, Date time) {
        this.name = name;
        this.age = age;
        this.time = time;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date time) {
        this.time = time;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Date" %>
<%@ page import="ytzl.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
<head>
    <title>table表格</title>
</head>
<body>
<%
    List list = new ArrayList();
    list.add(new User("周杰伦",18,new Date()));
    list.add(new User("胡子洋",19,new Date()));
    list.add(new User("葛霄",99,new Date()));
    request.setAttribute("list",list);
%>
    <table width="500px" height="500px" cellpadding="0px" cellspacing="0px" border="1px solid black">
        <tr>
            <td>编号</td>
            <td>姓名</td>
            <td>年龄</td>
            <td>创建时间</td>
        </tr>
        <c:forEach items="${list}" var="user" varStatus="s">
            <tr>
                <td>${s.count}</td>
                <td>${user.name}</td>
                <td>${user.age}</td>
                <td>${user.bitStr}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

三层架构

  • 软件设计架构
  • 界面层(表示层)
    • 用户看的界面,用户可以通过界面上的组件和服务器进行交互
  • 业务逻辑层
    • 处理业务逻辑的。
  • 数据访问层
    • 操作数据存储文件。

在这里插入图片描述

案例

需求

  • 用户信息的增删改查操作

设计

  • 技术选型

    • Servlet+Jsp+Mysql+JDBC+BeanUtils+tomcat
  • 数据库设计

    • 在这里插入图片描述
  • 开发

    • 环境搭建
      • 创建数据库环境
      • 创建项目,导入需要的jar包或xml依赖
    • 编码
  • 测试

  • 部署运维

任务视图

查询

在这里插入图片描述

添加

在这里插入图片描述

修改

在这里插入图片描述

Filter过滤器

方法

方法说明
chain.doFilter(request, response);过滤器放行

阐述

  • 生活中的过滤器
    • 净水器
    • 空气净化器
    • 土匪
  • web中的过滤器.
    • 当访问服务器的资源时,过滤器可以将请求拦截下来,完成一些特殊的功能
  • 过滤器的作用
    • 一般用于玩完成通过的操作
    • 如:登录验证、统一编码处理、敏感字符过滤…

使用

  1. 定义一个类,实现接口Filter

  2. 复写方法

  3. 配置拦截路径

web.xml配置

<filter>
    <filter-name>demo1</filter-name>
    <filter-class>com.ytzl.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
    <filter-name>demo1</filter-name>
    /*拦截路径*/
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注解 @WebFilter(“/*”)

  • package com.ytzl.filter;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    /**
     * @author 玫瑰到了花期
     * @data 2022/4/14 11:28
     * @love 又偷偷看你了
     * @V: CBWR-K
     */
    @WebFilter("/*")
    public class FilterDemo1 implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("doFilter......");
            //是否放行
            //放行
            chain.doFilter(request,response);
        }
        @Override
        public void destroy() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

过滤器执行流程

在这里插入图片描述

过滤器生命周期

在这里插入图片描述

过滤器配置详解

拦截路径配置
路径作用
具体资源路径/index.jsp只有访问index.jsp资源时,过滤器会被执行
拦截目录/user/*访问/user下的所有资源时,过滤器会被执行
后缀名拦截*.jsp访问所有后缀名为jsp资源时,过滤器会被执行
拦截所有资源/*访问所有资源时,过滤器会被执行
拦截方式配置:资源被访问的方式
注解配置说明
设置dispatcherTypes属性
REQUEST(request)默认值。浏览器直接请求资源
FORWARD(forward)转发访问资源
INCLUDE(include)包含访问资源
ERROR(error)错误跳转资源
ASYNC(async)异步访问资源

在这里插入图片描述

package com.ytzl.filter;
/**
 * @author 玫瑰到了花期
 * @data 2022/4/16 17:51
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.IOException;

/**浏览器直接请求资源时,该过滤器会被执行转发不会被执行*/
//@WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
/**只有转发时才会被执行*/
//@WebFilter(value = "/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
/**既可以转发访问,也可以直接请求*/
@WebFilter(value = "/index.jsp",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class FilterDemo5 implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        //放行
        chain.doFilter(request, response);
        System.out.println("FilterDemo5....");
    }

    @Override
    public void init(FilterConfig config) throws ServletException {

    }

    @Override
    public void destroy() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

WEB.XML配置

配置说明
设置标签即可
REQUEST(request)默认值。浏览器直接请求资源
FORWARD(forward)转发访问资源
INCLUDE(include)包含访问资源
ERROR(error)错误跳转资源
ASYNC(async)异步访问资源

过滤器链(配置多个过滤器)

  • 执行顺序:如果有两个过滤器:过滤器1、过滤器2
    1. 过滤器1
    2. 过滤器2
    3. 资源执行
    4. 过滤器2
    5. 过滤器1
  • 过滤器先后顺序问题
    • 注解配置:按照类名的字符串比较规则比较,值小的先执行
      • 如:AFilter BFilter,AFilter小、先执行
    • web.xml配置
      • 谁定义在上边,谁先执行

案例–登陆验证

需求

  1. 访问day17_case案例的资源,验证其是否登录
  2. 如果登录了,则直接放行
  3. 如果没登录,则跳转登陆页面,提示“您尚未登陆,请先登录”

在这里插入图片描述

代码

package itcast.Service;
/**
 * @author 玫瑰到了花期
 * @data 2022/4/18 17:19
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        //0.强制转换
        HttpServletRequest request1 = (HttpServletRequest) request;
        //1.获取资源请求的路径
        String uri = request1.getRequestURI();
        //2.判断是否包含登陆相关路径,要注,排除掉 css/js/图片/验证码等资源
        if (uri.contains("/login.jsp")||uri.contains("/LoginServlet")||uri.contains("/css/")||uri.contains("/js/")||uri.contains("/fonts/")||uri.contains("/VerificationCodeServlet")){
            //包含,用户就是想登陆。放行
            chain.doFilter(request, response);
        }else {
            //不包含,需要验证用户是否登陆
            //3.从session中获取user
            Object user = request1.getSession().getAttribute("user");
            if (user!=null){
                //登陆了,放行
                chain.doFilter(request, response);
            }else {
                //没有登陆,跳转登陆页面
                request1.setAttribute("login_msg","您尚未登录,请登录");
                request1.getRequestDispatcher("/login.jsp").forward(request1,response);
            }

        }



    }

    @Override
    public void init(FilterConfig config) throws ServletException {

    }

    @Override
    public void destroy() {

    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

案例–敏感词汇过滤

需求

  1. 对day17_case案例录入的数据进行敏感词汇过滤
  2. 敏感词汇:笨蛋,傻瓜
  3. 如果是敏感词汇,替换为“***”

在这里插入图片描述

分析

  1. 对request对象进行增强。增强获取参数相关方法
  2. 放行。传递代理对象

增强对象的功能

  • 设计模式:一些通用的解决固定问题的方式
  • 装饰模式:
  • 代理模式:
    • 概念
      • 真实对象:被代理的对象
      • 代理对象
      • 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
    • 动态代理
      • 静态代理:在一个类文件描述代理模式
      • 动态代理: 在内存中形成代理类
        • 实现步骤:
          1. 代理对象和真实对象实现相同的接口
          2. 代理对象 = Proxy.newProxyInstance()
          3. 使用代理对象调用方法
          4. 增强方法
        • 增强方式
          1. 增强参数列表
          2. 增强返回值类型
          3. 增强方法体执行逻辑
package itcast.Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author 玫瑰到了花期
 * @data 2022/4/18 17:58
 * @love 又偷偷看你了
 * @V: CBWR-K
 */
public class ProxyTest {
    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        //2.动态代理,增强lenovo对象
        /**
         * 三个参数
         *      1.类加载器 lenovo.getClass().getClassLoader()
         *      2.接口数组 lenovo.getClass().getInterfaces()
         *      3.处理器   new InvocationHandler()
         * */
        SaleComputer proxy_Lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 代理逻辑编写的方法,代理对象调用的所有方法都会触发该方法执行
             *      参数:
             *          1、proxy:代理对象
             *          2、method:代理对象调用的方法,被封装为的对象
             *          3、args:代理对象调用方法时,传递的实际参数
             * */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*                System.out.println("该方法执行了....");
                System.out.println(method.getName());
                System.out.println(args[0]);*/

                //判断是否是sale方法
                if (method.getName().equals("sale")){
                    //1.增强参数
                    double money = (double) args[0];
                    money = money * 0.85;
                    System.out.println("专车接你...");
                    Object obj = method.invoke(lenovo, money);
                    System.out.println("免费送货...");
                    //2.增强返回值
                    return obj+"_鼠标垫";
                }else {
                    Object obj = method.invoke(lenovo, args);
                    return obj;
                }
            }
        });
        //2.调用方法
        String computer = proxy_Lenovo.sale(8000);
        System.out.println(computer);
        proxy_Lenovo.show();

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

代码

package itcast.Service;
/**
 * @author 玫瑰到了花期
 * @data 2022/4/18 18:22
 * @love 又偷偷看你了
 * @V: CBWR-K
 */

import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=UTF-8");
        //1.创建代理对象,增强getParameter方法
        ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强getParameter方法
                //判断是否是getParameter方法
                if (method.getName().equals("getParameter")) {
                    //增强返回值
                    //获取返回值
                    String value = (String) method.invoke(request, args);
                    if (value != null) {
                        for (String str : list) {
                            if (value.contains(str)) {
                                System.out.println("str==>" + str);
                                value = value.replaceAll(str, "笨蛋");
                            }
                        }
                    }
                    request.getAttribute("");
                    return value;
                }
                return method.invoke(request, args);
            }
        });
        //2放行
        chain.doFilter(proxy_req, response);
    }

    private List<String> list = new ArrayList<String>();//敏感词汇集合

    @Override
    public void init(FilterConfig config) throws ServletException {
        try {
            //1.获取文件的真实路径
            ServletContext servletContext = config.getServletContext();
            String realPath = servletContext.getRealPath("/WEB-INF/敏感词汇.txt");
            System.out.println("realPath==>" + realPath);
            //2.读取文件
            BufferedReader br = new BufferedReader(new InputStreamReader(servletContext.getResourceAsStream("/WEB-INF/敏感词汇.txt"), "utf-8"));
            //BufferedReader br = new BufferedReader(new FileReader(realPath));
            //3.将文件的每一行数据设置添加到list中
            String line = null;
            while ((line = br.readLine()) != null) {
                list.add(line);
            }
            System.out.println("list.toString()==>" + list.toString());
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void destroy() {

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

Listener监听器

事件监听机制

事件监听机制说明
事件一件事情
事件源事件发生的地方
监听器一个对象
注册监听将事件、事件源、监听器绑定在一起。
当事件源上发生某个事件后,执行监听器代码
ServletContextListenter监听ServletContext对象的创建和销毁
void contextDestroyed(ServletContextEvent sce)ServletContext对象被销毁之前会调用该方法
void contextInitialized(ServletContextEvent sce)ServletContext对象创建后会调用该方法

使用

  1. 定义一个类,实现ServletContextListener接口

  2. 复写方法

  3. 配置

    • web.xml

      • <!--    配置监听器-->
            <listener>
                <listener-class>com.ytzl.Listener.ListenerDemo1</listener-class>
            </listener>
        
        • 1
        • 2
        • 3
        • 4
    • 注解

      • @WebFilter
        
        • 1

依赖导入

<dependencies>
     <!-- jsp -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
 		<!-- jsp -->
    
    <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>jstl-api</artifactId>
        <version>1.2</version>
        <exclusions>
            <exclusion>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- https://mvnrepository.com/artifact/taglibs/standard -->
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
    </dependency>
</dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号