SSM 三大框架原理、核心技术,运行流程讲解

news2024/11/27 5:37:29

作者:arrows
来源:https://www.cnblogs.com/arrows/p/10537733.html

一、Spring部分

1、 Spring的运行流程

·第一步:加载配置文件ApplicationContext ac = new
ClassPathXmlApplicationContext(“beans.xml”);

,ApplicationContext接口,它由BeanFactory接口派生而来,因而提供了BeanFactory所有的功能。配置文件中的bean的信息是被加载在HashMap中的,一个bean通常包括,id,class,property等,bean的id对应HashMap中的Key,value就是bean。

具体如何加载,源码如下:

if (beanProperty.element("map") != null){  
Map<String, Object> propertiesMap = new HashMap<String, Object>();  
Element propertiesListMap = (Element)beanProperty.elements().get(0);  
Iterator<?> propertiesIterator = propertiesListMap .elements().iterator();  
while (propertiesIterator.hasNext()) {  
    Element vet = (Element) propertiesIterator.next();  
    if(vet.getName().equals("entry")) {  
        String key = vet.attributeValue("key");  
        Iterator<?> valuesIterator = vet.elements()  .iterator();  
        while (valuesIterator.hasNext()) {  
            Element value = (Element) valuesIterator.next();  
            if (value.getName().equals("value")){  
                propertiesMap.put(key, value.getText());  
            }  
            if (value.getName().equals("ref")) {  
                propertiesMap.put(key, new String[]{
                        value.attributeValue("bean") 
                        });  
            }  
        }  
    }  
}  
bean.getProperties().put(name, propertiesMap);  
//看完反正我是默默放弃了。。。
} 

bean加载进HashMap中

·第二步:调用getBean方法,getBean是用来获取applicationContext.xml文件里的bean的,()写的是bean的id。一般情况下都会强转换成对应的业务层(接口)。例如SpringService
springService = (SpringService)ac.getBean(“Service”);

·第三步:这样我们就可以调用业务层(接口实现)的方法。

具体如下:

java反射博大精深,具体请看:

https://blog.csdn.net/sinat_38259539/article/details/71799078

那么bean中的东西到底是怎么注入进去的?简单来讲,就是在实例化一个bean时,实际上就实例化了类,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。这样就回到了我们java最原始的地方,对象.方法,对象.属性。

2、 Spring的原理

·什么是Spring?

·Spring是一个容器框架,它可以接管web层,业务层,dao层,持久层的各个组件,并且可以配置各种bean,并可以维护bean与bean的关系,当我们需要使用某个bean的时候,我们可以直接getBean(id),使用即可。

·Spring目的

就是让对象与对对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的(Spring根据这些配置,内部通过反射去动态的组装对象),Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。

·层次架构图

说明

·web层 :Struts充当web层,接管jsp,action,表单,主要体现出MVC的数据输入,数据的处理,数据的显示分离。

·model层 :model层在概念上可以理解为包含了业务层,dao层,持久层,需要注意的是,一个项目中,不一定每一个层次都有;

·持久层 :体现oop,主要解决关系模型和对象模型之间的阻抗。

3、Spring的核心技术

·IOC

·IOC(Inverse of
control)控制反转:所谓反转就是把创建对象(bean)和维护对象(bean)之间的关系的权利从程序转移到spring的容器(spring-config.xml)。

说明:

这对标签元素的作用:当我们加载spring框架时,spring就会自动创建一个bean对象,并放入内存。相当于我们常规的new一个对象,而中的value则是实现了“对象.set方法”,这里也体现了注入概念。

·DI

· DI(dependency
injection)依赖注入:实际上di和ioc是同一个概念,spring的设计者,认为di更准确的表示spring的核心;

·
spring提倡接口编程,在配合di技术就可以达到层与层解耦的目的,为什么呢?因为层与层之间的关联,由框架帮我们做了,这样代码之间的耦合度降低,代码的复用性提高。

·
接口编程的好处请访问Java中接口编程的好处以及实现方式的选择?

·AOP

·aspect oriented programming (面向切面编程);

·核心 :在不增加代码的基础上,还增加新功能;

·理解
:面向切面:其实是,把一些公共的“东西”拿出来,比如说,事务,安全,日志,这些方面,如果你用的到,你就引入。也就是说:当你需要在执行一个操作(方法)之前想做一些事情(比如开启事务,记录日志等等),那你就用before,如果想在操作之后做点事情(比如,关闭一些连接等等),那你就用after。其他类似。

二、Spring MVC部分

1、spring MVC框架

架构执行流程 (面试必问):

·1、 用户发送请求至前端控制器DispatcherServlet;

·2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器;

·3、 处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;

·4、 DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;

·5、 执行处理器(Controller,也叫后端控制器);

·6、 Controller执行完成返回ModelAndView;

·7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet;

·8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器;

·9、 ViewReslover解析后返回具体View;

·10、 DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中);

·11、 DispatcherServlet响应用户。

2、Spring MVC的原理

·1、什么是SpringMVC

· springmvc是spring框架的一个模板,springmvc和spring无需通过中间整合层进行整合;

· springmvc是一个基于mvc的web框架。

·mvc

·mvc在b/s系统下的应用:

· 前端控制器DispatcherServlet(不需要程序员开发)

·作用:接受请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。

· 处理器映射器HandlerMapping(不需要程序员开发)

·作用:根据请求的url查找Handler

· 处理器适配器HandlerAdapter

·作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler

· 处理器Handler(需要程序员开发)

·注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以正确执行Handler。

· 视图解析器View resolver(不需要程序员开发)

·作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)

· 视图view(需要程序员开发)

·View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

·Struts2与springmvc的区别?

1、
Struts2是类级别的拦截,一个类对应一个request上下文,springmvc是方法级别的拦截器,一个方法对应一个request上下文,而方法同时又跟一个URL对应,所以说从框架本身上Springmvc就容易实现restful
url,而Struts2的框架实现起来要费劲,因为Struts2中Action的一个方法对应一个URL,而其属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。

2、 由上边原因,springmvc的方法之间基本上独立的,独享request
response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量共享,这不会影响程序运行,却给我们编码读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。

3、
由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。

4、
拦截器实现机制上,Struts2有以自己的interceptor机制,springmvc用的是独立的AOP方式,这样导致Struts2的配置文件量还是比springMVC大。

5、
springmvc的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。

6、
springmvc集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回相应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码进去,使用起来也相对不方便。

7、 springmvc验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。

8、
springmvc和spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到springmvc一样的效果,但是需要xml配置的地方不少)。

9、 设计思想上,Struts2更加符合OOP的编程思想,springmvc就比较谨慎,在servlet上扩展。

10、 springmvc开发效率和性能高于Struts2.

11、 springMVC可以认为已经100%零配置。

3、spring MVC的核心技术

·注解开发 (@Controller,@ResponseMapping,@ResponseBody…)

·还有spring的诸多注解,这两者是不需要整合的

·传参,接参(request)

·基本配置

·文件上传与下载

·spring MVC中文件上传需要添加Apache Commons FileUpload相关的jar包;

·基于该jar,spring中提供了MultipartResolver实现类:CommonsMutipartResolver

·拦截器

· 其实最核心的还是springMVC的执行流程,哥哥点的作用得搞清楚。

三、Mybatis部分

1、Mybatis的运行流程:

·Mybatis运行流程图:

1、 :配置mybatis.xml文件,大体如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 引入外部文件
        resource:引入项目中配置文件
        url:引入网络中或者路径文件
    -->
    <properties resource="jdbc.properties"/>
    <settings>
        <!--<setting name="mapUnderscoreToCamelCase" value="true" />-->
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="aggressiveLazyLoading"  value="false" />
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.nuc.entity"></package>
    </typeAliases>
    <!-- - - - - - - 数据库环境配置- - - - - - - - - -->
    <environments default="environments">
        <environment id="environments">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.jdbcUrl}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- - - - - - - -映射文件路径- - - - - - -->
    <mappers>
        <!--自动扫描包下的映射文件,要求:同名,同目录-->
        <package name="com.nuc.mapper" />
    </mappers>
</configuration>

mybatis.xml

2、 :加载我们的xml文件;

3、 :创建SqlSessionFactoryBuilder;

4、 :创建SqlSessionFactory;

5、 :调用openSession(),开启sqlSession;

6、 :getMapper()来获取我们的mapper(接口),mapper对应的映射文件,在加载mybatis.xml时就会加载;

7、 :使用我们自己的mapper和它对应的xml来完成我们和数据库的交互。即增删改查。

8、 :提交session,关闭session。

代码如下:

String resource = "mybatis-config.xml";
SqlSession sqlSession = null;
InputStream inputStream = Resources.getResourceAsStream(resource);//读取mybatis配置文件
//SqlSessionFactoryBuilder这个类的作用就是为了创建SqlSessionFactory的
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
/**
 *  factory.openSession(); //需手动提交事务
 *   factory.openSession(true); //系统自动提交事务
 */
sqlSession = factory.openSession();
CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);
//增删改查的操作
sqlSession.commit();//如果没有提交,数据库的数据不会改变
sqlSession.close();

需要注意的是,sqlSession也自带一些数据交互的操作。

2、MyBatis的原理

·什么是Mybatis?

·
mybatis专注sql本身,需要程序员自己编写sql语句,sql修改,优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输入映射、输出映射);

· mybatis是一个持久层的框架,是apache下的顶级项目;

· mybatis托管到googlecode下,后来托管到github下:mybatis
github地址

·
mybatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。

· mybatis可以将向preparedStatement中的属兔参数自动进行输入映射,将查询结果集灵活映射成Java对象。(输出映射)

·mybatis底层实现

·
mybatis底层还是采用原生jdbc来对数据库进行操作的,只是通过SqlSessionFactory,SqlSession,Executor,StatementHandler,ParameterHandler,ResultHandler和TypeHandler等几个处理器封装了这些过程。

·对原生态jdbc程序(单独使用jdbc开发)问题总结:

·1、 数据库连接,使用时创建,不用就关闭,对数据库进行频繁连接开启和关闭,造成数据库资源的浪费;

解决:使用数据库连接池管理数据库连接。

2、 将sql语句硬编码到Java代码中,如果sql语句修改,需要对Java代码重新编译,不利于系统维护;

解决:将sql语句设置在xml配置文件中,即使sql变化,也无需重新编译。

3、 向preparedStatement中设置参数,对占位符位置和设置参数值,硬编码到Java文件中,不利于系统维护;

解决:将sql语句及占位符,参数全部配置在xml文件中。

4、 从resultSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护;

解决:将查询的结果集,自动映射成Java对象。

·mybatis工作原理:

·
mybatis通过配置文件创建sqlSessionFactory,sqlSessionFactory根据配置文件,配置文件来源于两个方面:一个是xml,一个是Java中的注解,获取sqlSession。SqlSession包含了执行sql语句的所有方法,可以通过SqlSession直接运行映射的sql语句,完成对数据的增删改查和事物的提交工作,用完之后关闭SqlSession。

3、Mybatis的核心技术:

·Mybatis输入映射:

· 通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo包装类型;

·Mybatis输出映射:

·一、resultType

· 作用:将查询结果按照sql列名、pojo属性名一致性映射到pojo中;

· 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功;

· 如果查询出来的列名和pojo中的属性名全部不一致,则不会创建pojo对象;

· 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象;

· 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

·二、resultMap

· 使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。

·association:

· 作用:将关联查询信息映射到一个pojo对象中;

· 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。

·
使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。

·collection:

· 作用:将关联查询信息映射到一个list集合中;

·
场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样做的目的也是方便对查询结果集进行遍历查询。如果使用resultType将无法将查询结果映射到list集合中。

·Mybatis的动态sql

·什么是动态sql?

· mybatis核心:对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装;

· 包括,where,if,foreach,choose,when,otherwise,set,trim等标签的使用。

·数据模型分析思路:

·1、每张表记录的数据内容

· 分模块对每张表记录的内容进行熟悉,相当于你学习系统需求(功能)的过程;

2、每张表重要的字段设置

· 非空字段、外键字段

·3、数据库级别表与表之间的关系

· 外键关系

·4、表与表之间的业务关系

· 在分析表与表之间的业务关系时一定要建立在某个业务意义基础上去分析。

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

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

相关文章

存储卡目录变成未知文件?这些技巧能让你恢复数据!

当存储卡的目录变成未知文件时&#xff0c;我们无法直接访问存储卡中的数据。但是&#xff0c;这并不意味着这些数据永远无法恢复。以下是几种可能恢复存储卡数据的方法&#xff1a; 使用数据恢复软件。从互联网上下载并安装专业的数据恢复软件这些软件可以扫描存储卡&#xf…

分布式接口幂等性设计实现

面对分布式架构和微服务复杂的系统架构和网络超时服务器异常等带来的系统稳定性问题&#xff0c;分布式接口的幂等性设计显得尤为重要。本文简要介绍了几种分布式接口幂等性设计实现&#xff0c;包括Token去重机制、乐观锁机制、数据库主键和状态机实现等&#xff0c;以加深理解…

面板安全增强,网站支持反向代理设置,1Panel开源面板v1.2.0发布

2023年5月15日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布v1.2.0版本。 在这一版本中&#xff0c;1Panel着重增强了安全方面的功能&#xff0c;包括安全入口访问、面板SSL设置、网站密码访问等&#xff0c;同时网站新增支持反向代理设置&#xff0c;并带…

JVM学习(三)

1. JAVA 四中引用类型 1.1. 强引用 在 Java 中最常见的就是强引用&#xff0c; 把一个对象赋给一个引用变量&#xff0c;这个引用变量就是一个强引 用。当一个对象被强引用变量引用时 &#xff0c;它处于可达状态&#xff0c;它是不可能被垃圾回收机制回收的&#xff0c;即…

Java阶段二Day21

Java阶段二Day21 文章目录 Java阶段二Day21整合Lombok基础组件1 Lombok简介2 安装和配置 Lombok3 Lombok 注解及其用法3.1 Getter 和 Setter3.2 ToString3.3 AllArgsConstructor 和 NoArgsConstructor3.4 Data 4. 总结5 微博项目优化 Knife4j1 Knife4j的优点2 Knife4j快速上手2…

使用Docker构建的MySQL主从架构:高可用性数据库解决方案

前言 MySQL主从架构&#xff0c;我们已经在vmware虚拟机上实践过了&#xff0c;接下来我们一起探讨在docker中如何使用MySQL主从架构。 &#x1f3e0;个人主页&#xff1a;我是沐风晓月 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是沐风晓月&#xff0c;阿里云社…

《论文阅读》基于提示的知识生成解决对话情感推理难题

《论文阅读》基于提示的知识生成解决对话情感推理难题 前言摘要作者新观点问题定义模型框架Global ModelLocal ModelPrompt Based Knowledge Generation分类器实验结果问题前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文翻译的解读后感到失…

openEuler 成功适配 LeapFive InFive Poros 开发板

近日&#xff0c;openEuler RISC-V 23.03 创新版本在跃昉科技的 Poros 开发板上成功运行。 openEuler 在 Poros 上适配成功&#xff0c;XFCE 桌面启动正常&#xff0c;文件系统、终端模拟器和输入法等相关 GUI 应用也运行流畅&#xff0c;Chromium 浏览器和 LibreOffice 等应用…

【Pm4py第三讲】关于Output

本节用于介绍pm4py中的输出函数&#xff0c;包括日志输出、模型输出、面向对象日志输出等。 1.函数概述 本次主要介绍Pm4py中一些常见的输入函数&#xff0c;总览如下表&#xff1a; 函数名说明write_bpmn()用于写入bpmn模型write_dfg()用于写入dfg模型write_pnml() 用于写入p…

面试之高手回答

1.int与Integer的区别 int与Integer的区别有很多&#xff0c;我简单罗列三个方面 第一个作为成员变量来说Integer的初始值是null&#xff0c;int的初始值是0&#xff1b; 第二个Integer存储在堆内存&#xff0c;int类型是在直接存储在栈空间&#xff1b; 第三个integer是个对象…

项目管理6大避坑技巧

1、拒绝错位战略目标 明确目标方向 做项目&#xff0c;首先需要明确项目目标。项目中有很多目标都很重要&#xff0c;但只有一两个目标是最重要的。在任何时刻&#xff0c;我们主要精力都应该集中在一到两个最重要的目标上。 一般最重要的目标具有以下特点&#xff1a;能够给组…

CSS--空间转换及动画

01-空间转换 空间转换简介 空间&#xff1a;是从坐标轴角度定义的 X 、Y 和 Z 三条坐标轴构成了一个立体空间&#xff0c;Z 轴位置与视线方向相同。空间转换也叫 3D转换属性&#xff1a;transform 平移 transform: translate3d(x, y, z); transform: translateX(); transfor…

能源硕士为何受热捧?社科院与杜兰大学能源管理硕士项目为你解惑

能源行业可谓是全球最具发展前景的行业之一&#xff0c;能源管理硕士更是近几年被争相推荐的“大热门”。广泛的就业选择、较高且稳定的收入&#xff0c;是该专业的特点之一&#xff0c;毕业后可选择在政府相关机构、能源监管部门、全国节能减排领域的各类研究机构工作&#xf…

Linux学习 Day3

目录 1. 时间相关的指令 2. cal指令 3. find指令&#xff1a;&#xff08;灰常重要&#xff09; -name 4. grep指令 5. zip/unzip指令 6. tar指令&#xff08;重要&#xff09;&#xff1a;打包/解包&#xff0c;不打开它&#xff0c;直接看内容 7. bc指令 8. uname –…

Shell基础学习---1、Shell概述、脚本入门、变量

1、Shell 概述 Shell是一个命令解释器&#xff0c;它接收应用程序/用户命令&#xff0c;然后调用操作系统内核。 说明&#xff1a;Shell是一个功能相当强大的编程语言&#xff0c;易编写&#xff0c;易调试、灵活性强。 1、 提供的Shell解释器 2、bash和sh的关系 3、CentOS…

简述-关于Kmeans轮廓系数随着聚类个数的增加后减少的问题

当我们在做Kmeans聚类的准备工作时&#xff0c;通常会用到手肘法&#xff08;elbow method&#xff09;或者轮廓系数&#xff08;silhouette score&#xff09;去找到最佳簇类个数。 对于轮廓系数寻找法&#xff0c;理论上来说&#xff0c;轮廓系数会随着聚类个数的增加而增加…

云渲染是什么?云渲染和传统渲染农场有什么区别?

云渲染是什么&#xff1f;云渲染和传统渲染农场有什么区别&#xff1f; 今天云渲染小编就来和大家说一说云渲染以及它和传统渲染农场的区别。 一、什么是云渲染&#xff1f;云渲染什么意思&#xff1f; 首先云渲染云渲染是一种依托于云计算的云端服务&#xff0c;用户将本地…

UVM 验证方法学之interface学习系列文章(七)高级 《bind 操作》(3)

在之前的文章,我们就bind 机制,进行了用法分析。其实,对于一些大型的复杂SOC设计,bind的操作,可以说是非常实用的。它不仅能够完成各种UVC的驱动激励操作,而且一定程度能够简便验证平台的搭建和后期维护。下面,我们举个例子说明bind在当今复杂环境下的妙用。 一 TB 思…

聊一聊API 测试有哪些不同类型?

用户希望能够跨设备和浏览器使用应用程序。因此&#xff0c;您必须进行全面的不同 API 测试类型&#xff0c;以了解它的工作情况以及它是否可以执行其主要功能。一些测试人员需要更多地关注这方面&#xff0c;因此&#xff0c;我们看到许多质量较差的应用程序。今天&#xff0c…

23. Unity - 3D游戏开发小计02 --- 动画结束UI、导航网格代理

1. 动画结束UI 一个游戏在通过后,都是需要一个界面显示当前游戏已经结束,即需要给游戏添加一个结束的界面,可以做一个简单的游戏结束界面,用一个图片展示: 首先在层级窗口添加两层UI中的Image,其中第一层的Image仅作背景,可将其填充颜色设置为纯黑色,第二层的Image添加…