JavaWeb 基础

作者: yjtuuige | 来源:发表于2022-02-26 22:10 被阅读0次

    一、基本概念

    1.1 概述

    • Web 开发:
      • web,网页的意思,如:www.baidu.com
    • 静态 Web:
      • html,css;
      • 数据始终不会发生变化;
    • 动态 Web:
      • 数据始终会发生变化,每个访问者,在不同的时间,不同的地点看到的信息各不相同;
      • 技术栈:Servlet/JSP,ASP,PHP;
    • Java 中,动态 web 资源,开发的技术统称为 JavaWeb。

    1.2 Web 应用程序

    • Web 应用程序:可以提供浏览器访问的程序;
      • a.html、b.html......这些 Web 资源,可以被外界访问,对外界提供服务;
      • 这些 Web 资源,都存在于这个世界的,某一个角落的计算机上;
      • URL;
      • Web 资源,会被放在同一个文件夹下:
        • Web 应用程序 -->Tomcat:服务器;
      • Web应用组成:(静态 Web,动态 Web)
        • html,css,js;
        • jsp,servlet;
        • Java 程序;
        • jar 包;
        • 配置文件 (Properties);
    • Web 应用程序编写完毕后,若想提供给外界访问,需要一个服务器,来统一管理。

    1.3 静态 Web

    • *.htm*.html 都是网页后缀,如果服务器上,一直存在这些资源,可以通过网络,直接进行读取;

    • 静态 Web 的缺点:

      • Web 页面无法动态更新,所有用户看到都是同一个页面;
        • 轮播图,点击特效:伪动态;
        • JavaScript(实际开发中,用的最多);
        • VBScript;
      • 无法和数据库交互(数据无法持久化,用户无法交互)。

    1.4 动态 Web

    • 页面动态展示:Web 的页面展示,内容因人而异;

    • 动态 Web 的缺点:

      • 加入服务器的动态 Web 资源,出现了错误,需要重新编写后台程序,重新发布;
      • 停机维护;
    • 优点:

      • 动态更新,所有用户看到都不是同一个页面;
      • 可以与数据库交互(数据持久化:注册、商品信息、用户信息...);

    二、Web 服务器

    2.1 实现方式

    1. ASP
    • 微软:国内最早流行的就是 ASP;
    • 在 HTML 中嵌入了 VB 的脚本,ASP + COM
    • 在 ASP 开发中,业务代码繁多,页面混乱;
    • 维护成本高;
    • IIS;
    1. php
    • PHP 开发速度快,功能强大,跨平台,代码简单(70% , WP);
    • 无法承载大的访问量(局限性);
    1. JSP/Servlet
    • B/S:浏览器和服务器;
    • C/S:客户端和服务器:
      • Sun 公司主推的 B/S 架构;
      • 基于 Java 语言;
      • 可以承载 高并发、高可用、高性能 带来的影响;
      • 语法类似 ASP;

    2.2 Web 应用服务器

    • 服务器是被动的操作,用来处理用户的请求,给用户响应信息;
    • Web 应用服务器:
      • IIS:ASP,Windows 自带;
      • Tomcat:
        • Apache 下 Jakarta 中的核心项目;
        • 最新的 Servlet 和 JSP 规范,能在 Tomcat 中得到体现;
        • 技术先进、性能稳定、免费,目前比较流行的 Web 应用服务器;
        • 轻量级应用服务器,在中小型系统和并发访问,用户不是很多的场合下使用;
        • 开发和调试 JSP 程序的首选;

    三、Tomcat

    四、HTTP

    4.1 HTTP 概述

    • Http(超文本传输协议):简单的 请求-响应 协议,运行在 TCP 之上;
      • 文本:html、字符串…. ;
      • 超文本:图片、音乐、视频、定位、地图…;
      • 端口:80;
    • Https:安全的;
      • 端口:443;

    4.2 两个时代

    • http1.0:
      • HTTP/1.0:客户端可以与 web 服务器连接后,只能获得一个 web 资源,就断开连接;
    • http2.0:
      • HTTP/1.1:客户端可以与 web 服务器连接后,可以获得多个 web 资源;

    4.3 Http 请求

    • 客户端 --- > 发请求(Request)--- > 服务器;
    • 百度测试:
    Request URL:https://www.baidu.com/  # 请求地址
    Request Method:GET  # get方法/post方法
    Status Code:200 OK  # 状态码:200
    Remote Address:14.215.177.39:443    # 远程地址
    
    Accept:text/html
    Accept-Encoding:gzip, deflate, br
    Accept-Language:zh-CN,zh;q=0.9  # 语言
    Cache-Control:max-age=0
    Connection:keep-alive   # 保持连接
    
    1. 请求行
    • 请求行中的请求方式:GET;
    • 请求方式:Get、Post、HEAD、DELETE、PUT、TRACT…;
      • Get 请求:能够携带的参数比较少,大小有限制,会在浏览器的 URL 地址栏显示数据内容,不安全,但高效
      • Post 请求:能够携带的参数没有限制,大小没有限制,不会在浏览器的 URL 地址栏显示数据内容,安全,但不高效
    1. 消息头
    Accept: # 告诉浏览器,所支持的数据类型
    Accept-Encoding:    # 支持编码格式 GBK UTF-8 GB2312 ISO8859-1
    Accept-Language:    # 告诉浏览器,语言环境
    Cache-Control:  # 缓存控制
    Connection: # 告诉浏览器,请求完成是断开还是保持连接
    HOST:   # 主机..../.
    

    4.4 Http 响应

    • 服务器 --- > 响应 --- > 客户端
    • 百度测试:
    Cache-Control:private   # 缓存控制
    Connection:Keep-Alive   # 保持连接
    Content-Encoding:gzip   # 编码
    Content-Type:text/html  # 文件类型
    
    1. 响应体
    Accept: # 告诉浏览器,所支持的数据类型
    Accept-Encoding:    # 支持编码格式 GBK UTF-8 GB2312 ISO8859-1
    Accept-Language:    # 告诉浏览器,语言环境
    Cache-Control:  # 缓存控制
    Connection:# 告诉浏览器,请求完成是断开还是保持连接
    HOST:   # 主机..../.
    Refresh:    # 告诉客户端,多久刷新一次
    Location:# 网页重新定位
    
    1. 响应状态码
    • 200:请求响应成功 200;
    • 3xx:请求重定向:
      • 重定向:重新到给指定新位置去;
    • 4xx:找不到资源 404;
      • 资源不存在;
    • 5xx:服务器代码错误 500;
      • 502:网关错误;

    五、Maven

    六、Servlet

    6.1 Servlet 简介

    • Servlet 是 sun 公司,开发动态 Web 的一门技术;
    • Servlet 是一个 API 接口,开发 Servlet 程序,只需完成两个步骤:
      • 编写类,实现 Servlet 接口;
      • 把开发好的 Java 类,部署到 Web 服务器中;
    • 实现了 Servlet 接口的 Java 程序叫做,Servlet;
    • Serlvet 接口,有两个默认的实现类:HttpServletGenericServlet

    6.2 构建 ServLet

    1. 构建 Maven 项目
    • 构建一个普通的 Maven 项目,删掉里面的 src 目录,然后在这个项目里,建立 Moudel(模块),这个空的工程就是 Maven 主工程(父工程);

    • 关于 Maven 父子工程的概念:

      • 父项目 pom.xml 中,包含子项目(模块)的名称:

        <!--子项目名称-->
        <modules>
            <module>servlet-01</module>
        </modules>
        
      • 子项目 pom.xml 中,包含父项目的信息:

        <!--父项目名称及信息 Maven3.84 创建项目后,内容被自动删除-->
        <parent>
            <artifactId>javaweb-02-servlet</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        
      • 或在子项目依赖中,添加父项目的依赖:

        • 但父项目依赖添加 dependencyManagement 标签,子项目在显示引用时,会有问题;
        <dependency>
            <groupId>com.study</groupId>
            <artifactId>javaweb-02-servlet</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        
      • 子项目可以直接继承使用父项目内容(反之不行);

        子项目 extends 父项目
        
    1. Maven 环境配置及优化:
    • 父项目 pom.xml 中,添加 Servlet 依赖:

    • Tomcat10 是 jakarta.servlet 包,而不是 javax.servlet

      <!-- Tomcat10依赖:jakarta.servlet 5.0.0-->
      <dependency>
          <groupId>jakarta.servlet</groupId>
          <artifactId>jakarta.servlet-api</artifactId>
          <version>5.0.0</version>
      </dependency>
      
    • 修改 Web 项目中的版本号 ;

      • web.xml 版本,改为与 Tomcat 一致的;
      • 修改 JDK 版本;
      • 修改其它依赖的版本;
    • 将 Maven 的结构搭建完整(补全相关目录);

    1. 编写 Servlet 程序
    • 编写一个普通类:
    • 实现 Servlet 接口,直接继承 HttpServlet
    public class HelloServlet extends HttpServlet {
        // doGet,doPost只是请求实现的不同方式,逻辑一样,可相互调用
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ServletOutputStream outputStream = resp.getOutputStream();
            // 响应流
            PrintWriter writer = resp.getWriter();
            writer.println("Hello Servlet!");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • web.xml 中,编写 Servlet 映射:
    <!--注册Servlet-->
    <servlet>
        <!--名字自定义,但要与下面映射名字保持一致-->
        <servlet-name>hello</servlet-name>
        <!--对应的Servlet程序-->
        <servlet-class>com.study.servlet.HelloServlet</servlet-class>
    </servlet>
    
    <!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <!--路径名自定义,访问:localhost:8080/Tomcat路径映射/路径名-->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    • 注解方式,创建 Servlet 映射:建议使用注解的方式配置

      // 格式:@WebServlet(name = “userServlet”,urlPatterns ="/userServlet")
      
      // 注释方式1:
      @WebServlet("/test")
      // 注释方式2:
      // @WebServlet(name = "test",urlPatterns = {"/t1"})
      public class HelloServlet extends HttpServlet {
      }
      // 设置多个路径
      @WebServlet({"/test/edit.do", "/test/delete.do"})
      

    为什么需要映射:

    • Servlet 是 Java 程序,如通过浏览器访问,浏览器就需要连接 Web 服务器,所以需要在 Web 服务中,要注册 Servlet 程序,并给浏览器提供能够访问的路径;
    1. 配置 Tomcat 测试程序
    • 在 IDEA 中配置 Tomcat 服务;

    • 启动测试:

      • 注:访问地址为 localhost:8080/Tomcat路径映射/路径名

    6.3 Servlet 原理

    • Servlet 是由 Web 服务器调用,Web 服务器,收到浏览器请求的执行流程:

    6.4 映射路径 Mapping

    • 指定一个映射路径:
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    • 指定多个映射路径:多个路径都指向同一个 Servlet

      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/hello</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/hello1</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/hello2</url-pattern>
      </servlet-mapping>
      ...
      
    • 指定通用映射路径:* 匹配所有路径 ;

      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/hello/*</url-pattern>
      </servlet-mapping>
      
    • 默认请求路径:

      <!--默认请求路径-->
      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/*</url-pattern>
      </servlet-mapping>
      
    • 指定后缀或者前缀:

      <!--
        可以自定义后缀实现请求映射:
        注意点:* 前面不能加项目映射的路径(如 /*.do 或 hello/*.do)
      -->
      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>*.do</url-pattern>
      </servlet-mapping>
      
    • 优先级问题:

      • 指定了固有的映射路径,优先级最高,如果没有,就按默认的处理请求;
      <!--404页面:访问未指定的路径,都会指向404页面-->
      <servlet>
          <servlet-name>error</servlet-name>
          <servlet-class>com.study.servlet.Error</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>error</servlet-name>
          <url-pattern>/*</url-pattern>
      </servlet-mapping>
      

    6.5 ServletContext 上下文

    • Web 容器在启动时,会为每个Web 程序,都创建一个对应的 ServletContext 对象,它代表了当前的 Web 应用:

      • 一个 Web 应用,对应一个 ServletContext
    1. 共享数据
    • 在一个 Servlet 中保存的数据,可以在另外一个 Servlet 中拿到;
    • 创建存储数据的 Servlet:
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // this.getInitParameter(); // 初始化参数
            // this.getServletConfig(); // Servlet 配置
            // ServletContext:Servlet 上下文
            ServletContext context = this.getServletContext();
            String username = "测试";
            // 保存数据:格式  键,值
            context.setAttribute("username", username);        
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • 创建读取数据的 Servlet:
    public class GetServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 创建 ServletContext 对象
            ServletContext context = this.getServletContext();
            // 获取数据,转换成String类型
            String username = (String) context.getAttribute("username");
            // 设置编码格式
            resp.setContentType("text/html");
            resp.getWriter().println("名字:" + username);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • web.xml 中注册 Servlet:
    <servlet>
        <servlet-name>set</servlet-name>
        <servlet-class>com.study.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>set</servlet-name>
        <url-pattern>/set</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>get</servlet-name>
        <servlet-class>com.study.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>get</servlet-name>
        <url-pattern>/get</url-pattern>
    </servlet-mapping>
    
    1. 获取初始化参数
    • web.xml 配置信息:
    <!--配置Web应用初始化参数-->
    <context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
    </context-param>
    
    • 获取信息:
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        // 获取参数信息 字符串对应配置文件中的参数名
        String url = context.getInitParameter("url");
        resp.getWriter().println(url);
    }
    
    1. 请求转发
    • 请求转发,URL 地址不会发生改变;
    • 重定向,URL 地址会发生改变;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        // 转发的请求路径
        // RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
        // 调用forward实现请求转发;
        // requestDispatcher.forward(req,resp);
        // 字符串为需要转发的另一个Servlet程序路径
        context.getRequestDispatcher("/url").forward(req,resp);
    }
    
    1. 读取资源文件
    • 读取 Properties 配置文件:
      • java 目录下新建 .properties 文件;
      • resources 目录下新建 .properties 文件;
    • 项目打包时,类和资源文件,都被打包到了 classes 同一个路径下,这个路径称为 classpath
    • 用文件流,在指定的路径下读取即可;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 文件流读取资源文件,第一个路径前必须要有 /
        // getResourceAsStream 将资源转换为流
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/study/servlet/aa.properties");
        // 创建属性集合对象
        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");
        resp.setContentType("text/html");
        resp.getWriter().println(user + ":" + pwd);
    }
    
    • 注:项目打包时,资源文件有时导出失败,需要在 pom.xml 中做相应配置:
    <!--在build中配置resources,防止资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    

    6.6 HttpServletResponse 响应

    • Web 服务器接收到客户端的 http 请求,针对这个请求,分别创建:
      • HttpServletRequest 对象:获取客户端 请求 过来的参数;
      • HttpServletResponse 对象:给客户端 响应 信息;
    1. 简单分类
    • 负责向浏览器发送数据的方法:
    // 字节流
    ServletOutputStream getOutputStream() throws IOException;
    // 字符流 中文
    PrintWriter getWriter() throws IOException;
    
    • 负责向浏览器,发送响应头的方法:
    void setCharacterEncoding(String var1); // 编码
    void setContentLength(int var1);    // 字符长度
    void setContentLengthLong(long var1);
    void setContentType(String var1);   // 类型
    void setDateHeader(String var1, long var2);
    void addDateHeader(String var1, long var2);
    void setHeader(String var1, String var2);
    void addHeader(String var1, String var2);
    void setIntHeader(String var1, int var2);
    void addIntHeader(String var1, int var2);
    
    • 响应的状态码:
    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;    // 响应成功
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;  // 重定向相关
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404; // 找不到资源
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500; // 服务器错误
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;   // 网关错误
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
    
    • 常用方法
    方法名 说明
    response.sendRedirect(URL url) 重定向
    setContentType(String type) 设置输出内容(MIME)类型(text/html)
    setContentLength(int length) 设置响应报文的长度
    getWriter( ) 获取输出字符流
    addHeader( String name, String value) 添加指定的键值到响应头信息中
    containsHeader(String name) 判断响应的头部是否被设置
    encodeURL(String url) 编码指定的URL
    sendError(int sc) 使用指定状态码发送一个错误到客户端
    setHeader( String name, String value) 设置指定响应头的值
    setStatus(int sc) 给当前响应设置状态
    1. 下载文件
    • 向浏览器输出消息;
    • 下载文件实现过程:
      1. 获取下载文件的路径;
      2. 下载的文件名;
      3. 设置浏览器的下载支持;
      4. 获取下载文件的输入流;
      5. 创建缓冲区;
      6. 获取 OutputStream 对象;
      7. 将 FileOutputStream 流写入到 buffer 缓冲区;
      8. 使用 OutputStream,将缓冲区中的数据,输出到客户端。
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.获取下载文件的路径
        String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/images/01.png");
        System.out.println("下载文件的路径:" + realPath);
        // 2.下载的文件名
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        // 3.设置浏览器的下载支持(Content-Disposition),并将文件名转码,避免乱码
        resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 4.获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);
        // 5.创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];
        // 6.获取 OutputStream 对象
        ServletOutputStream out = resp.getOutputStream();
        // 7.将 FileOutputStream 流写入到 buffer 缓冲区,使用 OutputStream,将缓冲区中的数据,输出到客户端
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        // 8.关闭流
        in.close();
        out.close();
    }
    
    1. 验证码功能
    • 验证码实现:
      • 前端实现;
      • 后端实现:需要用到 Java 的图片类,生成一个图片;
    public class ImageServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 让浏览器10秒自动刷新一次;
            resp.setHeader("refresh", "10");
            // 在内存中创建一个图片
            BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
            // 得到图片     g 为笔
            Graphics2D g = (Graphics2D) image.getGraphics();
            // 设置图片的背景颜色
            g.setColor(Color.white);
            g.fillRect(0, 0, 80, 20);
            // 给图片写数据
            g.setColor(Color.BLUE);
            g.setFont(new Font(null, Font.BOLD, 20));
            g.drawString(makeNum(), 0, 20);
            // 告诉浏览器,这个请求用图片的方式打开
            resp.setContentType("image/png");
            // 网站存在缓存,不让浏览器缓存
            resp.setDateHeader("expires", -1);
            resp.setHeader("Cache-Control", "no-cache");
            resp.setHeader("Pragma", "no-cache");
            // 把图片写给浏览器
            ImageIO.write(image, "png", resp.getOutputStream());
        }
    
        // 生成随机数
        private String makeNum() {
            Random random = new Random();
            String num = random.nextInt(9999999) + "";
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 7 - num.length(); i++) {
                sb.append("0");
            }
            num = sb.toString() + num;
            return num;
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    1. 实现重定向
    • 重定向,URL 地址会发生改变:
    • Web 资源 B 收到客户端 A 请求后,B 通知 A 去访问另一个 Web 资源 C,这个过程叫重定向;

    • 常见场景:用户登录

      void sendRedirect(String var1) throws IOException;
      
    • 重定向路径

    • 测试:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
            实现过程:
            resp.setHeader("Location","/r/img");
            resp.setStatus(302);
         */
        //  ‘/’ 表示:http://localhost:8080
        resp.sendRedirect("/r/img");
    }
    
    • 重定向和转发
      • 相同点:
        • 都会实现页面跳转;
      • 不同点:
        • 请求转发,URL 地址不会发生改变;307
        • 重定向,URL 地址会发生改变;302
    1. 简单实现登录重定向
    • Tomcat 10 所需依赖:Tomcat 版本 10.0.16JDK 17

      <!--Servlet 依赖-->
      <dependency>
          <groupId>jakarta.servlet</groupId>
          <artifactId>jakarta.servlet-api</artifactId>
          <version>5.0.0</version>
      </dependency>
      <!--
          备用Servlet 依赖
          <dependency>
              <groupId>com.guicedee.services</groupId>
              <artifactId>jakarta.servlet-api</artifactId>
              <version>1.2.2.1-jre17</version>
          </dependency>
      -->
      
      <!--JSP 依赖-->
      <dependency>
          <groupId>com.guicedee.services</groupId>
          <artifactId>jakarta.servlet.jsp-api</artifactId>
          <version>62</version>
      </dependency>
      <!--jstl 表达式依赖-->
      <dependency>
          <groupId>org.glassfish.web</groupId>
          <artifactId>jakarta.servlet.jsp.jstl</artifactId>
          <version>2.0.0</version>
      </dependency>
      

      可能遇到的问题:

      • JSP 中 out 内置对象红色,输入代码无提示:

        • 缺少 jsp-api.jarservlet-api.jar
    • 解决方法:

      • 方法 1:更换依赖版本;
      • 方法 2:从 Tomcat 安装目录 lib 目录下,复制到工程库里;
      • 方法 3:在 IDEA 里直接添加
    • 添加对应版本:

    • 添加成功:

    • 登录页面:
    <div>
        <%--这里提交的路径,需要寻找到项目的路径--%>
        <%--${pageContext.request.contextPath}代表当前的项目--%>
        <form action="${pageContext.request.contextPath}/login" method="get">
            <p>
                <label for="username">用户名:</label>
                <input type="text" id="username" name="username">
            </p>
            <p>
                <label for="password">密码:</label>
                <input type="password" id="password" name="password">
            </p>
            <input type="submit">
        </form>
    </div>
    
    • servlet:
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理请求  getParameter:获取参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username + ":" + password);
        // 注意,路径问题(/r1虚拟映射地址),否则404;
        resp.sendRedirect("/r1/success.jsp");
    }
    
    • 注册 Servlet:
    <servlet>
        <servlet-name>request</servlet-name>
        <servlet-class>com.study.servlet.RequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>request</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
    
    • 跳转页面
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>success</title>
    </head>
    <body>
    <h1>登录成功</h1>
    </body>
    </html>
    

    6.7 HttpServletRequest 请求

    • HttpServletRequest 代表客户端的请求,用户通过 HTTP 协议访问服务器,请求中的所有信息,都会被封装到 HttpServletRequest,通过这个 HttpServletRequest 的方法,获得客户端的所有信息;
    • 常用方法:
    方法名 含义
    String getHeader(String name) 获取请求中的报头信息
    Enumeration getHeaderNames() 获取请求中所有报头名的集合
    String getContextPath() 获取请求上下文路径(webapp的)
    String getRequestURI() 获取请求中的 URI
    String getMethod( ) 获取 HTTP 请求方法,GET 还是 POST
    getRemoteAddr() 远程 IP,即客户端 IP
    • 获取客户端的参数:
    方法名 含义
    String getParameter(String name) 获取请求(表单)中参数名为 name 的参数值
    String[] getParameterValues(String name) 获取请求中(表单)中所有参数名为 name 的参数值的集合(数组)
    Enumeration getParameterNames( ) 获取请求中所有参数名的集合(枚举 不常用)
    • 其它方法:
    方法名 说明
    setCharacterEncoding("utf-8") 设置请求的编码方式
    getLocalAddr() 获取本地 IP,即服务器 IP
    getLocalName() 获取本地名称,即服务器名称
    getLocalPort() 获取本地端口号,即 Tomcat 端口号
    getLocale() 用户的语言环境
    getProtocol() 协议,http协议
    getQueryString() 查询字符串
    getRemotePort() 远程端口,即客户端端口
    getRemoteUser() 远程用户
    getRequestedSessionId() 客户端的 Session 的 ID
    getRequestURI() 用户请求的 URL
    getScheme() 协议头,例如 http
    getServerName() 服务器名称
    getServerPort() 服务器端口
    getServletPath() Servlet 路径

    HttpServletRequest 测试:获取表单信息

    • 登录页面
    <form action="${pageContext.request.contextPath}/login" method="post">
        <p>
            <label for="username">用户名:</label>
            <input type="text" id="username" name="username">
        </p>
        <p>
            <label for="password">密码:</label>
            <input type="password" id="password" name="password">
        </p>
        <p>
            <input type="checkbox" name="hobbies" value="运动">运动
            <input type="checkbox" name="hobbies" value="阅读">阅读
            <input type="checkbox" name="hobbies" value="购物">购物
            <input type="checkbox" name="hobbies" value="电影">电影
        </p>
        <input type="submit">
    </form>
    
    • servlet
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求的编码
        req.setCharacterEncoding("utf-8");
        // 设置响应的编码
        resp.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbies = req.getParameterValues("hobbies");
        // 后台接收中文乱码问题
        System.out.println("username:" + username);
        System.out.println("password:" + password);
        System.out.println("========================");
        System.out.println(Arrays.toString(hobbies));
        System.out.println("========================");
    
        System.out.println(req.getContextPath());
        // 通过请求转发
        // 这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req, resp);
    }
    
    • 注册 servlet
    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.study.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
    

    getRequestDispatcher 转发的两种方式:

    • request.getRequestDispatcher(“url”)

      • 可以是相对路径,或绝对路径;
    • this.getServletContext().getRequestDispatcher(“url”)

      • 只能是绝对路径
    • 获取前端传递的参数

      • getParameter():一个参数值;
      • getParameterValues():多个参数值,数组;

    七、Cookie、Session

    7.1 会话

    • 会话:用户打开浏览器,打开很多超链接,访问多个 Web 资源,关闭浏览器,这个过程可以称之为会话;
    • 有状态会话:用户访问服务器,下次再访问时,服务器知道这个用户访问过,即有状态会话;
    • 服务器如何知道客户端访问过:
      • 服务端给客户端一个信件,客户端下次访问服务端,带上信件就可以; cookie
      • 服务器登记客户端信息,下次客户端访问时,由服务器来匹配客户端; seesion

    7.2 保存会话的两种技术

    • cookie:客户端技术(响应,请求);
    • session:服务器技术,把信息或者数据,放在 Session 中;
    • 常见应用:网站登录后,下次不用再登录,可以直接访问;

    7.3 Cookie

    • 从请求中拿到 cookie 信息;

    • 服务器响应给客户端 cookie;

    • 测试:Cookie

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        // 获取响应流
        PrintWriter out = resp.getWriter();
        // 服务器从客户端请求中获取Cookie,返回数组(Cookie存在多个)
        Cookie[] cookies = req.getCookies();
        // 判断Cookie是否存在
        if (cookies != null) {
            // 如果Cookie存在
            out.write("上一次访问时间:");
            // 遍历 cookies
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                // 获取Cookie的名字
                if (cookie.getName().equals("lastLoginTime")) {
                    // 获取Cookie的值,并转化成长整型(时间戳)
                    Long lastLoginTime = Long.parseLong(cookie.getValue());
                    // 将时间戳转换为日期格式
                    Date date = new Date(lastLoginTime);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            out.write("第一次访问!");
        }
        // 服务器给客户端响应(发送)一个Cookie
        // System.currentTimeMillis()+"":获取时间,并转成字符串
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        // 设置Cookie有效期,1天
        cookie.setMaxAge(24 * 60 * 60);
        resp.addCookie(cookie);
    }
    
    • Cookie 常用方法:
    // 获得Cookie
    Cookie[] cookies = req.getCookies();
    // 获得cookie中的key
    cookie.getName();
    // 获得cookie中的vlaue
    cookie.getValue();
     // 新建一个cookie(设置新的cookie)
    new Cookie("lastLoginTime", System.currentTimeMillis()+"");
    // 设置cookie的有效期1天
    cookie.setMaxAge(24*60*60);
    // 响应(发送)给客户端一个cookie
    resp.addCookie(cookie);
    
    • cookie:一般会保存在本地的用户目录下 appdata;
    • cookie 存储上限:
      • 一个 Cookie 只能保存一个信息;
      • 一个 Web 站点,可以给浏览器发送多个 cookie(最多20个);
      • Cookie 大小有限制 4kb;
      • 浏览器上限:300 个 Cookie;
    • 删除 Cookie:
      • 不设置有效期,关闭浏览器,自动失效;
      • 设置有效期时间为 0 ;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建另一个Cookie,名字与之前的必须相同(键不能重复)
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        // 设置Cookie有效期为0
        cookie.setMaxAge(0);
        resp.addCookie(cookie);
    }
    
    • Cookie 传递中文数据时乱码,解决方法:编码、解码
    // 对中文Value值编码
    URLEncoder.encode("中文的Cookie数据","utf-8")
    // 解码
    URLDecoder.decode(cookie.getValue(),"UTF-8")
    

    7.4 Session(重点)

    • 什么是 Session:
      • 服务器会给每一个用户(浏览器),创建一个 Seesion 对象;
      • 一个 Seesion 独占一个浏览器,只要浏览器没有关闭,这个 Session 就存在;
      • 用户登录之后,整个网站都可以访问;
    • Session 常用方法:
    方法名 说明
    getCreationTime():long 返回 Session 的创建时间
    getId():String 返回 Session 的唯一 ID
    getLastAccessedTime():long 返回 Session 的最后活跃时间
    getServletContext():ServletContext 获取 Servlet 上下文(Web 服务)
    getMaxInactiveInterval():int 返回 Session 的超时时间,秒
    setMaxInactiveInterval(int interval) 设置 Session 的超时时间,秒
    getAttribute(String name):Object 返回 Session 属性
    setAttribute(String name, Object value) 设置 Session 属性
    getAttributeNames():Enumeration 返回 Session 中存在的属性名
    removeAttribute(String name) 移除 Session 属性
    invalidate() 注销
    isNew():boolean 判断 Session 是否是新创建
    • Session 和 Cookie 的区别:
      • Cookie 是把用户的数据,写给浏览器,由 浏览器保存(可以多个);
      • Session 把用户的数据,写到 用户独占 Session 中(每个浏览器一个Session)服务器端保存(保存重要的信息,减少服务器资源的浪费);
      • Session 对象由 服务器创建
    • 使用场景:
      • 保存一个登录用户的信息;
      • 购物车信息;
      • 在整个网站中,经常会使用的数据,将它保存在 Session 中;

    Session 测试:

    • Servlet 1:存储 Session 的数据;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
    
        // 获取Session
        HttpSession session = req.getSession();
        // Session中存值
        session.setAttribute("name", "Session测试");
        // Session 中存储对象
        session.setAttribute("name1", new Person("学生1",20));
    
        // 获取Session的ID
        String id = session.getId();
    
        //Session创建的时候,把Session ID响应(发送)给了Cookie
        // Cookie cookie = new Cookie("JSESSIONID",sessionId);
        // resp.addCookie(cookie);
    
        // 判断Session是不是新创建
        if (session.isNew()) {
            resp.getWriter().write("Session 创建成功,ID:" + id);
        } else {
            resp.getWriter().write("Session ID:" + id);
        }
    }
    
    • Servlet 2:获取 Session 中的数据,并响应(发送)给客户端
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
    
        // 获取Session
        HttpSession session = req.getSession();
        // 获取Session的值
        String name = (String) session.getAttribute("name");
        // 获取Session存储的对象
        Person name1 = (Person) session.getAttribute("name1");
    
        // 把Session的值响应(发送)给客户端
        resp.getWriter().write("name:" + name);
        resp.getWriter().write("<br/>");
        resp.getWriter().write("name1:" + name1);
        // 输出到控制台测试
        System.out.println(name);
        System.out.println("==========================");
        System.out.println(name1);
    }
    
    • Servlet 3:移除 Session 属性、注销
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取Session
        HttpSession session = req.getSession();
        // 移除 Session 属性
        session.removeAttribute("name");
        // 手动注销Session:注销后,会重新生成Session ID
        session.invalidate();
    }
    
    • 注册 Servlet:web.xml
    • 会话自动过期:web.xml 配置;
    <!--设置Session默认的失效时间-->
    <session-config>
        <!--3分钟后Session自动失效,以分钟为单位-->
        <session-timeout>3</session-timeout>
    </session-config>
    
    • 多用户访问:

    八、JSP

    8.1 什么是 JSP

    • JSP:Java Server Pages,java 服务器端页面,和 Servlet 一样,用于动态Web。
    • 特点:
      • JSP 类似 HTML;
      • 区别:
        • HTML 只给用户提供 静态数据
        • JSP 页面中,可以嵌入 JAVA 代码,为用户提供 动态数据

    8.2 JSP原理

    分析 JSP 执行的过程:

    • JSP 代码层面:查看打包后的文件,没有发生变化;

    • 服务器内部分析:

      • Tomcat 服务器中有 work 目录;

      • IDEA 中使用 Tomcat,会在 IDEA 的 Tomcat 中生产 work 目录:

      • Catalina 目录下,生成 java 文件:

    结论:

    • 浏览器向服务器发送请求,不管访问什么资源,其实都是在访问 Servlet
    • JSP 最终也会被转换成为一个 Java 类;
    • JSP 本质上就是 Servlet
    • JSP 的三个方法:
    // 初始化
    public void _jspInit() {
    }
    // 销毁
    public void _jspDestroy() {
    }
    // JSPService
    public void _jspService(HttpServletRequest request, HttpServletResponse response) {
    }
    

    JSP 的执行过程

    1. 判断请求:

      • GET;
      • POST;
    2. 内置一些对象:

      // 页面上下文
      final jakarta.servlet.jsp.PageContext pageContext;
      // session
      jakarta.servlet.http.HttpSession session = null;
      // applicationContext    ServletContext改名
      final jakarta.servlet.ServletContext application;
      // 配置
      final jakarta.servlet.ServletConfig config;
      // out:输出
      jakarta.servlet.jsp.JspWriter out = null;
      // page:当前
      final java.lang.Object page = this;
      
      // 请求
      HttpServletRequest request
      // 响应
      HttpServletResponse response
      
    3. 输出页面前,增加的代码:

      // 设置响应的页面类型
      response.setContentType("text/html");
      pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
      
    4. 以上的对象,可以在 JSP 页面中直接使用;

    • 流程图:

    • JSP 页面输出格式:

    • JAVA 代码就会原封不动的输出;

    • HTML代码,会被转换为:

      out.write("<html>\r\n");
      

    8.3 JSP 基础语法

    • JSP 在 Java 的基础上,增加了扩充的语法(了解);

    • JSP 表达式<%= %>

      <%--表示注释--%>
      <%--
          JSP 表达式:
          作用:将程序的输出内容,输出到客户端
          格式:<%= 变量或者表达式%>
      --%>
      <%--输出当前时间--%>
      <%= new java.util.Date()%>
      
    • JSP 脚本片段<% %>

      <%--jsp脚本片段--%>
      <%
          int sum = 0;
          for (int i = 1; i <= 100; i++) {
              sum += i;
          }
          out.println("<h1>Sum=" + sum + "</h1>");
      %>
      
    • JSP 脚本片段的再实现:

      <%--JSP 脚本片段的再实现--%>
      <%
          int x = 10;
          out.println(x);
      %>
      <p>这是一个JSP文档</p>
      <%
          int y = 2;
          out.println(y);
      %>
      <hr>
      <%--在代码嵌入HTML元素--%>
      <%
          for (int i = 0; i < 5; i++) {
      %>
      <h1>Hello,World <%=i%></h1>
      <%
          }
      %>
      
    • JSP 声明<%! %>

      • JSP 声明,被 编译到 JSP 生成的 Java 类中,其他代码,被生成到 _jspService 方法中,在 JSP,嵌入 Java 代码即可;
      <%--JSP声明--%>
      <%!
          static {
              System.out.println("Loading Servlet!");
          }
      
          private int globalVar = 0;
      
          public void test() {
              System.out.println("进入了方法test");
          }
      %>
      
    • JSP 的注释:<%-- --%> 不会在客户端显示,HTML 的注释,会显示到客户端;

    8.4 JSP 指令

    • 格式:
    <%@ 指令名称 属性1="属性值1" 属性2="属性值2" ... 属性n="属性值n" %>
    
    1. page 指令
    • 定义网页依赖属性,比如脚本语言、error页面、缓存需求等;
    • 格式:<%@ page ... %>
    <%--设置文件格式和编码格式--%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%--导入包--%>
    <%@ page import="java.util.Date" %>
    <%--自定义错误页面--%>
    <%@ page errorPage="error/500.jsp" %>
    
    • 通过 web.xml 配置跳转页面:
    <error-page>
        <!--错误代码-->
        <error-code>404</error-code>
        <!--设置对应跳转页面-->
        <location>/error/404.jsp</location>
    </error-page>
    
    • 属性说明:
    属性 描述
    buffer 指定 out 对象使用缓冲区的大小
    autoFlush 控制 out 对象的 缓存区
    contentType 指定当前 JSP 页面的MIME类型和字符编码
    errorPage 指定当 JSP 页面发生异常时需要转向的错误处理页面
    isErrorPage 指定当前页面是否可以作为另一个 JSP 页面的错误处理页面
    extends 指定 servlet 从哪一个类继承
    import 导入要使用的 Java 类
    info 定义 JSP 页面的描述信息
    isThreadSafe 指定对 JSP 页面的访问是否为线程安全
    language 定义 JSP 页面所用的脚本语言,默认是 Java
    session 指定 JSP 页面是否使用 session
    isELIgnored 指定是否执行 EL 表达式
    isScriptingEnabled 确定脚本元素能否被使用
    1. Include 指令
    • 静态包含(统一编译):会将两个页面以静态方式编译,合二为一;
    • 格式:<%@ include ... %>
    <%@ include file="included.jsp"%>
    
    <%--
        JSP标签方式拼接
        jsp:include:拼接页面,每个页面还是单独的整体
    --%>
    
    1. Taglib 指令
    • 用于在 JSP 页面中导入标签库(JSP 标准标签库、第三方、自定义) ;
    • 格式:<%@ taglib ... %>
    <%--核心标签(c)是最常用的 JSTL标签 --%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    

    8.5 JSP 内置对象及作用域

    • 9 大内置对象:

      • PageContext:存东西;
      • Request:存东西;
      • Session 存东西;
      • Application 【SerlvetContext】 存东西;
      • Response;
      • config 【SerlvetConfig】;
      • out;
      • page,不用了解 ;
      • exception;
    • 作用域图示:

    • 测试:在对象中存值,并测试取值

    <%--内置对象:存入属性值--%>
    <%
        // 保存的数据只在一个页面中有效
        pageContext.setAttribute("name1", "pageContext");
        // 保存的数据只在一次请求中有效,请求转发会携带这个数据
        request.setAttribute("name2", "request");
        // 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
        session.setAttribute("name3", "session");
        // 保存的数据只在服务器中有效,从打开服务器到关闭服务器
        application.setAttribute("name4", "application");
    %>
    <%--取出属性值--%>
    <%
        // JSP代码内的java代码,必须保证语法的正确
        // 通过寻找方式取值,从底层到高层(作用域)
        String name1 = (String) pageContext.findAttribute("name1");
        String name2 = (String) pageContext.findAttribute("name2");
        String name3 = (String) pageContext.findAttribute("name3");
        String name4 = (String) pageContext.findAttribute("name4");
        String name5 = (String) pageContext.findAttribute("name5");
    %>
    <%--测试转发携带数据--%>
    <%
        pageContext.forward("/jsp3.jsp");
        // 作用等同
        // request.getRequestDispatcher("/index.jsp").forward(request,response);
    %>
    
    <%--使用EL表达式输出 ${变量}--%>
    <h1>取出的值为:</h1>
    <h3>${name1}</h3>
    <h3>${name2}</h3>
    <h3>${name3}</h3>
    <h3>${name4}</h3>
    <h3>${name5}</h3>   // 未创建变量,页面不显示
    <%--EL表达式会过滤 null值,普通表达式不能--%>
    <h3><%= name5 %></h3>   // 未创建变量,页面显示 null
    
    • 测试:在另一个页面中,取值上个页面的值
    <h1>取出的值为:</h1>
    <h3>${name1}</h3>
    <h3>${name2}</h3>
    <h3>${name3}</h3>
    <h3>${name4}</h3>
    <h3>${name5}</h3>
    

    对象作用域及应用场景:

    • request:客户端向服务器发送请求,产生的数据,用户看完就没用了;
      • 比如:新闻;
    • session:客户端向服务器发送请求,产生的数据,用户用完一会还有用;
      • 比如:购物车;
    • application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用;
      • 比如:聊天数据;

    8.6 JSP 标签、JSTL 标签、EL 表达式

    • EL 表达式:${ }
      • 获取数据;
      • 执行运算;
      • 获取 web 开发的常用对象;
    • JSP 标签:
    <%--包含页面--%>
    <jsp:include page="jsp3.jsp"></jsp:include>
    
    <%--
    http://localhost:8080/test.jsp?name=liu&age=20
    --%>
    <%--转发、携带参数转发--%>
    <jsp:forward page="/test.jsp">
        <jsp:param name="name" value="liu"></jsp:param>
        <jsp:param name="age" value="20"></jsp:param>
    </jsp:forward>
    
    • JSTL 标签:
      • JSTL 标签库的使用,是为了弥补 HTML 标签的不足;
      • 自定义许多标签,标签的功能和 Java 代码一样
      • 分类:核心标签、格式化标签、SQL标签、XML 标签;

    核心标签(掌握):

    • 引用核心标签库的语法:使用前,必须在页面头部引用
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
    • 标签说明:

    • JSTL 标签库使用步骤:

      • 引入对应的 taglib;
      • 使用其中的方法;
      • 复制依赖包到 Tomcat lib 目录下,否则会报错:JSTL 解析错误

    标签库添加依赖说明:Tomcat 版本 10.0.16

    1. pom.xml 文件中,增加依赖,导入标签库依赖;

      <!--标签库依赖-->
      <dependency>
          <groupId>org.apache.taglibs</groupId>
          <artifactId>taglibs-standard-spec</artifactId>
          <version>1.2.5</version>
      </dependency>
      <!--
          如果不够,再补充下面这个依赖
          <dependency>
              <groupId>org.apache.taglibs</groupId>
              <artifactId>taglibs-standard-impl</artifactId>
              <version>1.2.5</version>
          </dependency>
      -->
      
      • 依赖关系图:
    2. 将导入的 jar 包,复制到 Tomcat 服务器的 lib 目录下:(避免 JSTL 解析错误

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

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

      • taglibs-standard-spec-1.2.5.jar

      • Tomcat 的 lib 目录:

    • 标签测试:<c: if>
    <%--表单测试:以get方式,提交给当前页面--%>
    <form action="coreif.jsp" method="get">
        <%--
            EL表达式获取表单中的数据:${param.参数名}
        --%>
        <input type="text" name="username" value="${param.username}">
        <input type="submit" value="登录">
    
        <%--如果用户名=admin,登录成功 var 为布尔值--%>
        <c:if test="${param.username=='admin'}" var="isAdmin">
            <c:out value="管理员登录!"/>
        </c:if>
        <%--输出布尔值,注意自闭合标签--%>
        <c:out value="${isAdmin}"/>
    </form>
    
    • 标签测试: <c:choose><c:when>
    <%--定义一个变量score,值为85--%>
    <c:set var="score" value="85"/>
    <c:choose>
        <%--从上往下匹配--%>
        <c:when test="${score>=90}">
            优秀
        </c:when>
        <c:when test="${score>=80}">
            良好
        </c:when>
        <c:when test="${score>=70}">
            一般
        </c:when>
        <c:when test="${score<=60}">
            不及格
        </c:when>
    </c:choose>
    
    • 标签测试:<c:forEach>
    <%
        // 创建集合,并添加数据,注意:索引从0开始
        ArrayList<String> people = new ArrayList<>();
        people.add(0, "张三");
        people.add(1, "李四");
        people.add(2, "王五");
        people.add(3, "赵六");
        people.add(4, "田七");
        // 把集合添加到request(请求)中
        request.setAttribute("list", people);
    %>
    <%--
        var:每一次遍历出来的变量
        items:要遍历的对象
        begin:哪里开始
        end:到哪里
        step:步长
    --%>
    <%--从请求中遍历数据,注意:被取值的变量要包含在 ${} 中}--%>
    <c:forEach var="student" items="${list}">
        <c:out value="${student}"/><br/>
    </c:forEach>
    <hr/>
    <c:forEach var="student" items="${list}" begin="1" end="3" step="1">
        <c:out value="${student}"/><br/>
    </c:forEach>
    

    可能遇到的问题

    • JavaWeb 项目不生成 target 目录:

    九、JavaBean

    9.1 实体类

    • 实例类:一般和数据中的 表结构一一对应;
    • JavaBean 有特定的写法:
      • 必须要有一个无参构造
      • 属性必须私有化
      • 必须有对应的 get/set 方法

    9.2 字段映射

    • JavaBean 一般用来和数据库的字段做映射(ORM);

    • ORM :对象关系映射:

      • 表 ---> 类;
      • 字段 ---> 属性;
      • 行记录 ----> 对象;
    • 数据表示例:people

      id name age address
      1 张三 20 北京
      2 李四 18 上海
      3 王五 22 深圳
      // 类-->表
      class People{
          private int id;   // 属性-->字段
          private String name;
          private int age;
          private String address;
      }
      class A{
          new People(1,"张三",20,"北京");   // 对象-->一条记录
          new People(2,"李四",18,"上海");
          new People(3,"王五",22,"深圳");
      }
      
    • 测试:

    <%
        // People people = new People();
        // people.setId();
        // people.setName();
        // people.setAge();
        // people.setAddress();
    %>
    <%--创建对象--%>
    <jsp:useBean id="people" class="com.study.pojo.People" scope="page"/>
    <%--给对象赋值:数据库一条记录--%>
    <jsp:setProperty name="people" property="id" value="1"/>
    <jsp:setProperty name="people" property="name" value="张三"/>
    <jsp:setProperty name="people" property="age" value="20"/>
    <jsp:setProperty name="people" property="address" value="北京"/>
    <%--取值--%>
    <%--表达式方式:<%=people.getAddress()%>--%>
    ID:<jsp:getProperty name="people" property="id"/><br/>
    姓名:<jsp:getProperty name="people" property="name"/><br/>
    年龄:<jsp:getProperty name="people" property="age"/><br/>
    地址:<jsp:getProperty name="people" property="address"/><br/>
    

    十、MVC 三层架构

    • MVC:
    • Model:模型,指实体类对应数据库中的字段;
    • view:视图,指 JSP 页面;
    • Controller:控制器,指 Servlet,负责跳转页面;

    10.1 以前的架构

    • Servlet 和 JSP 都可以写 Java 代码;

      • Servlet 专注于处理请求,以及控制视图跳转;
      • JSP 专注于显示数据;
    • 用户直接访问控制层,控制层,可以直接操作数据库;

    • Servlet 的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码;

      • Servlet --> CRUD --> 数据库;
      • 弊端:程序臃肿,不利于维护;
    • 架构:没有什么是加一层解决不了的,如果不行,再加一层

      /*  
             程序员调用
                 |
               JDBC
                 |
        Mysql Oracle SqlServer...
      */
      

    10.2 MVC 三层架构

    • 架构图:

    • Model:模型

      • 业务处理:业务逻辑(Service);
      • 数据持久层:CRUD(Dao);
    • View:视图

      • 展示数据;
      • 提供链接发起 Servlet 请求 (a,form,img…)
    • Controller(Servlet):控制器

      • 接收用户的请求:(req:请求参数、Session 信息….);
      • 交给业务层处理对应的代码;
      • 控制视图的跳转;
      登录 --> 接收用户的登录请求 --> 处理用户的请求(获取用户登录的参数,username,password)--> 交给业务层处理登录业务(判断用户名密码是否正确:事务)--> Dao层(查询用户名和密码是否正确) --> 数据库
      

    十一、Filter(重点):过滤器

    11.1 Filter 概述

    • Filter:过滤器,过滤网站数据;

    • 应用:

      • 处理中文乱码;
      • 登录验证...;

    11.2 Filter 开发步骤

    1. 导包:

      • 注意:不要导错包,选 Servlet 包;

    2. 编写过滤器:

      • 实现 Filter 接口, 重写对应的方法;
      // 通过注解,设置过滤路径:show下的所有请求都被过滤
      // 也可以通过 web.xml 文件配置
      @WebFilter("/show/*")
      public class CharacterEncodingFilter implements Filter {
          // 初始化:web服务器启动,过滤器就初始化了,随时等待过滤对象出现
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              System.out.println("CharacterEncodingFilter 初始化");
          }
      
          // FilterChain : 链
          /*
              1.过滤中的所有代码,在过滤特定请求的时候,都会执行
              2.必须要让过滤器继续执行
              filterChain.doFilter(request,response);
          */
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              System.out.println("过滤器执行前");
              // 设置过滤执行的内容
              servletRequest.setCharacterEncoding("utf-8");
              servletResponse.setCharacterEncoding("utf-8");
              servletResponse.setContentType("text/html;charset=UTF-8");
      
              // 让请求继续执行,如果不写,程序到这里就被拦截,停止!
              filterChain.doFilter(servletRequest, servletResponse);
              System.out.println("过滤器执行后");
          }
      
          // 销毁:web服务器关闭的时候,过滤器才会销毁
          @Override
          public void destroy() {
              System.out.println("CharacterEncodingFilter 销毁");
          }
      }
      
    3. web.xml 中配置 Filter:

      • 也可以通过注解方式:@WebFilter("/show/*")
      <filter>
          <filter-name>CharacterEncodingFilter</filter-name>
          <filter-class>com.study.filter.CharacterEncodingFilter</filter�class>
          </filter>
      <filter-mapping>
          <filter-name>CharacterEncodingFilter</filter-name>
          <!--只要是 /show 下的任何请求,会经过这个过滤器-->
          <url-pattern>/show/*</url-pattern>
          <!--过滤所有内容,不建议使用-->    
          <!--<url-pattern>/*</url-pattern>-->
      </filter-mapping>
      

    十二、监听器 Listener(了解)

    • 实现监听器的接口(有 N 种);
    • 编写一个监听器,实现监听器的接口:
    // 统计网站在线人数:统计session
    // 注解方式:注册监听,也可以通过web.xml设置
    @WebListener
    public class OnlineCountListener implements HttpSessionListener {
        // 创建session监听:监测程序执行
        // 一旦创建Session,就触发一次这个事件
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            // 获取上下文:作用域提升到最大
            ServletContext context = se.getSession().getServletContext();
            // 获取在线人数
            Integer onlineCount = (Integer) context.getAttribute("onlineCount");
            // 如果为空,赋值为1
            if (onlineCount == null) {
                onlineCount = 1;
            } else {
                // 不为空,值自增
                onlineCount++;
            }
            // 将属性存储到上下文
            context.setAttribute("onlineCount", onlineCount);
            System.out.println("监听初始化");
        }
    
        // 销毁session监听
        // 一旦销毁Session,就触发一次这个事件
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            // 过程与创建相反
            ServletContext context = se.getSession().getServletContext();
            Integer onlineCount = (Integer) context.getAttribute("onlineCount");
            // 如果为空,赋值为0
            if (onlineCount == null) {
                onlineCount = 0;
            } else {
                onlineCount--;
            }
            // 将属性存储到上下文
            context.setAttribute("onlineCount", onlineCount);
            System.out.println("监听销毁");
        }
        /*
            Session销毁:
            1.手动销毁:getSession().invalidate();
            2.自动销毁:设置web.xml
        */
    }
    
    • JSP 页面获上下文属性:
    <%=this.getServletConfig().getServletContext().getAttribute("onlineCount")%>
    
    • web.xml 中注册监听器:或用注解方式:@WebListener
    <listener>
        <listener-class>com.study.listener.OnlineCountListener</listener-class>
    </listener>
    

    十三、过滤器、监听器常见应用

    13.1 监听器在 GUI 中的应用

    • GUI 基础
    • 实例:监听器在 GUI 编程中使用;
    public class TestPanel {
        public static void main(String[] args) {
            // 新建一个窗体
            Frame frame = new Frame("GUI 监听测试");
            // 面板
            Panel panel = new Panel(null);
            // 设置窗体的布局
            frame.setLayout(null);
            // 设置背景颜色
            frame.setBounds(300, 300, 500, 500);
            frame.setBackground(new Color(217, 217, 217));
            panel.setBounds(50, 50, 300, 300);
            // 设置背景颜色
            panel.setBackground(new Color(70, 142, 192));
            frame.add(panel);
            frame.setVisible(true);
            // 监听事件:监听关闭事件
            frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
        }
    }
    

    13.2 Filter 实现权限拦截:

    • 过滤器在用户登录中的使用;
      • 用户登录后,向 Sesison 中放入用户的数据;
      • 进入主页时,判断用户是否已经登录,未登录,进行页面跳转;

    测试:

    • JSP 页面:
    <%--login.jsp--%>
    <form action="<%= request.getContextPath()%>/login" method="post">
        <input type="text" name="username">
        <input type="submit">
    </form>
    
    <%--error.jsp--%>
    <h3>没有权限,用户名错误</h3>
    <p><a href="<%= request.getContextPath()%>/login.jsp">返回登录页面</a></p>
    
    <%--/sys/success.jsp--%>
    <h1>登录成功</h1>
    <p><a href="<%= request.getContextPath()%>/logout">注销</a></p>
    
    • 常量类:便于后期管理
    public class Constant {
        public static final String USER_SESSION = "USER_SESSION";
    }
    
    • Servlet:登录
    @WebServlet("/login")
    public class LoginServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取前端的请求参数
            String username = req.getParameter("username");
    
            if (username.equals("admin")) {
                // 登录成功:把属性值存入session
                req.getSession().setAttribute(Constant.USER_SESSION, req.getSession().getId());
                // 获取路径后,再拼接地址
                // 注意:如果路径用相对路径表示,”/“表示:http://localhost:8080,与转发的不同
                resp.sendRedirect(req.getContextPath()+"/sys/success.jsp");
            } else {
                // 登录失败
                // 请求转发
                // req.getRequestDispatcher("/error.jsp").forward(req,resp);
                // 重定向
                resp.sendRedirect(req.getContextPath()+"/error.jsp");
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • Servlet:注销
    @WebServlet("/logout")
    public class LogoutServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取session
            Object user_session = req.getSession().getAttribute(Constant.USER_SESSION);
            // 如果不为空,移除 Session 属性
            if (user_session != null) {
                req.getSession().removeAttribute(Constant.USER_SESSION);
            }
            // 重定向
            resp.sendRedirect(req.getContextPath() + "/login.jsp");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    • 过滤器:
    // 过滤所有sys下的请求
    @WebFilter("/sys/*")
    public class SysFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
            // 类型转换:获取session,重定向
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
            // 过滤:所有session为空,跳转页面
            if (request.getSession().getAttribute(Constant.USER_SESSION) == null) {
                response.sendRedirect(request.getContextPath() + "/error.jsp");
            }
            chain.doFilter(req, resp);
        }
    
        @Override
        public void destroy() {
        }
    }
    
    • 注册:web.xml 配置,或通过注解;

    过滤器实现,不同用户登录不同页面

    • 思路:建立不同等级用户文件夹及页面,如:VIP1、VIP2、VIP3...;
    • 过滤器里设置,不同等级对应的跳转页面;
    // 通过对象属性判断
    if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP1) {
        response.sendRedirect( "vip1/index.jsp");
    }
    if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP2) {
        response.sendRedirect( "vip2/index.jsp");
    }
    if (request.getSession().getAttribute(Constant.USER_SESSION).Level == VIP3) {
        response.sendRedirect( "vip3/index.jsp");
    }
    

    十四、JDBC

    14.1 实验环境搭建

    • 创建 MySQL 数据库:相关链接

    • 导入 JDBC 依赖:

    <!--MySQL 依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    
    <!--druid 依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>
    

    14.2 JDBC 固定步骤

    1. 加载驱动;
    2. 连接数据库,代表数据库;
    3. 向数据库发送 SQL 的对象 Statement : CRUD;
    4. 编写 SQL(根据业务,不同的 SQL);
    5. 执行 SQL;
    6. 关闭连接;

    14.3 事务

    14.4 Junit 单元测试

    • Junit 依赖:

      <!--单元测试-->
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.13.2</version>
      </dependency>
      
    • 使用方式:

      • @Test 注解,只有在方法上有效;
      • 只要加了这个注解的方法,不需要 main 方法,就可以直接运行;
      @Test
      public void test(){
          System.out.println("Hello World");
      }
      
      • 错误时,报红色信息;

    相关文章

      网友评论

        本文标题:JavaWeb 基础

        本文链接:https://www.haomeiwen.com/subject/rrjcrrtx.html