JavaWeb:JSP概述及原理

news2024/11/18 20:45:28

1,JSP概述

JSP(全称:Java Server Pages):Java服务端页面。 是一种动态的网页技术,其中既可以定义 HTMLJSCSS等静态内容,还可以定义 Java代码的动态内容,也就是 JSP = HTML + Java。如下就是jsp代码:

<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>JSP,Hello World</h1>
        <%
        	System.out.println("hello,jsp~");
        %>
    </body>
</html>

上面代码 h1 标签内容是展示在页面上,而Java的输出语句是输出在idea的控制台。

那么,JSP能做什么呢?现在我们只用 servlet 实现功能,看存在什么问题。如下图所示,当我们登陆成功后,需要在页面上展示用户名

在这里插入图片描述

上图的用户名是动态展示,也就是谁登陆就展示谁的用户名。只用 servlet 如何实现呢?在今天的资料里已经提供好了一个 LoginServlet ,该 servlet 就是实现这个功能的,现将资料中的 LoginServlet.java 拷贝到 request-demo 项目中来演示。接下来启动服务器并访问登陆页面

在这里插入图片描述

输入了 zhangsan 用户的登陆信息后点击 登陆 按钮,就能看到如下图效果

在这里插入图片描述

当然如果是 lisi 登陆的,在该页面展示的就是 lisi,欢迎您,动态的展示效果就实现了。那么 LoginServlet 到底是如何实现的,我们看看它里面的内容

在这里插入图片描述

上面的代码有大量使用到 writer 对象向页面写标签内容,这样我们的代码就显得很麻烦;将来如果展示的效果出现了问题,排错也显得有点力不从心。而JSP是如何解决这个问题的呢?在资料中也提供了一个 login.jsp 页面,该页面也能实现该功能,现将该页面拷贝到项目的 webapp下,需要修改 login.html 中表单数据提交的路径为下图

在这里插入图片描述

重新启动服务器并进行测试,发现也可以实现同样的功能。那么 login.jsp 又是如何实现的呢?那我们来看看 login.jsp 的代码

在这里插入图片描述

上面代码可以看到里面基本都是 HTML 标签,而动态数据使用Java代码进行展示;这样操作看起来要比用 servlet 实现要舒服很多。

JSP作用:简化开发,避免了在Servlet中直接输出HTML标签。

2,JSP快速入门

接下来我们做一个简单的快速入门代码。

2.1 搭建环境

创建一个mavenweb项目,项目结构如下:

在这里插入图片描述

pom.xml 文件内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dcxuexi</groupId>
  <artifactId>jsp-demo</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

2.2 导入JSP依赖

dependencies 标签中导入JSP的依赖,如下

<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
</dependency>

该依赖的 scope 必须设置为 provided,因为tomcat中有这个jar包了,所以在打包时我们是不希望将该依赖打进到我们工程的war包中。

2.3 创建jsp页面

在项目的 webapp 下创建jsp页面

在这里插入图片描述

通过上面方式创建一个名为 hello.jsp 的页面。

2.4 编写代码

hello.jsp 页面中书写 HTML 标签和 Java 代码,如下

<%--
  Created by IntelliJ IDEA.
  User: DongChuang
  Date: 2023/1/2
  Time: 16:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>hello jsp ...</h1>
<%
    System.out.println("hello jsp ...");
%>
</body>
</html>

2.5 测试

启动服务器并在浏览器地址栏输入 http://localhost:8080/jsp-demo/hello.jsp,我们可以在页面上看到如下内容

在这里插入图片描述

在这里插入图片描述

同时也可以看到在 idea 的控制台看到输出的 hello jsp ... 内容。

3,JSP原理

我们之前说JSP就是一个页面,那么在JSP中写 html 标签,我们能理解,但是为什么还可以写 Java 代码呢?

因为 JSP本质上就是一个Servlet 接下来我们聊聊访问jsp时的流程

在这里插入图片描述

  1. 浏览器第一次访问 hello.jsp 页面
  2. tomcat 会将 hello.jsp 转换为名为 hello_jsp.java 的一个 Servlet
  3. tomcat 再将转换的 servlet 编译成字节码文件 hello_jsp.class
  4. tomcat 会执行该字节码文件,向外提供服务

我们可以到项目所在磁盘目录下找 target\tomcat\work\Tomcat\localhost\jsp-demo\org\apache\jsp 目录,而这个目录下就能看到转换后的 servlet

在这里插入图片描述

打开 hello_jsp.java 文件,来查看里面的代码

在这里插入图片描述

由上面的类的继承关系可以看到继承了名为 HttpJspBase 这个类,那我们在看该类的继承关系。

在这里插入图片描述

可以看到该类继承了 HttpServlet ;那么 hello_jsp 这个类就间接的继承了 HttpServlet ,也就说明 hello_jsp 是一个 servlet

继续阅读 hello_jsp 类的代码,可以看到有一个名为 _jspService() 的方法,该方法就是每次访问 jsp 时自动执行的方法,和 servlet 中的 service 方法一样 。

而在 _jspService() 方法中可以看到往浏览器写标签的代码:

在这里插入图片描述

以前我们自己写 servlet 时,这部分代码是由我们自己来写,现在有了 jsp 后,由tomcat完成这部分功能。

4,JSP脚本

JSP脚本用于在JSP页面内定义Java代码。在之前的入门案例中我们就在JSP页面定义的Java代码就是JSP脚本。

4.1 JSP脚本分类

JSP脚本有如下三个分类:

  • <% … %>:内容会直接放到_jspService()方法之中
  • <%= … %>:内容会放到out.print()中,作为out.print()的参数
  • <%! … %>:内容会放到_jspService()方法之外,被类直接包含

代码演示:

hello.jsp 中书写

<%
    System.out.println("hello,jsp~");
    int i = 3;
%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,i 变量定义在了 _jspService() 方法中

在这里插入图片描述

hello.jsp 中书写

<%="hello"%>
<%=i%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了 out.print() 中,作为参数

在这里插入图片描述

hello.jsp 中书写

<%!
    void  show(){}
	String name = "zhangsan";
%>

通过浏览器访问 hello.jsp 后,查看转换的 hello_jsp.java 文件,该脚本的内容被放在了成员位置

在这里插入图片描述

4.2 案例

4.2.1 需求

使用JSP脚本展示品牌数据

在这里插入图片描述

说明:

  • 在该案例中数据不从数据库中查询,而是在JSP页面上写死

4.2.2 实现

  • 在项目的 webapp 中创建 brand.jspbrand.jsp 内容如下

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <input type="button" value="新增"><br>
    <hr>
        <table border="1" cellspacing="0" width="800">
            <tr>
                <th>序号</th>
                <th>品牌名称</th>
                <th>企业名称</th>
                <th>排序</th>
                <th>品牌介绍</th>
                <th>状态</th>
                <th>操作</th>
    
            </tr>
            <tr align="center">
                <td>1</td>
                <td>三只松鼠</td>
                <td>三只松鼠</td>
                <td>100</td>
                <td>三只松鼠,好吃不上火</td>
                <td>启用</td>
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
    
            <tr align="center">
                <td>2</td>
                <td>优衣库</td>
                <td>优衣库</td>
                <td>10</td>
                <td>优衣库,服适人生</td>
                <td>禁用</td>
    
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
    
            <tr align="center">
                <td>3</td>
                <td>小米</td>
                <td>小米科技有限公司</td>
                <td>1000</td>
                <td>为发烧而生</td>
                <td>启用</td>
    
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
        </table>
    </body>
    </html>
    

    现在页面中的数据都是假数据。

  • brand.jsp 中准备一些数据

    <%
        // 查询数据库
        List<Brand> brands = new ArrayList<Brand>();
        brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
        brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
        brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
    %>
    

    注意: 这里的类是需要导包的

  • brand.jsp 页面中的 table 标签中的数据改为动态的

    <table border="1" cellspacing="0" width="800">
        <tr>
            <th>序号</th>
            <th>品牌名称</th>
            <th>企业名称</th>
            <th>排序</th>
            <th>品牌介绍</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
        <%
         for (int i = 0; i < brands.size(); i++) {
             //获取集合中的 每一个 Brand 对象
             Brand brand = brands.get(i);
         }
        %>
        <tr align="center">
            <td>1</td>
            <td>三只松鼠</td>
            <td>三只松鼠</td>
            <td>100</td>
            <td>三只松鼠,好吃不上火</td>
            <td>启用</td>
            <td><a href="#">修改</a> <a href="#">删除</a></td>
        </tr>
    </table>
    

    上面的for循环需要将 tr 标签包裹起来,这样才能实现循环的效果,代码改进为

    <table border="1" cellspacing="0" width="800">
        <tr>
            <th>序号</th>
            <th>品牌名称</th>
            <th>企业名称</th>
            <th>排序</th>
            <th>品牌介绍</th>
            <th>状态</th>
            <th>操作</th>
        </tr> 
        <%
         for (int i = 0; i < brands.size(); i++) {
             //获取集合中的 每一个 Brand 对象
             Brand brand = brands.get(i);
        %>
        	 <tr align="center">
                <td>1</td>
                <td>三只松鼠</td>
                <td>三只松鼠</td>
                <td>100</td>
                <td>三只松鼠,好吃不上火</td>
                <td>启用</td>
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
        <%
         }
        %>  
    </table>
    

    注意:<%%>里面写的是 Java代码,而外边写的是HTML标签

    上面代码中的 td 标签中的数据都需要是动态的,所以还需要改进

    <table border="1" cellspacing="0" width="800">
        <tr>
            <th>序号</th>
            <th>品牌名称</th>
            <th>企业名称</th>
            <th>排序</th>
            <th>品牌介绍</th>
            <th>状态</th>
            <th>操作</th>
        </tr>
        <%
         for (int i = 0; i < brands.size(); i++) {
             //获取集合中的 每一个 Brand 对象
             Brand brand = brands.get(i);
        %>
        	 <tr align="center">
                <td><%=brand.getId()%></td>
                <td><%=brand.getBrandName()%></td>
                <td><%=brand.getCompanyName()%></td>
                <td><%=brand.getOrdered()%></td>
                <td><%=brand.getDescription()%></td>
                <td><%=brand.getStatus() == 1 ? "启用":"禁用"%></td>
                <td><a href="#">修改</a> <a href="#">删除</a></td>
            </tr>
        <%
         }
        %>
    </table>
    

4.2.3 成品代码

<%@ page import="com.dcxuexi.pojo.Brand" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
    // 查询数据库
    List<Brand> brands = new ArrayList<Brand>();
    brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
    brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
    brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<input type="button" value="新增"><br>
<hr>
<table border="1" cellspacing="0" width="800">
    <tr>
        <th>序号</th>
        <th>品牌名称</th>
        <th>企业名称</th>
        <th>排序</th>
        <th>品牌介绍</th>
        <th>状态</th>
        <th>操作</th>
    </tr>
    <%
        for (int i = 0; i < brands.size(); i++) {
            Brand brand = brands.get(i);
    %>

    <tr align="center">
        <td><%=brand.getId()%></td>
        <td><%=brand.getBrandName()%></td>
        <td><%=brand.getCompanyName()%></td>
        <td><%=brand.getOrdered()%></td>
        <td><%=brand.getDescription()%></td>
		<td><%=brand.getStatus() == 1 ? "启用":"禁用"%></td>
        <td><a href="#">修改</a> <a href="#">删除</a></td>
    </tr>
    <%
        }
    %>
</table>
</body>
</html>

4.2.4 测试

在浏览器地址栏输入 http://localhost:8080/jsp-demo/brand.jsp ,页面展示效果如下

在这里插入图片描述

4.3 JSP缺点

通过上面的案例,我们可以看到JSP的很多缺点。

由于JSP页面内,既可以定义HTML标签,又可以定义 Java代码,造成了以下问题:

  • 书写麻烦:特别是复杂的页面

    既要写HTML标签,还要写Java代码

  • 阅读麻烦

    上面案例的代码,相信你后期再看这段代码时还需要花费很长的时间去梳理

  • 复杂度高:运行需要依赖于各种环境,JREJSP容器,JavaEE ···

  • 占内存和磁盘:JSP会自动生成.java.class文件占磁盘,运行的是.class文件占内存

  • 调试困难:出错后,需要找到自动生成的.java文件进行调试

  • 不利于团队协作:前端人员不会Java,后端人员不精HTML

    如果页面布局发生变化,前端工程师对静态页面进行修改,然后再交给后端工程师,由后端工程师再将该页面改为JSP页面

由于上述的问题, JSP已逐渐退出历史舞台, 以后开发更多的是使用 HTML + Ajax 来替代。Ajax是我们后续会重点介绍。有个这个技术后,前端工程师负责前端页面开发,而后端工程师只负责前端代码开发。下来对技术的发展进行简单的说明

在这里插入图片描述

  1. 第一阶段:使用 servlet 即实现逻辑代码编写,也对页面进行拼接。这种模式我们之前也接触过

  2. 第二阶段:随着技术的发展,出现了 JSP ,人们发现 JSP 使用起来比 Servlet 方便很多,但是还是要在 JSP 中嵌套 Java 代码,也不利于后期的维护

  3. 第三阶段:使用 Servlet 进行逻辑代码开发,而使用 JSP 进行数据展示

    在这里插入图片描述

  4. 第四阶段:使用 servlet 进行后端逻辑代码开发,而使用 HTML 进行数据展示。而这里面就存在问题,HTML 是静态页面,怎么进行动态数据展示呢?这就是 ajax 的作用了。

那既然JSP已经逐渐的退出历史舞台,那我们为什么还要学习 JSP 呢?原因有两点:

  • 一些公司可能有些老项目还在用 JSP ,所以要求我们必须动 JSP
  • 我们如果不经历这些复杂的过程,就不能体现后面阶段开发的简单

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/135572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javaee之SpringMVC2

SpringMVC返回值类型以及响应数据类型 1.搭建环境 还是按照springMVC1中的搭建环境进行搭建。这里就不多说。 响应之返回值是String类型 我们先来创建一个User类 User,java package com.pxx.domain;import java.io.Serializable;public class User implements Serializab…

PS 矩形选区工具(1)基本用法 生成图层 选区方式演示讲解

我们先打开PS 然后打开一个项目 我们可以选择一个图层 然后 点击左上角 图像>调整>色相.饱和度 弹出操作框之后 我们拉动色相的色条 对应视图就会发生主体颜色的变化 然后 我们打开一个只有一个图层的图片项目 我们对这个图层操作 整个都会变化 但如果我只是想改其中…

后悔升级iPhone?教你如何把iOS15降回iOS14

还在使用betabeta版iOS 15和iPadOS 15吗&#xff1f;如果你出于某种原因准备返回稳定的iOS 14&#xff0c;本篇文章将会为你详细介绍如何从 iOS 15 beta版降级到 iOS 14&#xff0c;这对于有一定动手能力的人来说并不难。 如何从 iOS 15 beta版降级到 iOS 14 重要提示&#xf…

Spring是怎么回事?新手入门就看这篇吧

前言 今天壹哥给大家介绍一套开源的轻量级框架&#xff0c;它就是Spring。在给大家详细讲解Spring框架之前&#xff0c;壹哥先给大家介绍Spring框架的主要内容&#xff1a; Spring的基本概念 Spring核心思想之ioc Spring核心思想之aop Spring框架对事务的支持 在本系列文章…

解决前端如何使用插件crypto-js进行AES加密方式数据加密

一、问题 目录 一、问题 1.1 问题概述 1.2 操作过程描述 二、解决 2.1 说明 2.2 crypto-js安装 2.3 使用crypto-js 1.1 问题概述 如何进行加密和解密以及采用什么方式进行加密解密是本文主要解决的内容~ 之前有小伙伴问了关于加密解密的事&#xff0c;确实是的&#xff…

pdfbox / XSL + FOP 转换 PDF文档

XSL-FO是XSL Formatting Objects的缩写&#xff0c;它是一种用于文档格式的XML 置标语言。XSL-FO是XSL的一部分&#xff0c;而XSL是一组定义XML数据转换与格式的W3C技术。XSL的其他部分有XSLT与XPath。 XSL-FO是用于格式化XML数据的语言&#xff0c;全称为Extensible Styleshe…

python基础讲解 02

人工智能学习基础四剑客&#xff08;库&#xff09;Numpy ( Numerical Python)使用创建随机数组查看数组的属性数组与标量之间的计算四剑客&#xff08;库&#xff09; Python被大量应用在数据挖掘和深度学习领域,其中使用极其广泛的是Numpy&#xff08;N维数组对象和向量运算&…

Nessus学习

攻击主机&#xff1a; Kali 192.168.11.106靶机&#xff1a;windows server 2008 r2 192.168.11.134 x64 32位nessus实验原理&#xff1a;利用漏洞扫描器能够自动应用漏洞扫描原理&#xff0c;对目标主机安全漏洞进行检测&#xff0c;附带识别主机漏洞的特征库的功能&#xf…

使用客户端证书登录MySQL

使用客户端证书登录MySQL登录MySQL具有安全性高、不用输入密码的优点&#xff0c;这里说明生成证书和登录的过程。 实验环境是Linux上的的MySQL 8.0.31社区版。 生成证书 使用openssl req创建X.509证书&#xff0c;下面的命令创建有效期10年的私钥&#xff0c;使用man req可…

Kali Linux中shutdown指令的用法3-3

3 TIME介绍 TIME是shutdown指令的第二个参数&#xff0c;用来表示实现关机计划的时间&#xff0c;如果不指定TIME&#xff0c;则默认是1分钟之后实现关机计划。 3.1 通过hh:mm格式指定时间 可以通过hh:mm格式指定关机的具体时间&#xff0c;其中hh表示小时&#xff0c;mm表示…

CredSSP加密数据库修正

新部署的机器&#xff0c;远程登录时&#xff0c;提示如下&#xff1a; 原因是注册表里面缺少东西&#xff0c;新建一个文本文件&#xff0c;复制如下内容保存&#xff0c;然后将保存的文本文件保存为后缀为reg的可执行文件 Windows Registry Editor Version 5.00[HKEY_LOCAL_…

【Django项目开发】权限相关的模型设计(五)

文章目录一、什么是权限二、RBAC模型三、权限模型类设计1、需要设计那些字段2、特别注意四、角色模型类设计1、需要定义的字段有2、 多对多模型类设计(重点)五、用户模型类设计(前面已经设计好了)六、菜单模型类设计(前面已经设计好了)七、总结:上面4个表的关联关系为:如下八、…

Spring注解详解(使用注解的方式完成IOC)

补充&#xff1a;xml配置 最开始(Spring 1.x)&#xff0c;Spring都是通过xml配置控制层(controller)--业务逻辑层(service)--dao层--数据源的关系&#xff0c;但是比较复杂 Spring 2.x的时候&#xff0c;随着JDK1.5支持注解的方式&#xff0c;实现了 "xml 注解" 的开…

Vue自定义指令,自定义插件,过滤器,混入,nextTick

自定义指令&#xff1a;自己定义类似指令的技术。V-开关的特殊属性&#xff0c;是一个对象&#xff0c;自己定义其作用。 指令: v-特殊属性 * vue内置指令: v-html v-text v-pre * v-bind v-on v-if v-show v-for …

我的前端学习经历

我最近在开发一个NFT相关的Saas&#xff0c;部分截图如下&#xff1a;这是我一段时间前&#xff0c;朋友圈发的图&#xff0c;现在Saas在页面上有点变化&#xff0c;但懒得再截图了。客观而言&#xff0c;布局还可以&#xff0c;这一套的技术栈是&#xff1a;React TailwindCs…

​无线数据终端DTU的电路防护元器件推荐产品型号

​无线数据终端DTU是专门用于将串行数据转换为IP数据或将IP数据转换为串行数据通过无线通信网络传输的无线终端设备&#xff0c;因此对于它来说ESD静电放电及雷击浪涌的防护显得尤其重要。 DTU已广泛应用于电力、环保、LED信息发布、物流、水文、气象等行业&#xff0c;其硬件…

【人工智能原理自学】方差代价函数:知错

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。 &#x1f514;本文讲解一元一次函数感知器&#xff1a;如何描述直觉&#xff0c;一起卷起来叭&#xff01; 目录…

[oeasy]python0037_字符画艺术_asciiview_自制小动物_imagick_asciiart

牛说(cowsay) 回忆上次内容 我们狂飙了一路 从用shell 直接执行 python程序到用shell 循环执行 python程序 循环体中 把 python的 输出结果 用管道 交给了 figlet 把 figlet的 输出结果 用管道 交给了 cowsay 把 cowsay的 输出结果 用管道 交给了 lolcat 最后 提权 直接运行 s…

课程设计 | 学生成绩管理系统

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

减小 Go 代码编译后的二进制体积

1 基线用例 减小编译后的二进制的体积&#xff0c;能够加快程序的发布和安装过程。接下来呢&#xff0c;我们分别从编译选项和第三方压缩工具两方面来介绍如何有效地减小 Go 语言编译后的体积。 我们采用同一个测试工程来测试不同方式的效果。 使用的测试工程如下&#xff0…