JavaWeb

article/2025/8/26 7:28:31

目录

    • 1. 基本概念
      • 1.1 基本概念
      • 1.2 web应用程序
      • 1.3 静态web
      • 1.4 动态web
    • 2. web服务器
    • 3. tomcat详解
      • 3.1 安装
      • 3.2 启动
      • 3.3 配置
        • 3.3.1 配置启动的端口号
        • 3.3.2 配置主机的名称
        • 3.3.3 其他常用配置项
          • 日志配置
          • 数据源配置
          • 安全配置
      • 3.4 发布一个网站
    • 4. Http协议
      • 4.1 什么是http
      • 4.2 http的两个时代
      • 4.3 http请求
      • 4.4 http响应
    • 5. Maven
    • 6. Servlet
      • 6.1 servlet简介
      • 6.2 HelloServlet
      • 6.3 Servlet原理
      • 6.4 Mapping( **<font style="color:rgb(44, 44, 54);">映射 ** )问题
      • 6.5 ServletContext
      • 6.6 HttpServletResponse
        • <font style="color:rgb(232, 62, 140);background-color:rgb(246, 246, 246);">常见应用
      • 6.7 HttpServletRequest
    • 7. cookie/session
      • 7.1 会话
      • 7.2 保存会话的两种技术
      • 7.3 Cookie
      • 7.4 Session (重点)
    • 8. jsp
      • 8.1 什么是jsp
      • 8.2 jsp原理
      • 8.3 JSP基础语法
      • 8.4 jsp指令
      • 8.5 九大内置对象
      • 8.6 jsp标签、JSTL标签、EL表达式
        • 8.6.1 JSP 标签(JSP Actions)
          • 定义:
          • 常见标签:
          • 示例代码:
          • 注意事项:
        • 8.6.2 EL 表达式(Expression Language)
          • 定义:
          • 语法格式:
          • EL 访问对象范围:
          • 示例:
          • EL 运算符:
        • 8.6.3 JSTL 标签库(JSP Standard Tag Library)
          • 定义:
          • 引入方式(在 JSP 页面顶部添加):
          • 常用 JSTL 标签分类:
            • 核心标签库(Core Tags)
            • 格式化标签库(Fmt Tags)
            • 3. 函数标签库(Fn Tags)
        • 8.6.4 面试高频问题(附参考答案)
        • 8.6.5 斜体样式总结
    • 9. MVC三层架构
      • 9.1 什么是 MVC?
      • 9.2 MVC 在 Java Web 中的三层架构详解
        • 表现层(View / UI Layer)
        • 控制层(Controller Layer)
        • 业务逻辑层(Service Layer)
        • 数据访问层(DAO Layer / Persistence Layer)
      • 9.3 MVC 分层结构
      • 9.4 MVC 的优势
      • 9.5 简单案例演示
        • 场景:用户登录功能
          • View(login.jsp)
          • Controller(LoginServlet)
          • Service(UserService.java)
          • DAO(UserDao.java)
          • View(welcome.jsp)
      • 9.6 面试高频问题
    • 10. 过滤器(Filter)
    • 11. 监听器
    • 12. 过滤器、监听器常见应用

1. 基本概念

1.1 基本概念

  1. 静态web:html,css(提供给所有人看的数据,始终不会发生变化)
  2. 动态web:Servlet/JSP,ASP,PHP,淘宝等几乎所有的网站
    (提供给所有人看的数据,始终会发生变化,每个人在不同时间,不同地方看到的都不同)

1.2 web应用程序

web应用程序:可以提供浏览器访问的程序

  • a.html、b.html等多个web资源,这些web资源可以被外界访问,对外界提供服务。
  • 你们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
  • URL:统一资源定位符。
  • 这个统一的web资源会被放在同一个文件夹下,web应用程序—>Tomcat:服务器。
  • web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理。

1.3 静态web

_ .htm, _ .html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取。

画板

1.4 动态web

页面会动态展示:Web的页面展示的效果因人而异。

画板

2. web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息。
** IIS ** :

** IIS(Internet Information Services) ** 是微软开发的一款Web服务器软件,专为Windows操作系统设计。它主要用于托管网站、提供Web服务以及支持多种协议如HTTP、HTTPS等。IIS不仅易于安装和配置,还提供了强大的功能集,包括安全性控制、性能优化以及对多种Web技术的支持。

** Tomcat ** :

** Tomcat ** 则是Apache软件基金会下的一个项目,旨在提供一个运行Java Servlets和JavaServer Pages (JSP) 的环境。由于有Sun Microsystems(现Oracle Corporation)以及其他各方的支持,最新的Servlet和JSP规范通常能够在Tomcat中得到体现。例如,Tomcat 5支持了当时的最新标准Servlet 2.4和JSP 2.0规范。作为一个免费且开源的Web应用服务器,Tomcat因其轻量级特性而在中小型系统中被广泛采用,特别是在并发访问用户数量有限的情况下,它是开发和调试JSP程序的理想选择。

当在一台机器上配置好Apache服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat时,它实际上作为一个与Apache 独立的进程单独运行的。

虽然Tomcat起源于Apache项目,并且能够与Apache HTTP服务器协同工作以处理静态和动态内容,但它本身是一个独立的Servlet容器,专注于Java Web应用程序的执行。因此,在使用Tomcat进行Web开发时,开发者可以根据需要选择是否将其与Apache结合使用,或者单独利用Tomcat来满足项目需求。这种灵活性使得Tomcat成为Java Web应用开发中的重要工具。

3. tomcat详解

3.1 安装

3.2 启动

3.3 配置

Tomcat的配置主要通过编辑其配置文件来完成,这些文件通常位于conf目录下。

3.3.1 配置启动的端口号

Tomcat默认使用8080端口作为HTTP服务端口。如果需要更改这个端口号,可以在conf/server.xml文件中找到相应的<Connector>元素并修改port属性。

例如,将Tomcat的HTTP端口更改为8081:

<Connector port="8081" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />
  • port: 指定Tomcat监听的端口号,mysql:3306;http:80;https:443。
  • protocol: 指定使用的协议类型,这里为HTTP/1.1。
  • connectionTimeout: 设置连接超时时间(毫秒),超过该时间未处理完请求则断开连接。
  • redirectPort: 当客户端请求的是HTTPS服务时,会重定向到此端口。
3.3.2 配置主机的名称

server.xml文件中,可以通过<Host>元素配置虚拟主机。默认情况下,Tomcat使用localhost作为主机名,并将其映射到127.0.0.1

例如,配置一个名为www.none.com的虚拟主机:

<Host name="www.none.com" appBase="webapps"unpackWARs="true" autoDeploy="true">
</Host>
  • name: 虚拟主机的域名。
  • appBase: 应用程序的基本目录,默认为webapps
  • unpackWARs: 是否自动解压WAR包,默认为true
  • autoDeploy: 是否自动部署新的应用程序,默认为true
3.3.3 其他常用配置项
日志配置

日志文件通常位于logs目录下,可以通过编辑conf/logging.properties文件来调整日志级别和输出格式。

数据源配置

如果应用需要连接数据库,可以在conf/context.xml或应用的META-INF/context.xml中配置数据源。

例如,配置MySQL数据源:

<Resource name="jdbc/myDB" auth="Container" type="javax.sql.DataSource"maxTotal="100" maxIdle="30" maxWaitMillis="10000"username="dbuser" password="dbpass" driverClassName="com.mysql.none.jdbc.Driver"url="jdbc:mysql://localhost:3306/mydatabase"/>
安全配置

为了提高安全性,可以配置SSL/TLS以启用HTTPS服务。这通常涉及到生成证书并在server.xml中配置<Connector>元素。

例如,配置HTTPS连接器:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="150" SSLEnabled="true" scheme="https" secure="true"clientAuth="false" sslProtocol="TLS"keystoreFile="/path/to/keystore.jks" keystorePass="changeit"/>

** 请你谈谈网站是如何访问的? **

三次握手,交互。

  1. 输入URL:用户在浏览器地址栏中输入一个URL(统一资源定位符),比如http://www.example.com。这个URL包含了协议(如HTTP或HTTPS)、域名(如www.example.com)和可能的路径(如/path/page.html)。
  2. DNS解析:浏览器需要知道对应域名的IP地址才能建立连接。它首先会在本地缓存或者操作系统缓存中查找该域名对应的IP地址。如果找不到,就会向配置的DNS服务器发送请求查询该域名的IP地址。DNS服务器通过递归查询的方式找到正确的IP地址并返回给浏览器。
  3. 建立TCP连接:获取到目标服务器的IP地址后,浏览器会尝试与服务器建立TCP连接。这通常涉及到**三次握手 **的过程,以确保客户端和服务器双方都准备好进行数据传输。
  4. TLS握手(如果是HTTPS):如果使用的是HTTPS协议,还需要额外进行一次TLS(Transport Layer Security)握手来加密通信。这一步骤包括客户端和服务器之间的密钥交换、证书验证等操作,确保通信的安全性。
  5. 发送HTTP请求:一旦TCP连接建立完成(对于HTTPS还需完成TLS握手),浏览器会构造并发送一个HTTP请求报文到服务器。这个请求报文中包含诸如请求方法(GETPOST等)、请求的URL、头部信息(Headers)以及可能的主体内容(Body)。
  6. 服务器处理请求:Web服务器接收到请求后,会根据请求的内容进行处理。如果是静态文件,服务器直接返回文件;如果是动态内容,则服务器可能会调用相应的脚本或应用程序来生成响应内容。
  7. 发送HTTP响应:服务器处理完请求后,会构建一个HTTP响应报文发回给浏览器。响应报文中包含状态行(表明请求的结果)、头部信息和响应体(通常是HTML文档或其他类型的数据)。
  8. 浏览器接收响应:浏览器接收到响应后,开始解析HTML文档,并根据文档中的指令加载CSS样式表、JavaScript脚本以及其他资源(如图片、视频等)。在这个过程中,浏览器可能会发起更多的HTTP请求来获取这些资源。
  9. 渲染页面:随着资源逐步加载完毕,浏览器开始构建DOM树、应用CSS样式,并执行JavaScript代码,最后将内容渲染到屏幕上供用户查看。

整个流程涉及了多种网络协议(如HTTP、HTTPS、TCP/IP)、技术栈(如HTML、CSS、JavaScript)以及中间件(如DNS服务器、Web服务器)。此外,现代浏览器还提供了许多优化措施,比如预取、预加载、压缩传输等,来提升网页加载速度和用户体验。

3.4 发布一个网站

tomcat的目录结构如下:

apache-tomcat-<version>/
├── bin/              # 存放启动、停止等脚本
│   ├── startup.sh    # Linux下启动Tomcat的脚本
│   ├── shutdown.sh   # Linux下停止Tomcat的脚本
│   ├── catalina.sh   # Tomcat核心控制脚本
│   └── ...           # 其他脚本文件
├── conf/             # 配置文件目录
│   ├── server.xml    # 主配置文件,定义端口、连接器等
│   ├── web.xml       # 默认Web应用部署描述符
│   ├── context.xml   # 上下文配置文件
│   ├── tomcat-users.xml # 用户认证配置
│   └── ...           # 其他配置文件如logging.properties等
├── lib/              # 共享库(JAR)目录
│   ├── servlet-api.jar # Servlet API实现
│   ├── jsp-api.jar     # JSP API实现
│   └── ...           # 其他必要的依赖库
├── logs/             # 日志文件目录
│   ├── catalina.out  # 主要日志文件
│   ├── localhost_access_log.*.txt # HTTP请求访问日志
│   └── ...           # 其他日志文件
├── webapps/          # Web应用程序部署目录
│   ├── ROOT/         # 默认根应用
│   ├── manager/      # 内置管理控制台
│   ├── examples/     # 官方示例应用
|   		├──WEB-INF
|   		   ├── classes  # java程序
|   		   ├── lib      # 所依赖的jar包
|   		   └── web.xml  # 网页配置
|   		├──static       # 静态资源
|   		├──index.html
|   		└──...
│   └── ...           # 自定义的应用程序或WAR包
├── work/             # 运行时工作目录
│   └── Catalina/     # 存储编译后的JSP类文件
├── temp/             # 临时文件目录
├── LICENSE           # 许可证文件
└── NOTICE            # 版权声明

这部分是我们配置网站时所使用的:

├── webapps/          # Web应用程序部署目录
│   ├── ROOT/         # 默认根应用
│   ├── manager/      # 内置管理控制台
│   ├── examples/     # 官方示例应用
|   		├──WEB-INF
|   		   ├── classes  # java程序
|   		   ├── lib      # 所依赖的jar包
|   		   └── web.xml  # 网页配置
|   		├──static       # 静态资源
|   		├──index.html
|   		└──...
│   └── ...           # 自定义的应用程序或WAR

4. Http协议

4.1 什么是http

HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。

  • 文本:html,字符串…
  • 超文本:图片,音乐,视频,定位,地图…
  • 默认端口:80
  • HTTPS:安全的协议
    • 默认端口:443

4.2 http的两个时代

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

4.3 http请求

客户端—->发请求(request)—->服务器(比如:访问百度)
General

// 请求地址
Request URL: https://www.baidu.com/
// 请求方法
Request Method: GET
// 状态代码
Status Code: 200 OK
// 远程地址
Remote Address: 14.215.177.38:443
// 引用站点策略
Referrer Policy: strict-origin-when-cross-origin

Request Headers

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
  • 请求方式:GETPOSTHEADDELETEPUT
    GET: 请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效。
    POST:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。
  1. 请求头(消息头)
Accept: 告诉浏览器,它所支持的数据类型
Accept-Encoding: 告诉浏览器,它支持哪种编码格式:GBK,UTF-8,GB2312,ISO8859-1
Accept-Language: 告诉浏览器,它的语言环境
Cache-Control: 缓存控制
Connection: 告诉浏览器,请求完成是断开还是保持
HOST:主机

4.4 http响应

Response Headers

// 缓存控制
Cache-Control: no-cache
// 保持连接(http1.1)
Connection: keep-alive
// 文本编码类型
Content-Encoding: gzip
// 响应类型
Content-Type: text/html;charset=utf-8
  1. 响应体
Accept: 告诉浏览器,它所支持的数据类型
Accept-Encoding: 告诉浏览器,它支持哪种编码格式:GBK,UTF-8,GB2312,ISO8859-1
Accept-Language: 告诉浏览器,它的语言环境
Cache-Control: 缓存控制
Connection: 告诉浏览器,请求完成是断开还是保持
HOST:主机
Refrush:告诉客户端,多久刷新一次
Location:让网页重新定位
  1. 响应状态码
  • 200:响应成功
  • 3xx:请求重定向(304等等)
  • 4xx:找不到资源(404等等)
  • 5xx:服务器代码错误(500代码错误,502网关错误)

当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

  1. URL 解析

浏览器解析输入的 URL,提取协议(HTTP/HTTPS)、域名、路径等信息。

  1. DNS 解析

检查本地缓存(浏览器、系统、hosts 文件),若无则通过 DNS 服务器递归查询,将域名转换为 IP 地址

  1. 建立 TCP 连接

通过 三次握手 与服务器建立可靠的 TCP 连接(SYN → SYN-ACK → ACK)。

  1. TLS 握手(**HTTPS **)

若为 HTTPS,进行 TLS 握手:证书验证、密钥交换,建立加密通道。

  1. 发送 HTTP 请求

浏览器发送 HTTP 请求(如 GET/index.html ),包含请求头(host、User-Agent 等)和请求体(如 GET 数据)。

  1. 服务器处理请求

服务器解析请求,执行逻辑(如查询数据库),生成响应内容(HTML、图片等)。

  1. 返回 HTTP 响应

服务器返回 HTTP 响应,包含状态码(如 200 OK)、响应头(Content-Type)和响应体(页面内容)。

  1. 浏览器渲染页面

解析 HTML :构建 DOM 树。

加载 CSS/JS :构建 CSSOM 树,执行 JavaScript。

渲染树合成 :合并 DOM 和 CSSOM,布局(Layout)并绘制(Paint)页面。

5. Maven

Maven是一个强大的项目管理和理解工具,主要用于Java项目的构建、依赖管理和文档生成。以下是Maven的简明概述:

  • 核心概念:POM(Project Object Model),它是一个XML文件,包含了项目的配置信息,如依赖、构建目标等。
  • 依赖管理:自动处理项目依赖的库,通过中央仓库或自定义仓库下载所需的JAR包,并解决版本冲突。
  • 构建生命周期:定义了构建过程的标准阶段,包括验证、编译、测试、打包、集成测试、验证、部署等。
  • 插件支持:提供了多种插件来扩展其功能,比如编译代码、创建Javadoc以及运行单元测试等。
  • 多模块项目:支持复杂项目的分模块构建,方便大型项目的维护和管理。

Maven通过提供统一的构建系统、**约定优于配置 **的原则以及强大的依赖管理能力,极大地简化了Java项目的开发流程。

  • Maven项目的标准结构:
myproject/
├── src/
│   ├── main/
│   │   ├── java/Java 源代码
│   │   └── resources/    ← 资源文件(如 .properties, XML 等)
│   └── test/
└── pom.xml

6. Servlet

6.1 servlet简介

  • Servlet就是sun公司开发动态web的一门技术。
  • Sun在这些API中提供一个接口叫做:Servlet,如果你想开发一个 Servlet 程序,只需要完成两个小步骤:
    1. 编写一个类,实现 Servlet 接口;
    2. 把开发好的Java类部署到web服务器中。
      把实现了Servlet接口Java程序叫做 Servlet

6.2 HelloServlet

Serlvet接口Sun公司有两个默认的实现类: HttpServlet GenericServlet,如图所示, HttpServlet 继承自 GenericServlet ,我们手写一个servlet就需要继承自 HttpServlet

  1. 构建一个普通的Maven项目,删掉里面的src目录,在这个项目里面建立Moudel;

这个空的工程就是Maven主工程;

  1. 关于Maven父子工程的理解:
    父工程中会有:
<modules><module>servlet-01</module>
</modules>

子项目中会有:

<parent><groupId>org.example</groupId><artifactId>javaweb-servlet</artifactId><version>1.0-SNAPSHOT</version>
</parent>

父项目中的jar包依赖,子项目可以直接使用,反过来则不可以,这就是 ** 继承 **

  1. Maven环境优化
  2. 编写一个Servlet程序
    1. 编写一个普通的类 HelloServlet
    2. 我们实现Servlet接口,这里我们直接继承 HttpServlet
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("hello servlet");PrintWriter writer = resp.getWriter();writer.println("Hello Servlet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req,resp);}
}
  1. 编写Servlet的映射
    ** 为什么需要映射 ** :我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径。
<!--注册servlet-->
<servlet><servlet-name>helloservlet</servlet-name><servlet-class>com.sunyiwenlong.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet请求路径-->
<servlet-mapping><servlet-name>helloservlet</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置tomcat
    注意:配置项目发布路径就可以了
  2. 启动测试

6.3 Servlet原理

6.4 Mapping( **映射 ** )问题

<project><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>my-web-app</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><dependencies><!-- Servlet API --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><!-- JSP API --><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.3.3</version><scope>provided</scope></dependency><!-- Spring MVC 示例 --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.20</version></dependency>
</dependencies><build><finalName>mywebapp</finalName><plugins><!-- 编译插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><!-- WAR 插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.2.3</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin></plugins>
</build></project>
  1. 一个Servlet可以指定一个映射路径 "/hello"
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 处理逻辑}
}
  1. 一个Servlet可以指定多个映射路径 {"/hello", "/hi", "/greeting"}
@WebServlet({"/hello", "/hi", "/greeting"})
public class HelloServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 多个路径都能访问到这个 Servlet}
}
  1. 一个Servlet可以指定通用映射路径 "/user/*"
@WebServlet("/user/*")
public class UserServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 所有以 /user/ 开头的请求都会进入该 Servlet}
}
  1. 指定一些后缀或者前缀等等…
@WebServlet("*.do")
public class ActionServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 所有以 .do 结尾的请求都进入此 Servlet}
}
@WebServlet("/api/*")
public class ApiServlet extends HttpServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// 所有 /api/ 开头的请求进入此 Servlet}
}

6.5 ServletContext

ServletContext 是 Java Web 开发中一个非常核心的接口,属于 Servlet API 的一部分。每个 Web 应用在服务器启动时都会创建一个唯一的 ServletContext 实例。

Q:可以自己手动 new 一个 ServletContext 对象?

A:不能直接 new 创建 ServletContext

Q:why?

A:ServletContext 是 由 Web 容器(如 Tomcat、Jetty)在启动 Web 应用时自动创建的,它是整个 Web 应用的运行环境对象。

Q:那我们怎么获取它?

A:在 Servlet 、Listener、JSP中获取;

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

  1. 共享数据:在这个Servlet中保存的数据,可以在另一个Servlet中拿到

画板

1. 在 Servlet 中获取 `ServletContext`,将一个数据保存在了`ServletContext`中
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// this.getInitParameter(); 获取初始化参数(web.xml文件中的初始化参数)// this.getServletConfig(); 获取servlet的配置(web.xml文件中的配置)// this.getServletContext(); 获取servlet上下文ServletContext context = this.getServletContext();String username = "张三";context.setAttribute("username",username);// 将一个数据保存在了ServletContext中}
}
// 在 Listener 中获取getServletContext
public class MyListener implements HttpSessionListener {public void sessionCreated(HttpSessionEvent se) {ServletContext context = se.getSession().getServletContext();}
}
2. 将保存在context的数据响应出来。
public class GetServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext context = this.getServletContext();String username = (String) context.getAttribute("username");resp.setContentType("text/html");resp.setCharacterEncoding("utf-8");resp.getWriter().println("名字"+username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req,resp);}
}
3. 配置URL地址映射
<!-- // web.xml文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><servlet><servlet-name>helloservlet1</servlet-name><servlet-class>com.sunyiwenlong.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>helloservlet1</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><servlet><servlet-name>getservlet</servlet-name><servlet-class>com.sunyiwenlong.servlet.GetServlet</servlet-class></servlet><servlet-mapping><servlet-name>getservlet</servlet-name><url-pattern>/getc</url-pattern></servlet-mapping>
</web-app>
1. <font style="color:rgba(0, 0, 0, 0.85);">也可以通过注解配置地址映射 :: Servlet 3.0+ 规范 

  1. 获取初始化参数
    1. 配置web应用中的基本参数
<!-- web.xml文件 -->
<!--配置一些web应用一些初始化参数-->
<context-param><param-name>url</param-name><param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
2. 实现`ServletDemo03`的`Post`和`Get`的逻辑
public class ServletDemo03 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String url = this.getInitParameter("url");resp.getWriter().println(url);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
  1. 请求转发
    1. 配置相关URL映射的地址
// web.xml文件
// 请求sd4
<servlet><servlet-name>gp</servlet-name><servlet-class>com.sunyiwenlong.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping><servlet-name>gp</servlet-name><url-pattern>/gp</url-pattern>
</servlet-mapping><servlet><servlet-name>sd4</servlet-name><servlet-class>com.sunyiwenlong.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping><servlet-name>sd4</servlet-name><url-pattern>/sd4</url-pattern>
</servlet-mapping>
2. `/sd4`请求 找到`ServletDemo04`,`ServletDemo04`逻辑块中进行请求 转发到`/gp`,到`/gp`的页面, `/gp`找到`ServletDemo03`。
// 请求/sd4找到ServletDemo04,ServletDemo04进行请求转发到/gp,到/gp的页面
// (浏览器路径是sd4的路径,页面拿到的是/gp的数据)
public class ServletDemo04 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {ServletContext context = this.getServletContext();System.out.println("进入了demo04");// RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");// 转发的路径// requestDispatcher.forward(req,resp);// 调用forward请求转发context.getRequestDispatcher("/gp").forward(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
  1. 读取资源文件 Properties
  • **<font style="color:rgb(44, 44, 54);background-color:rgba(175, 184, 193, 0.2);">src/main/java ** 目录下新建 properties
  • **<font style="color:rgb(44, 44, 54);background-color:rgba(175, 184, 193, 0.2);">src/main/ **** resources ** 目录下新建 properties
  • 最后都被打包到了同一个路径下: ** target/classes ** ,我们俗称这个路径为classpath。
    1. 实现一个文件流,读取properties
public class ServletDemo05 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {InputStream stream = this.getServletContext().getResourceAsStream("/WEB-INF/CLASSES/db.properties");Properties properties = new Properties();properties.load(stream);String username = properties.getProperty("username");String password = properties.getProperty("password");resp.getWriter().println(username+":"+password);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
2. 用例图解析:

画板

在一个Web应用中,客户端通过请求与服务器上的多个`Servlet`交互,`ServletContext`在其中扮演的角色是提供一个全局的数据共享空间。

重点理解:ServletContext是所有Servlet都能访问的全局上下文,用于在应用范围内共享数据;而HttpSession则是针对单个用户的会话管理,用于保存用户相关的临时数据。

6.6 HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的 HttpServletRequest 对象,代表响应的一个 HttpServletResponse ;|

  • 如果要获取客户端请求过来的参数:找 HttpServletRequest
  • 如果要给客户端响应一些信息:找 HttpServletResponse
  1. 负责向浏览器发送数据的方法
public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;
  1. 响应的状态码
public static final int SC_CONTINUE = 100;
/*** Status code (200) indicating the request succeeded normally.*/
public static final int SC_OK = 200;/*** Status code (302) indicating that the resource has temporarily* moved to another location, but that future references should* still use the original URI to access the resource.** This definition is being retained for backwards compatibility.* SC_FOUND is now the preferred definition.*/
public static final int SC_MOVED_TEMPORARILY = 302;/*** Status code (302) indicating that the resource reside* temporarily under a different URI. Since the redirection might* be altered on occasion, the client should continue to use the* Request-URI for future requests.(HTTP/1.1) To represent the* status code (302), it is recommended to use this variable.*/
public static final int SC_FOUND = 302;/*** Status code (304) indicating that a conditional GET operation* found that the resource was available and not modified.*/
public static final int SC_NOT_MODIFIED = 304;/*** Status code (404) indicating that the requested resource is not* available.*/
public static final int SC_NOT_FOUND = 404;/*** Status code (500) indicating an error inside the HTTP server* which prevented it from fulfilling the request.*/
public static final int SC_INTERNAL_SERVER_ERROR = 500;/*** Status code (502) indicating that the HTTP server received an* invalid response from a server it consulted when acting as a* proxy or gateway.*/
public static final int SC_BAD_GATEWAY = 502;// ...
常见应用
  1. 向浏览器输出消息的
  2. ** 下载文件 ** 实现方式
public class FileServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.要获取下载文件的路径 :\转义字符String realPath = "E:\\dev\\StudyProjects\\javaweb-servlet\\response\\src\\main\\resources\\大乔.jpg";// 2.下载的文件名是啥?String filename = realPath.substring(realPath.lastIndexOf("\\") + 1);// 3.设置想办法让浏览器能够支持下载我们需要的东西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流写入到bufer缓冲区while ((len = in.read(buffer))>0){// 每次读取的长度大于0的情况下,就写出去out.write(buffer,0,len);// 写出字节,从0写到len}// 8.使用OutputStream将缓冲区中的数据输出到客户端!in.close();out.close();}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
  1. ** 验证码功能 ** 实现方式
public class ImageServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 让浏览器3秒刷新一次resp.setHeader("refresh", "3");// 在内存中创建一个图片BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);// 宽、高、颜色// 得到图片Graphics2D g = (Graphics2D) image.getGraphics();// 得到一只2D的笔// 设置图片的背景颜色g.setColor(Color.white);g.fillRect(0, 0, 80, 20);// 填充颜色// 换个背景颜色g.setColor(Color.BLUE);// 设置字体样式:粗体,20g.setFont(new Font(null,Font.BOLD,20));// 画一个字符串(给图片写数据)g.drawString(makeNum(),0,20);// 告诉浏览器,这个请求用图片的方式打开resp.setContentType("image/jpeg");// 网站存在缓存,不让浏览器缓存resp.setDateHeader("expires",-1);resp.setHeader("Cache-Control","no-cache");resp.setHeader("Pragma","no-cache");// 把图片写给浏览器boolean write = ImageIO.write(image, "jpg",resp.getOutputStream());}// 生成随机数private String makeNum() {Random random = new Random();String num = random.nextInt(9999999) + "";// 随机数,最大七位,[0,9999999)StringBuffer sb = new StringBuffer();for (int i = 0; i < 7 - num.length(); i++) {// 不足七位,则添加0sb.append("0");}num = sb.toString()+num;// 不足七位,在随机数前面添加0return num;}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
  1. 实现 ** 请求重定向 **
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*resp.setHeader("Location","/response_war/image");resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);*/resp.sendRedirect("/response_war/image");// 重定向相当于上面两行代码}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

6.7 HttpServletRequest

HttpServletRequest 代表客户端的请求,用户通过 Http 协议访问服务器,HTTP请求中的所有信息会被封装到 HttpServletRequest ,通过这个 HttpServletRequest 的方法,获得客户端的所有信息。

  1. 获取前端传递的参数
    1. getParameter(String name) | 获取指定名称的请求参数值(GET 或 POST 表单)
String username = request.getParameter("username");
String password = request.getParameter("password");
  1. getParameterValues(String name) | 用于获取多个值的参数(如多选框)
String[] hobbies = request.getParameterValues("hobby");
if (hobbies != null) {for (String hobby : hobbies) {System.out.println("兴趣爱好:" + hobby);}
}
  1. getParameterMap() | 返回所有参数的 Map 形式,键为参数名,值为字符串数组(适合处理复杂表单)
Map<String, String[]> parameterMap = request.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { //使用foreach语句循环使用System.out.println(entry.getKey() + " = " + Arrays.toString(entry.getValue()));
}
  1. getInputStream() / getReader() | 适用于接收 JSON、XML 等原始请求体内容(常用于前后端分离项目)
StringBuilder jsonBody = new StringBuilder();
BufferedReader reader = request.getReader();
String line;while ((line = reader.readLine()) != null) {jsonBody.append(line);
}System.out.println("接收到的JSON:" + jsonBody.toString());

注意:你需要使用 JSON 解析库(如 Jackson、Gson)来解析这个字符串。

  1. 获取路径参数(RESTful 风格) :request.getPathInfo()
@WebServlet("/user/*")
public class UserServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) {String pathInfo = request.getPathInfo(); // 如:/user/123 → 返回 "/123"if (pathInfo != null && pathInfo.length() > 1) {String userId = pathInfo.substring(1); // 去掉开头斜杠System.out.println("用户ID:" + userId);}}
}
  1. 完整代码如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 获取普通参数String username = request.getParameter("username");String password = request.getParameter("password");// 获取多选参数String[] hobbies = request.getParameterValues("hobby");// 获取所有参数(Map)Map<String, String[]> parameterMap = request.getParameterMap();// 输出参数System.out.println("用户名:" + username);System.out.println("密码:" + password);if (hobbies != null) {for (String hobby : hobbies) {System.out.println("兴趣:" + hobby);}}// 处理 JSON 数据(如果需要)if ("application/json".equals(request.getContentType())) {BufferedReader reader = request.getReader();StringBuilder json = new StringBuilder();String line;while ((line = reader.readLine()) != null) {json.append(line);}System.out.println("JSON 内容:" + json);}
}
  1. 请求 ** 转发 **

前端:

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>首页</title></head><body><form action="${pageContext.request.contextPath}/login" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>爱好:<input type="checkbox" name="hobbys" value="代码"> 代码<input type="checkbox" name="hobbys" value="唱歌"> 唱歌<input type="checkbox" name="hobbys" value="女孩"> 女孩<input type="checkbox" name="hobbys" value="电影"> 电影<br><input type="submit" name="提交"></form></body></html>

后端:

public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(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[] hobbys = req.getParameterValues("hobbys"); // 爱好System.out.println(username);System.out.println(password);System.out.println(Arrays.toString(hobbys));// 这里的 / 代表当前的web应用,所以不需要再加上/request_war这个上下文路径了,否则会出现404错误 转发req.getRequestDispatcher("/success.jsp").forward(req,resp);}
}

web.xml

<servlet><servlet-name>login</servlet-name><servlet-class>com.sunyiwenlong.request.LoginServlet</servlet-class>
</servlet><servlet-mapping><servlet-name>login</servlet-name><url-pattern>/login</url-pattern>
</servlet-mapping>

这个 web.xml 配置定义了一个名为 login 的Servlet及其URL映射,对应的Java代码实现了基本的登录处理逻辑,包括获取请求参数和返回响应

面试题:请你聊聊重定向转发的区别?
相同点:页面都会实现跳转
不同点:

请求转发的时候,URL地址栏不会产生变化。
* 适合服务器内部跳转 。
* 状态码: 307 (临时重定向)
* RequestDispatcher.forward()

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 设置属性request.setAttribute("message", "这是转发时携带的消息");// 获取 RequestDispatcher 并转发RequestDispatcher dispatcher = request.getRequestDispatcher("/target.jsp");dispatcher.forward(request, response);
}
-  重定向时候,URL地址栏会发生变化。 *  跳转到外部网站。 *  状态码:302 ,301(永久重定向) * ` HttpServletResponse.sendRedirect() `
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// 重定向到另一个路径response.sendRedirect("http://example.com");  // 也可以是相对路径:"/myapp/target.jsp"
}

7. cookie/session

7.1 会话

无状态的会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话。
有状态的会话:一个用户打开一个浏览器,访问某些资源(网站),下次再来访问该资源(网站),我们会知道这个用户曾经来过,称之为有状态会话;

**一个网站,怎么证明你来过? **

  1. 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;cookie(客户端)
  2. 服务器登记你来过了,下次你来的时候我来匹配你;session(服务端)

7.2 保存会话的两种技术

cookie:

  • 客户端技术,(响应、请求)

session:

  • 服务端技术,利用这个技术,可以保存用户的会话信息?我们可以把信息或者数据放在Session中。
用户登录↓
服务器创建 Session 并保存用户信息↓
服务器生成 JSESSIONID 并写入 Cookie↓
客户端保存 Cookie↓
后续请求携带 Cookie 到服务器↓
服务器根据 JSESSIONID 找到对应的 Session↓
继续处理用户逻辑

7.3 Cookie

  1. 从请求中拿到cookie
  2. 服务器响应给客户端cookie
Cookie[] cookies = req.getCookies();// 获得cookie
cookie.getName();// 获得cookie中的key
cookie.getValue();// 获得cookie中的value
new Cookie("lastLoginTime",System.currentTimeMills()+"");// 新建一个cookie
cookie.setMaxAge(24*60*60);// 设置cookie的有效期,单位:秒
resp.addCookie(cookie);// 响应给客户端一个cookie

Q: cookie:一般会保存在本地的用户目录下appdata,一个网站cookie是否存在上限!聊聊细节问题

  • 一个Cookie只能保存一个信息;
  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
  • Cookie大小有限制4kb
  • 300个cookie浏览器上限

删除cookie

  • 不设置有效期,关闭浏览器,自动失效
  • 设置有效期时间为 0

编码解码,怎么解决中文乱码问题

URLEncoder.encode("张三","UTF-8")
URLDecoder.decode("张三","UTF-8")

保存上一次登录时间** 实现方式 **

// 保存用户上一次访问的时间
public class CookieDemo01 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 服务器告诉你,你来的时间,把这个时间封装成一个信息,你下次带来,我就知道你上次来的时间// 解决中文乱码req.setCharacterEncoding("utf-8");resp.setCharacterEncoding("utf-8");PrintWriter out = resp.getWriter();// Cookie,服务器端从客户端获取cookieCookie[] cookies = req.getCookies();// 数组,说明cookie可以有多个// 判断cookie是否if (cookies != null) {out.write("你上一次登录的时间是:");for (int i = 0; i < cookies.length; i++) {// 获取cookie的名字if (cookies[i].getName().equals("lastLoginTime")) {// 获取cookie的值long l = Long.parseLong(cookies[i].getValue());Date date = new Date(l);out.write(date.toLocaleString());}}} else {out.write("你是第一次登录!");}Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");cookie.setMaxAge(24*60*60);// 设置cookie的有效期为一天,单位是:秒resp.addCookie(cookie);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

7.4 Session (重点)

** 什么是Session: **

  • 服务器会给每一个用户(浏览器)创建一个Seesion对象。
  • 一个session独占一个浏览器,只要浏览器没有关闭,这个session就存在。
  • 用户登录之后,整个网站它都可以访问。(保存用户的信息;也可以保存购物车的信息)

Session **和cookie的区别 **

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)保存在 ** 客户端**** ; **
  • Session把用户的数据写到用户独占 Session 中, ** 服务器端 ** 保存(保存重要的信息,减少服务器资源的浪费)
  • Session对象由服务( sevice )创建;

使用场景

  • 保存一个登录用户的信息;
  • 购物车信息;
  • 在整个网站中经常会使用的数据,我们将它保存在 Session 中;

会话自动过期

<!--设置session默认的失效时间-->
<session-config><!--15分钟后session自动失效,以分钟为单位--><session-timeout>15</session-timeout>
</session-config>

Session的使用

public class SessionDemo01 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 解决中文乱码req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=utf-8");// 得到SessionHttpSession session = req.getSession();// 给session中存东西session.setAttribute("name", "张三");// 获取session的idString sessionId = session.getId();// 判断session是不是新创建if (session.isNew()) {resp.getWriter().write("session创建成功,ID:" + sessionId);} else {resp.getWriter().write("session已经存在了,ID:" + sessionId);}// session创建的时候做了什么事情/*Cookie cookie = new Cookie("JSESSIONID", sessionId);resp.addCookie(cookie);*///------------------// 从session中获取数据String name = (String) session.getAttribute("name");//------------------// 从session中删除指定name的数据session.removeAttribute("name");// 手动注销sessionsession.invalidate();}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

8. jsp

8.1 什么是jsp

Java Server Pages:Java服务端页面,和servlet一样,用于动态web技术
最大的特点:

  • 写jsp就像在写html
  • 区别:
    • html只给用户提供静态的数据
    • jsp页面中可以嵌入Java代码,为用户提供动态数据

8.2 jsp原理

思路:jsp是怎样执行的?

  • 代码层面没有任何问题
  • 服务器内部工作
    • tomcat中有一个work目录;IDEA中使用tomcat的会在IDEA的tomcat中生产一个work目录
  • 目录地址

我电脑上的地址:

C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2

发现页面会被转换成为一个Java类!

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!

JSP最终也会被转换成为一个Java类!

JSP本质上就是一个Servlet

// 初始化
public void _jspInit() {
}
// 销毁
public void _jspDestroy() {
}
// JSPservice
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
  1. 判断请求的方式
  2. 内置一些对象(9个)
final javax.servlet.jsp.PageContext pageContext;    	// 页面上下文
javax.servlet.http.HttpSession session = null;       	// session
final javax.servlet.ServletContext application;        	// applicationContext
final javax.servlet.ServletConfig config;            	// config
javax.servlet.jsp.JspWriter out = null;                	// out
final java.lang.Object page = this;                    	// page:当前
HttpServletRequest request;                            	// 请求
HttpServletResponse response;                        	// 响应
  1. 输出页面前增加的代码
response.setContentType("text/html;charset=UTF-8");// 设置响应的页面类型
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;
  1. 以上的内置对象可以在jsp页面中直接使用
  2. 原理图

画板

在JSP页面中;
只要是 JAVA代码就会原封不动的输出;
如果是 HTML 代码,就会被转换为:

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

这样的格式,输出到前端!

8.3 JSP基础语法

JSP作为java技术的一种应用,它拥有一些自己扩充的语法,Java所有语法它都支持!

  1. JSP 表达式
<%--jsp表达式
作用:用来将程序的输出,输出到客户端
<%= 变量或表达式%>
--%>
<%= new java.util.Date() %>
  1. JSP 脚本
<%--jsp脚本片段--%>
<%int sum = 0;for (int i = 0; i < 100; i++) {sum+=i;}out.print("<h1>sum="+sum+"</h1>");
%>
//--------------------------
<%--EL表达式:${变量} --%>
<%for (int i = 0; i < 3; i++) {
%>
<h1>Hello World! ${i}</h1>
<%}
%>
  1. jsp声明
<%!
static {
System.out.println("loading Servlet!");
}
private int globalVar =0;public void kuang(){System.out.println("进入了该方法!");
}
%>

JSP 声明会被编译到jsp生成java的类中,其他的会被生成到 _jspService 方法中

8.4 jsp指令

  1. 定制错误页面
<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp" %>

或者在web.xml定制全局的错误页面

  1. 包含头部和尾部

8.5 九大内置对象

  • PageContext 存东西
  • Request 存东西
  • Response
  • Session 存东西
  • Application(ServletContext) 存东西
  • Config(ServletConfig)
  • out
  • page
  • exception

存东西的区别

pageContext.setAttribute("name1","张三1"); 	// 保存的数据只在一个页面中有效
request.setAttribute("name2","张三2");		// 保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","张三3");		// 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","张三4");	// 保存的数据只在服务器中有效,从打开服务器到关闭服务器

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

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

8.6.1 JSP 标签(JSP Actions)
定义:

JSP 标准动作标签是 JSP 内置的一组 XML 风格的标签,用于控制页面行为、操作 JavaBean、引入资源等。

常见标签:
标签功能说明
<jsp:include>包含另一个资源(相当于动态 include)
<jsp:forward>请求转发到另一个资源
<jsp:useBean>创建或查找一个 JavaBean 实例
<jsp:setProperty>设置 JavaBean 属性
<jsp:getProperty>获取 JavaBean 属性值
示例代码:
<jsp:useBean id="user" class="com.example.User" scope="request"/>
<jsp:setProperty name="user" property="name" value="张三"/>
<jsp:getProperty name="user" property="name"/>
注意事项:
  • scope 可选值:page / request / session / application
  • 推荐与 EL 表达式配合使用,避免直接写 Java 脚本
8.6.2 EL 表达式(Expression Language)
定义:

EL 表达式是一种简化版的表达式语言,用来替代 JSP 中的 <% ... %><jsp:getProperty>,使页面更简洁、易读。

语法格式:
${expression}
EL 访问对象范围:
EL 对象作用域
${pageScope}page 作用域
${requestScope}request 作用域
${sessionScope}session 作用域
${applicationScope}application 作用域
示例:
欢迎用户:${user.name} <br>
当前访问路径:${pageContext.request.requestURI} <br>
请求方法:${pageContext.request.method}
EL 运算符:
类型示例
算术运算${5 + 3}
比较运算${age > 18}
逻辑运算${user != null && user.age > 20}
条件判断${score >= 60 ? '及格' : '不及格'}

8.6.3 JSTL 标签库(JSP Standard Tag Library)
定义:

JSTL 是一组标准的自定义标签库,提供通用功能如条件判断、循环、URL 处理、国际化等。

引入方式(在 JSP 页面顶部添加):
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
常用 JSTL 标签分类:
核心标签库(Core Tags)
标签功能
<c:set>设置变量
<c:out>输出内容(可转义 HTML)
<c:if>条件判断
<c:choose>/<c:when>/<c:otherwise>多条件分支
<c:forEach>循环遍历
<c:forTokens>拆分字符串循环
<c:url>构造 URL
<c:redirect>重定向
<c:import>导入外部 URL 内容

示例:

<c:set var="name" value="李四" />
<c:out value="${name}" escapeXml="true"/><c:if test="${age > 18}">成年人
</c:if>
<c:forEach items="${users}" var="user">${user.name} - ${user.age}<br>
</c:forEach>
格式化标签库(Fmt Tags)
标签功能
<fmt:formatDate>格式化日期时间
<fmt:parseDate>解析日期时间
<fmt:formatNumber>格式化数字、货币
<fmt:message>国际化消息显示

示例:

<fmt:formatDate value="${now}" pattern="yyyy-MM-dd HH:mm:ss" />
<fmt:formatNumber value="12345.6789" type="currency" currencySymbol="¥"/>
3. 函数标签库(Fn Tags)
函数功能
fn:length()获取集合/字符串长度
fn:contains()判断是否包含某子串
fn:split()分割字符串
fn:join()合并数组为字符串

示例:

<c:if test="${fn:contains(name, 'Tom')}">名字中包含 Tom
</c:if>
长度:${fn:length("Hello World")}

使用场景推荐做法
页面展示数据使用 EL 表达式
控制流程使用 JSTL 标签
避免 Java 脚本尽量不用 <% ... %>
MVC 分离JSP 只做展示,业务逻辑交给 Servlet 或 Controller
国际化支持使用 <fmt:message> 结合 properties 文件
安全输出使用 <c:out> 防止 XSS 攻击

8.6.4 面试高频问题(附参考答案)

Q1:EL 表达式中的 .[] 有什么区别?

A:. 用于访问属性名固定的对象字段;[] 可以动态传 key,也支持带特殊字符的属性名,例如:${map['user-name']}


Q2:JSTL 标签为什么比 Java 脚本更好?

A:因为 JSTL 是标签形式,更易于维护、阅读,且更适合美工或前端人员参与开发,同时避免了大量 Java 代码混杂在 HTML 中的问题。


Q3:如何防止 EL 表达式被忽略?

A:在 web.xml 中设置 JSP 默认启用 EL:

<jsp-config><jsp-property-group><url-pattern>*.jsp</url-pattern><el-ignored>false</el-ignored></jsp-property-group>
</jsp-config>

Q4:JSTL 的 <c:forEach> 和 Java 的 for 循环有什么不同?

A:<c:forEach> 是标签库实现的循环,底层由 Java 代码处理,但它是声明式的,适合嵌入 HTML 页面中使用,而 Java 的 for 是命令式的,用于后端逻辑。


8.6.5 斜体样式总结

在现代 JSP 开发中,应尽量避免使用 <% ... %> 脚本,而是使用 EL 表达式 + JSTL 标签库 来完成页面逻辑,这样不仅结构清晰,而且更符合 MVC 设计模式。

9. MVC三层架构

  • MVC 是什么?
  • 三层架构的组成(View、Controller、Model)
  • 各层职责划分
  • 示例说明
  • 面试高频问题与参考答案

MVC(Model-View-Controller)是一种经典的软件设计模式,广泛应用于 Web 应用开发中,尤其是 Java Web(Servlet + JSP)、Spring MVC 等框架。


9.1 什么是 MVC?

MVC 全称是 Model-View-Controller,它将应用程序分为三个核心部分:

层级英文名中文名职责
MModel模型处理数据、业务逻辑
VView视图用户界面(如 HTML 页面)
CController控制器接收请求,调用模型,返回视图

9.2 MVC 在 Java Web 中的三层架构详解

在实际开发中,我们通常把 MVC 扩展为 三层架构(3-tier architecture),分别是:

表现层(View / UI Layer)
  • 技术实现:JSP、HTML、CSS、JavaScript
  • 作用
    • 展示数据给用户
    • 接收用户输入
    • 不处理复杂逻辑,只负责展示和跳转

控制层(Controller Layer)
  • 技术实现:Servlet、Spring MVC 的 @Controller@RestController
  • 作用
    • 接收用户的 HTTP 请求
    • 调用 Service 层处理业务逻辑
    • 根据结果选择跳转的页面(Forward/Redirect)

示例代码(Servlet):

@WebServlet("/login")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String username = request.getParameter("username");String password = request.getParameter("password");UserService userService = new UserService();User user = userService.login(username, password);if (user != null) {request.setAttribute("user", user);request.getRequestDispatcher("welcome.jsp").forward(request, response);} else {response.sendRedirect("login.jsp");}}
}

业务逻辑层(Service Layer)
  • 技术实现:Java 类(如 UserServiceOrderService
  • 作用
    • 处理具体的业务规则(如登录验证、订单计算)
    • 调用 DAO 层获取或保存数据

示例代码:

public class UserService {private UserDao userDao = new UserDao();public User login(String username, String password) {return userDao.findByUsernameAndPassword(username, password);}
}

数据访问层(DAO Layer / Persistence Layer)
  • 技术实现:Java 类(如 UserDao),配合 JDBC、MyBatis、Hibernate 等
  • 作用
    • 与数据库交互(增删改查)
    • 封装底层 SQL 操作

示例代码:

public class UserDao {public User findByUsernameAndPassword(String username, String password) {// 连接数据库查询// 返回 User 对象}
}

9.3 MVC 分层结构

+---------------------+
|       Browser       |
+----------+----------+|| HTTP Requestv
+----------+----------+
|     Controller      | ←→ 调用 Service 层
+----------+----------+|| 调用 DAO 层v
+----------+----------+
|       Service       |
+----------+----------+|| 操作数据库v
+----------+----------+
|         DAO         |
+----------+----------+|| 数据库操作v
+----------+----------+
|       Database      |
+---------------------+

9.4 MVC 的优势

优点说明
分工明确各层职责清晰,便于团队协作
易于维护修改某一层不影响其他层
可扩展性强可以替换某一层而不影响整体架构
降低耦合各层之间通过接口通信,松耦合设计
提高复用性Service 层可被多个 Controller 复用

9.5 简单案例演示

场景:用户登录功能
View(login.jsp)
<form action="login" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br><input type="submit" value="登录">
</form>
Controller(LoginServlet)
@WebServlet("/login")
public class LoginServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String username = request.getParameter("username");String password = request.getParameter("password");UserService userService = new UserService();User user = userService.login(username, password);if (user != null) {request.setAttribute("user", user);request.getRequestDispatcher("welcome.jsp").forward(request, response);} else {response.sendRedirect("login.jsp");}}
}
Service(UserService.java)
public class UserService {private UserDao userDao = new UserDao();public User login(String username, String password) {return userDao.findByUsernameAndPassword(username, password);}
}
DAO(UserDao.java)
public class UserDao {public User findByUsernameAndPassword(String username, String password) {// 查询数据库并封装成 User 对象return user;}
}
View(welcome.jsp)
<h1>欢迎你,${user.username}</h1>

9.6 面试高频问题

Q1:MVC 和三层架构的区别是什么?

A:MVC 是一种设计模式,强调的是 用户交互流程;而三层架构是从 系统架构角度 划分的层次结构,两者结合后形成完整的 Web 应用结构。


Q2:为什么要在项目中使用 MVC?

A:为了实现 前后端分离、逻辑清晰、易于维护、提高开发效率。各层分工明确,便于多人协作开发。


Q3:Servlet 在 MVC 中属于哪一层?JSP 呢?

A:

  • Servlet 属于 Controller 层,用于接收请求和控制流程;
  • JSP 属于 View 层,用于展示数据和页面渲染。

Q4:如果不用 MVC,会有什么问题?

A:会出现大量业务逻辑混杂在 JSP 页面中,导致代码难以维护、不易测试、安全性差等问题。


Q5:如何保证各层之间的低耦合?

A:通过接口编程、依赖注入等方式,使各层之间仅依赖接口而非具体实现类。


10. 过滤器(Filter)

Filter(过滤器)是 Servlet 规范中的一种组件,可以对客户端的请求(request)和服务器的响应(response)进行拦截,实现一些公共处理逻辑,过滤数据。

  • 做通用处理:请求编码处理,处理中文乱码; 压缩响应数据;敏感词过滤;
  • 对所有请求都有作用: 权限控制, 登录验证; 日志记录;
  • 可以配置多个 Filter

** Filter开发步骤: **

  1. 导包
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<!--servlet依赖-->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<!--jsp依赖-->
<dependency><groupId>javax.servlet.jsp</groupId><artifactId>javax.servlet.jsp-api</artifactId><version>2.3.3</version><scope>provided</scope>
</dependency>
  1. 编写过滤器(自定义类实现 javax.servlet.Filter 接口)
public class CharaterEncodingFilter implements Filter {/*** 初始化:服务器启动的时候,就已经初始化了,随时等待过滤对象出现!* @param filterConfig* @throws ServletException*/@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("CharaterEncodingFilter初始化");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");response.setContentType("text/html;charset=utf-8");System.out.println("CharaterEncodingFilter执行前");// 让我们的请求继续走,如果不写,程序到这里就被拦截停止chain.doFilter(request,response);System.out.println("CharaterEncodingFilter执行后");}/*** 销毁:web服务器关闭的时候,过滤会被销毁*/@Overridepublic void destroy() {System.out.println("CharaterEncodingFilter销毁");}
}
  1. 在web.xml中配置Filter
<filter><filter-name>filter</filter-name><filter-class>com.sunyiwenlong.filter.CharaterEncodingFilter</filter-class>
</filter>
<filter-mapping><filter-name>filter</filter-name><!--只要是/servlet的任何请求,会经过这个过滤器 --><url-pattern>/servlet/*</url-pattern>
</filter-mapping>

11. 监听器

实现一个监听器的接口;(有N种监听器)

  1. 编写一个监听器
public class OnlineCountListener implements HttpSessionListener {/*** 创建session的监听:创建一个session就会触发一次这个事件。* sessionCreated 是 HttpSessionListener 接口中的一个回调方法,用于监听 HttpSession 对象被创建时的事件。* @param se*/@Overridepublic void sessionCreated(HttpSessionEvent se) {ServletContext context = se.getSession().getServletContext();Integer onlineCount = (Integer) context.getAttribute("OnlineCount");if (onlineCount==null){onlineCount = new Integer(1);}else {int count = onlineCount.intValue();onlineCount = new Integer(count+1);}context.setAttribute("OnlineCount",onlineCount);}/*** 销毁session监听* 销毁Session就会触发一次这个事件!* sessionDestroyed 是 HttpSessionListener 接口中的一个回调方法,每当一个 HttpSession 被销毁时就会自动调用这个方法。* @param se*/@Overridepublic void sessionDestroyed(HttpSessionEvent se) {ServletContext context = se.getSession().getServletContext();Integer onlineCount = (Integer) context.getAttribute("OnlineCount");if (onlineCount==null){onlineCount = new Integer(0);}else {int count = onlineCount.intValue();onlineCount = new Integer(count-1);}context.setAttribute("OnlineCount",onlineCount);}
}
  1. 在web.xml中注册监听器
<listener><listener-class>com.sunyiwenlong.listener.OnlineCountListener</listener-class></listener>
  1. 看情况是否使用

12. 过滤器、监听器常见应用

public class TestPanel {public static void main(String[] args) {// 新建一个窗口Frame frame = new Frame("中秋节快乐");frame.setLayout(null);// 设置窗口的布局// 新建一个面板Panel panel = new Panel(null);// 设置窗口的坐标长宽frame.setBounds(300,300,500,500);// 设置背景颜色frame.setBackground(new Color(0,0,255));// 设置面板的坐标长宽panel.setBounds(50,50,300,300);// 设置面板背景色panel.setBackground(new Color(0,255,0));// 把面板放到窗口中frame.add(panel);// 设置可见性 trueframe.setVisible(true);// 监听事件,监听关闭事件frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});}
}

http://www.hkcw.cn/article/SMwEKWbadU.shtml

相关文章

CodeTop之K个一组翻转链表

题目链接 25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 题目解析 算法原理 1> 计算出有多少个结点 2> 计算出我们需要翻转多少组: 结点数/k 组数 3> 每一组都进行k个数的头插 细节 1>使用newHead来组装反转后的结点组成的链表 2>使用…

Window Server 2019--07 PKI、SSL网站与邮件安全

了解PKI、SSL技术的核心原理掌握PKI架构服务器配置掌握证书管理与应用 公钥基础设施&#xff08;Public Key Infrastructure&#xff0c;PKI&#xff09;是一个完整的颁发、吊销、管理数字证书的系统&#xff0c;是支持认证、加密、完整性和可追究性服务的基础设施。PKI通过第…

BigemapPro 数据坐标转度分秒格式教程

有用户在使用BigemapPro时遇到这种情况&#xff1a;尽管已将坐标格式设置为度分秒&#xff0c;但数据属性表中的经纬度却依旧显示为十进制&#xff0c;这是什么原因呢&#xff1f; 遇到这种情况不要慌&#xff0c;只需通过新增字段并赋值为度分秒格式就可以解决。 操作步骤 1…

逻辑回归知识点

一、逻辑回归概念 逻辑回归(Logistic Regression)是一种广泛应用于分类问题的统计方法&#xff0c;尤其适用于二分类问题。 注意: 尽管名称中有"回归"二字&#xff0c;但它实际上是一种分类算法。 解决二分类的问题。 API&#xff1a;sklearn.linear_model.Logis…

【excel宏基础】“在第一格按下ctrl+下箭头跳到最后一格的过程没有被记录在代码中,导致录入信息的时,不能实现自动找到最后一格录入信息”问题解决方法之一

一、问题描述 需求&#xff1a;在“信息录入”表中输入姓名、部门、身份证&#xff0c;点击“确认”&#xff0c;使信息自动录入到信息汇总的“引用表”中。 问题&#xff1a;录制宏时&#xff0c;按照三的视频教程&#xff0c;在第一格按下ctrl下箭头跳到最后一格的过程没有…

《智能医学》征稿通知:7天可见刊,专科及以上可发表

香港科学出版社(Hong Kong Scientific Publishers Journals)是一家全球独立高质量的学术出版机构&#xff0c;遵循国际开放获取的出版(OA)原则。现已与科检易学术携手共同征集高质量文章。目前可出版来自高等学校、科研院所和企业的先进科技成果。包括理、工、农、医、经、管、…

2025.05.29【Network】多组学分析:网络互作图绘制

Customization Explore all the parameters offered by the igraph package to customize chart appearance. Layout algorithm Several layout algorithm are offered by the igraph package. Learn how to use them and what are the possibilities. 文章目录 Customizatio…

如何选择适合团队的项目管理工具

选择适合团队的项目管理工具需综合考虑团队规模、项目类型、使用便捷性、功能丰富性、成本预算等因素&#xff0c;其中团队规模的匹配度尤为重要&#xff0c;不同规模团队适用的项目管理工具也不尽相同&#xff0c;合适的工具能够有效提高团队协作效率。 一、团队规模与工具匹配…

基于ubuntu安装hadoop

前言 提起大数据&#xff0c;就会觉得很厉害&#xff0c;将众多的数据整合在一起&#xff0c;在有条理的呈现在屏幕前的我们。有时候可能会想到底是什么在支撑着大数据&#xff0c;大数据的出现&#xff0c;方便了我们日常生活中的方方面面。那这些海量的数据计算机是怎么存储和…

如何在线免费将音乐伴奏提取

一键分离人声与伴奏&#xff01;让音乐创作再无边界&#xff01;有时我们想要学习某首歌曲&#xff0c;需要将人声和伴奏进行分离&#xff0c;如何将音乐人声提取出来呢。 音乐分离工具&#xff1a;在线音乐人声提取 - 分离音频人声与伴奏 - iLoveOFD在线 在线音乐人声提取工…

使用SCSS实现随机大小的方块在页面滚动

目录 一、scss中的插值语法 二、方块在界面上滚动的动画 一、scss中的插值语法 插值语法 #{}‌ 是一种动态注入变量或表达式到选择器、属性名、属性值等位置的机制 .类名:nth-child(n) 表示需同时满足为父元素的第n个元素且类名为给定条件 效果图&#xff1a; <div class…

超高频 RFID 读写器(三格电子)

一、 功能概述 本文档是 SG-UHF80 系列超高频 RFID 读写器产品说明书&#xff0c;包含 SG-UHF80-485、 SG-UHF80-TCP &#xff0c;共两个产品。使用框图如下图所示。 1.1 产品功能 本系列产品用来读写超高频 RFID 标签&#xff0c;支持 Modbus_RTU/ModbusTCP 从站功能。 可实…

Java 微服务架构设计:服务拆分与服务发现的策略

Java 微服务架构设计&#xff1a;服务拆分与服务发现的策略 微服务架构作为一种热门的软件架构风格&#xff0c;在 Java 领域有着广泛的应用。它通过将系统拆分为一组小型服务来实现更灵活、可扩展的系统设计。在微服务架构中&#xff0c;服务拆分和服务发现是两个关键环节。本…

信号量的应用:利用信号量实现进程互斥

设置互斥信号量 下面进行详细解释 1. 信号量定义与初始化 semaphore mutex; mutex 1; // 初始化为1信号量定义&#xff1a;semaphore 是定义信号量的类型 &#xff0c;这里定义了一个名为 mutex 的信号量。信号量是一种用于实现进程同步与互斥的机制&#xff0c;本质上是一个…

多模态大模型:开启智能决策的新时代

想要掌握如何将大模型的力量发挥到极致吗&#xff1f;叶梓老师带您深入了解 Llama Factory —— 一款革命性的大模型微调工具。 1小时实战课程&#xff0c;您将学习到如何轻松上手并有效利用 Llama Factory 来微调您的模型&#xff0c;以发挥其最大潜力。 CSDN教学平台录播地址…

python模块和包

模块 Python模块(Module) 是一个Python文件&#xff0c;以.py结尾&#xff0c;模块能定义函数、类和变量&#xff0c;模块里也能包含可执行的的代码 每一个模块都能帮助我们快速的实现一些功能&#xff0c;比如实现和时间相关的功能就可以使用time模块&#xff0c;我们可以认…

《仿盒马》app开发技术分享-- 订单列表页(端云一体)

开发准备 上一节我们实现了订单详情的展示&#xff0c;但是我们的确认订单页面只在下单成功后才会出现供用户查看&#xff0c;现在我们要有一个常驻的入口让用户去随时查看自己的订单以及订单状态&#xff0c;订单状态分为多个&#xff0c;还需要给用户提供切换的功能 功能分…

【第3章 文本】3.3 文本的定位

文章目录 水平与垂直定位示例textAligntextBaseline 将文本居中文本的度量绘制坐标轴旁边的文本标签在圆弧周围绘制文本 水平与垂直定位 在canvas中使用 strokeText() 或 fillText() 绘制文本时&#xff0c;需要指定所绘文本的 X 和 Y 的坐标&#xff0c;然而&#xff0c;浏览…

C++哈希

一.哈希概念 哈希又叫做散列。本质就是通过哈希函数把关键字key和存储位置建立映射关系&#xff0c;查找时通过这个哈希函数计算出key存储的位置&#xff0c;进行快速查找。 上述概念可能不那么好懂&#xff0c;下面的例子可以辅助我们理解。 无论是数组还是链表&#xff0c;查…

Java中的设计模式实战:单例、工厂、策略模式的最佳实践

Java中的设计模式实战&#xff1a;单例、工厂、策略模式的最佳实践 在Java开发中&#xff0c;设计模式是构建高效、可维护、可扩展应用程序的关键。本文将深入探讨三种常见且实用的设计模式&#xff1a;单例模式、工厂模式和策略模式&#xff0c;并通过详细代码实例&#xff0…