JavaWeb 基础

一、基本概念

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");
    }
    
    • 错误时,报红色信息;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容