JavaEE架构

一.架构选型

1.VM架构

VM架构通常指的是虚拟机(Virtual Machine)的架构。虚拟机是一种软件实现的计算机系统,它模拟了物理计算机的功能,允许在单一物理硬件上运行多个操作系统实例。虚拟机架构主要包括以下几个关键组件:
1. 虚拟机监控器(Hypervisor):
   - 类型1 Hypervisor:直接运行在物理硬件上,也称为裸机Hypervisor。例如,VMware ESXi、Microsoft Hyper-V、Xen。
   - 类型2 Hypervisor:运行在宿主操作系统之上,例如VMware Workstation、Oracle VirtualBox。
2. 虚拟机(VM):
   - 每个虚拟机都包含一个完整的操作系统和应用程序,它们与物理硬件隔离,运行在虚拟化环境中。
3. 虚拟硬件:
   - 虚拟机通过虚拟硬件与物理硬件交互,包括虚拟CPU、虚拟内存、虚拟磁盘和虚拟网络接口等。
4. 管理工具:
   - 用于创建、配置、管理和监控虚拟机的工具,例如VMware vCenter、Microsoft System Center Virtual Machine Manager。
5. 存储和网络虚拟化:
   - 虚拟化技术还扩展到存储和网络资源,允许更灵活和高效地管理这些资源。
虚拟机架构的主要优势包括:
- 资源利用率:通过在一台物理服务器上运行多个虚拟机,可以更高效地利用硬件资源。
- 隔离性:每个虚拟机都是独立的,一个虚拟机的故障不会影响其他虚拟机。
- 灵活性:虚拟机可以轻松地迁移、复制和备份。
- 成本效益:减少了对物理硬件的需求,降低了硬件和维护成本。
虚拟机架构广泛应用于数据中心、云计算、开发和测试环境等场景。

2.MVC架构

MVC架构(Model-View-Controller)是一种广泛应用于软件工程中的设计模式,特别是在构建用户界面时。它将应用程序分为三个主要部分,以提高代码的组织性和可维护性。以下是MVC架构的详细介绍:

1. Model(模型):
   - 职责:负责管理应用程序的核心数据和业务逻辑。它直接与数据库或其他数据源交互,处理数据的获取、存储和更新。
   - 特点:独立于用户界面,不直接与用户交互。

2. View(视图):
   - 职责:负责显示数据(模型)给用户,并接收用户的输入。视图通常是从模型获取数据的用户界面元素。
   - 特点:不包含业务逻辑,只负责数据的展示和用户交互的呈现。

3. Controller(控制器):
   - 职责:作为模型和视图之间的中介,处理用户的输入并更新模型。控制器接收来自视图的用户操作,根据需要更新模型,并可能要求视图更新以反映模型的变化。
   - 特点:包含应用程序的逻辑,决定如何处理用户输入和更新视图。

MVC架构的主要优势包括:

- 分离关注点:通过将应用程序的不同方面分离,使得代码更易于管理和维护。
- 可复用性:模型和控制器可以在不同的视图中复用,减少了代码重复。
- 并行开发:不同的开发者可以同时工作在模型、视图和控制器上,提高了开发效率。
- 易于测试:分离的组件使得单元测试和集成测试更加容易。

MVC架构广泛应用于Web开发框架中,如Ruby on Rails、ASP.NET MVC、Spring MVC等。它帮助开发者构建结构清晰、易于扩展和维护的应用程序。

3.MVVC架构

MVVC架构(Model-View-ViewModel)是一种软件设计模式,它是MVC(Model-View-Controller)架构的一个变体,主要用于简化用户界面的开发,特别是在实现数据绑定和命令绑定的场景中。MVVC架构在WPF(Windows Presentation Foundation)、Silverlight以及一些现代Web框架如Knockout.js和Vue.js中得到了广泛应用。

MVVC架构的组成部分包括:

1. Model(模型):
   - 职责:代表应用程序的核心数据和业务逻辑。它通常包含数据访问层和业务规则,与MVC中的Model类似。
   - 特点:独立于用户界面,不直接与用户交互。

2. View(视图):
   - 职责:定义应用程序的用户界面。它负责展示数据(来自ViewModel)给用户,并接收用户的输入。
   - 特点:不包含业务逻辑,只负责数据的展示和用户交互的呈现。

3. ViewModel(视图模型):
   - 职责:作为视图和模型之间的桥梁,提供视图所需的数据和命令。ViewModel通常包含视图的状态和行为逻辑,它将模型的数据转换为视图可以轻松使用的形式。
   - 特点:包含视图的逻辑,但不直接引用视图,实现了视图和模型的解耦。

MVVC架构的关键特性是数据绑定,它允许视图和ViewModel之间的自动同步。当ViewModel中的数据发生变化时,视图会自动更新,反之亦然。这种双向数据绑定机制减少了手动更新UI的代码量,提高了开发效率。

MVVC架构的主要优势包括:

- 分离关注点:清晰地分离了视图、视图逻辑和业务逻辑,使得代码更易于管理和维护。
- 可测试性:ViewModel不依赖于视图,使得单元测试更加容易。
- 可复用性:ViewModel可以在不同的视图中复用,减少了代码重复。
- 简化开发:数据绑定和命令绑定减少了样板代码,加快了开发速度。

MVVC架构特别适合于需要复杂用户交互和数据展示的应用程序,它通过提供一种结构化的方式来组织代码,帮助开发者构建响应迅速、易于维护的应用程序。

二.项目架构

将分层思想转换为Java包

1.创建项目

2.创建包(“项目架构”)

2.1 dao(数据操作的抽象:CRUD

DAO(Data Access Object)是一种设计模式,用于抽象和封装对数据源的访问。它提供了一个接口,用于执行对数据库或其他持久化机制的操作,而不暴露底层数据访问的细节。DAO模式的主要目的是将业务逻辑与数据访问逻辑分离,从而提高代码的可维护性、可复用性和可测试性。

DAO模式通常包括以下几个组件:

1. **DAO接口**:
   - 定义了对数据源进行操作的方法,如创建、读取、更新和删除(CRUD)操作。
   - 接口使得可以在不改变业务逻辑的情况下更换数据访问的实现。

2. **DAO实现类**:
   - 实现了DAO接口,包含具体的SQL语句或使用ORM(对象关系映射)框架来与数据库交互。
   - 负责处理数据的持久化和检索。

3. **数据传输对象(DTO)**:
   - 用于在应用程序的不同层之间传输数据的简单对象。
   - 通常只包含属性和getter/setter方法,不包含业务逻辑。

4. **数据源**:
   - 可以是关系型数据库、NoSQL数据库、文件系统或其他任何形式的数据存储。

DAO模式的主要优势包括:

- **分离关注点**:业务逻辑不需要知道数据是如何持久化的,只需通过DAO接口与数据交互。
- **易于维护**:数据访问逻辑集中在一个地方,便于管理和优化。
- **提高可复用性**:DAO可以在不同的业务逻辑中复用,减少了代码重复。
- **增强可测试性**:可以通过模拟DAO接口来测试业务逻辑,而不需要实际访问数据库。

在实际应用中,DAO模式常常与其他设计模式如服务层模式结合使用,以进一步分离业务逻辑和数据访问逻辑。此外,现代框架如Spring和Hibernate提供了对DAO模式的支持,简化了数据访问层的开发。

2.2 entity

开发Entity的主要目的是在软件系统中对现实世界的对象或概念进行抽象和建模,以便更好地管理和操 作数据。具体目的包括:

数据抽象与封装

       抽象现实对象:Entity将现实世界的对象或概念抽象为代码中的数据结构,便于系统处理。

       封装数据与行为:Entity封装了对象的属性和行为,确保数据的一致性和完整性。

数据持久化

       与数据库映射:Entity通常与数据库表对应,简化数据存储与检索。

       ORM支持:通过对象关系映射(ORM),Entity可以直接与数据库交互,减少手动SQL操作。

2.3 service(解决项目中的业务问题)

Service 的核心职责
1. **封装业务逻辑**:
   - Service 层包含应用程序的核心业务逻辑,例如验证、计算、数据处理等。
   - 它确保业务规则的一致性和可维护性。

2. **协调数据访问**:
   - Service 层调用数据访问对象(DAO)或 Repository 来获取或存储数据。
   - 它可以在多个 DAO 或 Entity 之间协调复杂的操作。

3. **事务管理**:
   - Service 层通常负责管理事务(Transaction),确保多个操作在一个事务中执行。
   - 在 Java EE 中,可以使用 `@Transactional` 注解来声明事务边界。

4. **解耦表示层和数据访问层**:
   - Service 层作为中间层,将表示层(如 Controller)与数据访问层(如 DAO)解耦,使代码更易于维护和测试。

5. **提供可重用的服务**:
   - Service 层的方法可以被多个表示层组件(如 Web 控制器、REST 端点)调用,实现逻辑的复用。

2.4 controller

数据的传输

        前端到后台

        后台到前端

2.5 vo

View Object: 视图对象,也需要展示的对象

2.6 dto

DTO(Data Transfer Object,数据传输对象)是一种设计模式,用于在不同层或组件之间传输数据。DTO 的主要目的是封装数据,减少网络调用次数,并简化数据交换的过程。它通常是一个简单的 Java 类,包含属性和对应的 getter/setter 方法,不包含任何业务逻辑。

---

### DTO 的核心特点
1. **数据封装**:
   - DTO 是一个纯粹的数据容器,用于封装一组相关的数据。
   - 它通常只包含字段和访问这些字段的方法(getter/setter)。

2. **减少网络调用**:
   - 在分布式系统中,DTO 可以一次性传输大量数据,减少多次调用的开销。

3. **解耦层与层之间的依赖**:
   - DTO 用于在表示层(如前端)、服务层和持久层之间传递数据,避免直接暴露领域模型(Entity)。

4. **简化数据格式**:
   - DTO 可以根据需要调整数据的结构,例如过滤敏感信息、组合多个字段或转换数据格式。

5. **无业务逻辑**:
   - DTO 不包含任何业务逻辑,仅用于数据传输。

---

### DTO 的使用场景
1. **前后端数据交互**:
   - 在 RESTful API 中,DTO 用于封装请求和响应的数据。
2. **服务间通信**:
   - 在微服务架构中,DTO 用于在不同服务之间传递数据。
3. **数据脱敏**:
   - DTO 可以隐藏敏感信息(如密码、身份证号),只暴露必要的数据。
4. **数据聚合**:
   - DTO 可以将多个 Entity 或数据源的数据组合成一个对象,简化数据传输。

2.7 exception

项目的异常处理类

2.8 util

工具类

顺序随意(数据库->controller)

三.数据库

四.实体类(entity)

lombok:自动生成set/get,toString(),构造器

@Data //set/get 无参构造方法
@AllArgsConstructor //全参构造器
@NoArgsConstructor //无参构造器
public class User {private Integer id;private String username;private String password;
}

五.数据访问层(Dao)

1.接口

/*** 用户数据访问接口*/
public interface IUserDao {/*** 添加数据* @param user*/void save(User user);/*** 根据id删除* @param id*/void deleteId(int id);/*** 修改数据* 对象指定的id* @param user*/void update(User user);/*** 返回所有数据* @return 所有数据的集合*/List<User> findAll();
}

2.实现类

/*** UserDao的实现类* @author 武鑫鑫*/
public class UserDaoImpl implements IUserDao {@Overridepublic void save(User user) {JdbcUtil.connect();String sql = "INSERT INTO user_tab(user_username,user_password) VALUES(?,?)";JdbcUtil.preparedStatement(sql,user.getUsername(),user.getPassword());JdbcUtil.executeUpdate();JdbcUtil.close();}@Overridepublic void deleteId(int id) {JdbcUtil.connect();String sql = "DELETE FROM user_tab WHERE user_id = ?";JdbcUtil.preparedStatement(sql,id);JdbcUtil.executeUpdate();JdbcUtil.close();}@Overridepublic void update(User user) {JdbcUtil.connect();String sql = "UPDATE user_tab SET user_password=? WHERE user_username=?";JdbcUtil.preparedStatement(sql,user.getPassword(),user.getUsername());JdbcUtil.executeUpdate();JdbcUtil.close();}@Overridepublic List<User> findAll() {List<User> list = new ArrayList<>();JdbcUtil.connect();String sql = "SELECT * FROM user_tab";JdbcUtil.preparedStatement(sql);ResultSet rs = JdbcUtil.executeQuery();try {while (rs.next()){int id = rs.getInt("user_id");String username = rs.getString("user_username");String password = rs.getString("user_password");User user = new User(id,username,password);list.add(user);}} catch (SQLException e) {throw new RuntimeException(e);}JdbcUtil.close();return list;}
}

六.DTO

模拟一个Result对象:职责将对象搬运到前端

1.Result

@Data
public class Result {private int code;private String msg;private Object results;private Date time;/*** 成功返回* @param results* @return*/public static Result success(Object results){Result r = new Result();r.setCode(ResultCode.SUCCESS.getCode());r.setMsg(ResultCode.SUCCESS.getMsg());r.setResults(results);r.setTime(new Date());return r;}/*** 失败返回* @return*/public static Result failed(){Result r = new Result();r.setCode(ResultCode.FAILED.getCode());r.setMsg(ResultCode.FAILED.getMsg());r.setTime(new Date());return r;}
}

2.ResultCode(枚举类)

/*** 枚举类:[代码."原因"]* 规范返回结果[代码,代码解析]*/
@Getter
public enum ResultCode {SUCCESS(200,"成功"),FAILED(500,"失败");private Integer code;private String msg;/*** 构造器** @param code* @param msg*/private ResultCode(Integer code,String msg){this.code = code;this.msg = msg;}
}

七.JdbcUtil

JDBC的简单封装工具类

1.引入依赖

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version>
</dependency>

2.jdbc五部曲

2.1 注册驱动

 //只运行一次static {registry();}/*** 1.注册驱动*/private static void registry(){try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}

2.2 建立连接

    /*** 2.建立连接*/public static void connect(){try {conn = DriverManager.getConnection(URL,USERNAME,PASSWORD);} catch (SQLException e) {throw new RuntimeException(e);}}

2.3 预处理

执行SQL语句

    /*** 3.预处理SQL* @param sql* @param values*/public static void preparedStatement(String sql,Object... values){try {pst = conn.prepareStatement(sql);for (int i=0;i<values.length;i++){pst.setObject(i+1,values[i]);}} catch (SQLException e) {throw new RuntimeException(e);}}

2.4 执行SQL

(1)执行增删改操作

    /*** 4.1执行增删改*/public static void executeUpdate(){try {pst.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);}}

(2)执行查询操作

    /*** 4.2执行查询* @return*/public static ResultSet executeQuery(){try {rs = pst.executeQuery();} catch (SQLException e) {throw new RuntimeException(e);}return rs;}

2.5 关闭数据库连接

    /*** 5.关闭数据库的连接*/public static void close(){try {if(rs!=null){rs.close();}if(pst!=null){pst.close();}if(conn!=null){conn.close();}} catch (SQLException e) {throw new RuntimeException(e);}}

八.service

业务

public interface IUserService {/*** 添加用户* @param user*/void add(User user);void updateUser(User user);void removeById(int id);List<User> findAll();
}
public class UserServiceImpl implements IUserService {private IUserDao userDao;public UserServiceImpl() {this.userDao = new UserDaoImpl();}@Overridepublic void add(User user) {userDao.save(user);}@Overridepublic void updateUser(User user) {userDao.update(user);}@Overridepublic void removeById(int id) {userDao.deleteId(id);}@Overridepublic List<User> findAll() {return userDao.findAll();}
}

九.单元测试

1.引入单元测试框架(JUnit)

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>

2.测试用例

public class UserServiceImplTest {private IUserService ius;@Beforepublic void init(){ius = new UserServiceImpl();}@Testpublic void add() {User user = new User();user.setUsername("wjx");user.setPassword("888");ius.add(user);}@Testpublic void updateUser() {User user = new User();user.setUsername("wjx");user.setPassword("666");ius.updateUser(user);}@Testpublic void removeById() {ius.removeById(1);}@Testpublic void findAll(){List<User> all = ius.findAll();Assert.assertEquals(1,all.size());}
}

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

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

相关文章

[笔记] 汇编杂记(持续更新)

文章目录 前言举例解释函数的序言函数的调用栈数据的传递 总结 前言 举例解释 // Type your code here, or load an example. int square(int num) {return num * num; }int sub(int num1, int num2) {return num1 - num2; }int add(int num1, int num2) {return num1 num2;…

如何在Linux中设置定时任务(cron)

在Linux系统中&#xff0c;定时任务是自动执行任务的一种非常方便的方式&#xff0c;常常用于定期备份数据、更新系统或清理日志文件等操作。cron是Linux下最常用的定时任务管理工具&#xff0c;它允许用户根据设定的时间间隔自动执行脚本和命令。在本文中&#xff0c;我们将详…

【MySQL】我在广州学Mysql 系列—— 数据备份与还原

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天周一&#xff0c;过两天就是元宵节了&#xff0c;今年元宵节各位又要怎么过呢&#xff01;&#xff01; 本文主要对Mysql数据库中的数据备份与还原内容进行讨论&#xff01;&#xff01; 回顾&#xff1a;&#x1f449;【MySQ…

【redis】数据类型之hash

Redis中的Hash数据类型是一种用于存储键值对集合的数据结构。与Redis的String类型不同&#xff0c;Hash类型允许你将多个字段&#xff08;field&#xff09;和值&#xff08;value&#xff09;存储在一个单独的key下&#xff0c;从而避免了将多个相关数据存储为多个独立的key。…

SpringBoot+Dubbo+zookeeper 急速入门案例

项目目录结构&#xff1a; 第一步&#xff1a;创建一个SpringBoot项目&#xff0c;这里选择Maven项目或者Spring Initializer都可以&#xff0c;这里创建了一个Maven项目&#xff08;SpringBoot-Dubbo&#xff09;&#xff0c;pom.xml文件如下&#xff1a; <?xml versio…

游戏引擎学习第96天

讨论了优化和速度问题&#xff0c;以便简化调试过程 节目以一个有趣的类比开始&#xff0c;提到就像某些高端餐厅那样&#xff0c;菜单上充满了听起来陌生或不太清楚的描述&#xff0c;需要依靠服务员进一步解释。虽然这听起来有些奇怪&#xff0c;但实际上&#xff0c;它反映…

【分布式理论9】分布式协同:分布式系统进程互斥与互斥算法

文章目录 一、互斥问题及分布式系统的特性二、分布式互斥算法1. 集中互斥算法调用流程优缺点 2. 基于许可的互斥算法&#xff08;Lamport 算法&#xff09;调用流程优缺点 3. 令牌环互斥算法调用流程优缺点 三、三种算法对比 在分布式系统中&#xff0c;多个应用服务可能会同时…

安宝特方案 | AR助力制造业安全巡检智能化革命!

引言&#xff1a; 在制造业中&#xff0c;传统巡检常面临流程繁琐、质量波动、数据难以追溯等问题。安宝特AR工作流程标准化解决方案&#xff0c;通过增强现实AR技术&#xff0c;重塑制造业安全巡检模式&#xff0c;以标准化作业流程为核心&#xff0c;全面提升效率、质量与…

科技查新过不了怎么办

“科技查新过不了怎么办&#xff1f;” “科技查新不通过的原因是什么&#xff1f;” 想必这些问题一直困扰着各位科研和学术的朋友们&#xff0c;尤其是对于查新经验不够多的小伙伴&#xff0c;在历经千难万险&#xff0c;从选择查新机构、填写线上委托单到付费&#xff0c;…

【AI时代】Page Assist - 本地 AI 模型的 Web UI (谷歌浏览器) 本地DeepSeek启用联网功能

Page Assist - 本地 AI 模型的 Web UI 一、部署本地模型 参考教程&#xff1a;https://blog.csdn.net/Bjxhub/article/details/145536134二、安装插件 Page Assist 浏览器谷歌商店搜索 Page Assist &#xff0c;安装该插件。 注意&#xff1a;需要一点科学的魔法。 三、使用…

collabora online+nextcloud+mariadb在线文档协助

1、环境 龙蜥os 8.9 docker 2、安装docker dnf -y install dnf-plugins-core dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sed -i shttps://download.docker.comhttps://mirrors.tuna.tsinghua.edu.cn/docker-ce /etc/yum.repos.…

Spring MVC 拦截器(Interceptor)与过滤器(Filter)的区别?

1、两者概述 拦截器&#xff08;Interceptor&#xff09;&#xff1a; 只会拦截那些被 Controller 或 RestController 标注的类中的方法处理的请求&#xff0c;也就是那些由 Spring MVC 调度的请求。过滤器&#xff08;Filter&#xff09;&#xff1a; 会拦截所有类型的 HTTP …

SQL Server 逻辑查询处理阶段及其处理顺序

在 SQL Server 中&#xff0c;查询的执行并不是按照我们编写的 SQL 语句的顺序进行的。相反&#xff0c;SQL Server 有自己的一套逻辑处理顺序&#xff0c;这个顺序决定了查询的执行方式和结果集的生成。了解这些处理阶段和顺序对于优化查询性能和调试复杂查询非常重要。 SQL …

二、通义灵码插件保姆级教学-IDEA(使用篇)

一、IntelliJ IDEA 中使用指南 1.1、代码解释 选择需要解释的代码 —> 右键 —> 通义灵码 —> 解释代码 解释代码很详细&#xff0c;感觉很强大有木有&#xff0c;关键还会生成流程图&#xff0c;对程序员理解业务非常有帮忙&#xff0c;基本能做到哪里不懂点哪里。…

Java 大视界 -- 5G 与 Java 大数据融合的行业应用与发展趋势(82)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

四、自然语言处理_08Transformer翻译任务案例

0、前言 在Seq2Seq模型的学习过程中&#xff0c;做过一个文本翻译任务案例&#xff0c;多轮训练后&#xff0c;效果还算能看 Transformer作为NLP领域的扛把子&#xff0c;对于此类任务的处理会更为强大&#xff0c;下面将以基于Transformer模型来重新处理此任务&#xff0c;看…

深入探索JavaCV:功能强大的Java计算机视觉库

&#x1f9d1; 博主简介&#xff1a;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;…

新一代高性能无线传输模块M-GATEWAY3

M-GATEWAY3是M3系列的通用接口模块&#xff0c;用于接收各种总线信号并将它们集成到一个系统中。该模块通过标准化传输协议XCPonETH进行输出&#xff0c;确保为各种测量应用提供无损信号。此外&#xff0c;M-GATEWAY3支持通过热点、ETH-PC或USB-C传输数据。借助M-GATEWAY3&…

deepseek+“D-id”或“即梦AI”快速生成短视频

1、deepseek生成视频脚本 1.1、第一步&#xff1a;使用通用模板提出需求&#xff0c;生成视频脚本 对话输入示例脚本1&#xff1a; 大年初五是迎财神的日志&#xff0c;帮我生成10秒左右的短视频&#xff0c; 体现一家3口在院子里欢庆新年&#xff0c; 孩子在院子里放鞭炮烟…

aspectFill(填充目标区域的同时保持图像的原有宽高比 (aspect ratio)图像不会被拉伸或压缩变形

“aspectFill” 是一个常用于图像和视频处理的术语&#xff0c;尤其是在用户界面 (UI) 设计和图形编程领域。它描述的是一种图像缩放或调整大小的方式&#xff0c;旨在填充目标区域的同时保持图像的原有宽高比 (aspect ratio)。 更详细的解释: Aspect Ratio (宽高比): 指的是图…