Spring Boot+Thymeleaf+MyBatis--推荐一个后端练手极佳的商城项目
项目整体架构
newbee-mall
├── src/main/java
└── ltd.newbee.mall
├── common // 存放相关的常量配置及枚举类
├── config // 存放 web 配置类
├── controller // 存放控制类,包括商城端和后台管理系统中的 controller 类
├── admin // 存放后台管理系统中的 controller 类
├── common // 存放通用的 controller 类
└── mall // 存放商城端的 controller 类
├── dao // 存放数据层接口
├── entity // 存放实体类
├── interceptor // 存放拦截器
├── service // 存放业务层方法
├── util // 存放工具类
└── NewBeeMallApplication // Spring Boot 项目主类
├── src/main/resources
├── mapper // 存放 MyBatis 的通用 Mapper文件
├── static // 默认的静态资源文件目录
├── admin // 存放后台管理系统端的静态资源文件目录
└── mall // 存放商城端的静态资源文件目录
├── templates
├── admin // 存放后台管理系统端页面的模板引擎目录
└── mall // 存放商城端页面的模板引擎目录
├── application.properties // 项目配置文件
├── newbee_mall_schema.sql // 项目所需的 SQL 文件
└── upload.zip // 商品图片
└── pom.xml // Maven 配置文件
运行项目
商城项目包括后台管理系统和商城系统,在启动成功后,两个系统都可以正常访问。
商城系统的访问地址如下,键入任何一个地址都能够访问到商城首页。
①BASE_URL
②BASE_URL + '/'
③BASE_URL + /index
④BASE_URL + /index.html
后台管理系统的访问地址如下,键入任何一个地址都能够进入到商城后台管理系统。
①BASE_URL + '/admin'
②BASE_URL + '/admin/login'
③BASE_URL + '/admin/index'
其中BASE_URL为主机名+端口号,比如笔者的本地地址就是localhost:28089。
账号及密码
项目启动成功了,但是由于权限问题无法完整地体验商城的所有功能。这就需要几个测试账号来解决。该项目在表中初始化了几条用户记录和管理员记录,数据分别存储在tbnewbeemalluser表和tbnewbeemalladmin_user表中,读者也可以自行添加和修改这些记录。商城系统用户的测试账号及密码如下所示。
①账号:13700002703 密码:123456
②账号:13711113333 密码:shisan
③账号:13811113333 密码:shisan
后台管理系统管理员的测试账号及密码如下所示。
①账号:admin 密码:123456
②账号:newbee-admin1 密码:123456
③账号:newbee-admin2 密码:123456
对于两张表中存储的密码字段都是经过MD5加密后的字符串。如果想要直接通过数据库来修改密码的话,需要将密码进行MD5转换,然后将加密后的字符串放到数据表的密码这一列中。
登录和管理系统登录
商城系统的很多功能是需要登陆才能够正常使用的,比如后台管理、下单流程。如果不是商城的用户或者管理员,肯定没有权限,大部分功能也无法操作。这一节就演示一下登陆过程。
首先是商城端的登录页面,打开浏览器并输入登录页面地址:
如下图所示,在登录页面依次输入正确的账号密码和验证码,并点击“立即登录”按钮完成商城端的登录。这样就可以完成后续的商品下单等操作了。
然后是商城后台管理系统端的登录页面,打开浏览器并输入登录页面地址:
http://localhost:28089/admin/login
如下图所示,在登录页面依次输入正确的账号密码和验证码,并点击“登录”按钮就能够进入到后台管理系统中。
进入后台管理系统后,就可以对商品模块、轮播图模块等进行操作。
项目优点
商城系统也是一个比较复杂的系统,牵涉的技术内容比较多,而且功能点和技术栈要求也比较高,从零搭建一个商城系统,这个过程不仅考验着开发人员的技术储备丰富度,更考验着技术使用的熟练度,同时对于开发人员的系统设计能力也有要求(系统如何切分,功能点如何设计,页面结构和交互如何优化等),这些技术栈的掌握程度和项目整体的统筹规划都在一定程度上代表着一个技术人员的能力。
开发和统筹一个完整的大型商城系统往往要求技术人员了解很多不同的技术或者框架,比如常用的前端页面模板和基本的 Web 开发知识、后端开发技术框架(如 Spring Boot 、模板引擎、ORM 框架等)、服务器基础设施(如基础的 shell 命令,Nginx 、 MySQL 等常用软件的搭建和使用)都需进行全局考虑和选择。
产品流程完整
横向来梳理一个商城系统的产品流程,从零到正式上线访问所涉及的项目环节如下,产品设计、原型设计、功能开发、功能测试、产品上线、后期维护等环节缺一不可,开发人员可能不太关注完整的流程,但是能够掌握整个流程的基本脉络对于日后的提升会有极大的帮助。
项目功能
首页
用户进入商城首页后,首先会看到管理员设置的分类信息和轮播图,之后是配置的热销商品等模块,这些数据都是在后台管理系统中配置好的运营数据,在商城端直接读取并且显示到页面即可。浏览
接下来用户可以选择直接浏览商品信息,也可以根据分类或者关键字去搜索商品,这是用户的浏览行为。登录和注册
在浏览时我们需要判断用户是否登录,这里就牵涉到商城用户的注册和登录功能了。如果不是商城的注册用户,很多行为是会被限制掉的,此时在商城浏览的用户可以选择填写信息并注册为我们的商城用户,之后就能够登录并且使用商城端的所有功能。选择商品
登录用户可以在商城网站上任性壕气的浏览、挑选商品,此时就引出了商城的购物车功能,用户可以将商品放到购物车中,可以增加、删除、修改购物车中的商品以及商品数量,这些功能点已经在新蜂商城系统中实现。提交订单
壕气的用户终于要下手了,在确定了需要购买的商品和数量后,就可以执行提交订单的操作,仅仅选择商品是不行的,我们还需要把用户的收货信息给记录下来。以上这些信息都处理完毕后,就到了订单环节,此时的用户点击结算按钮,我们的系统中也就对应的生成了一笔订单数据。订单流程
用户在成功的提交订单后,就可以正式的进入到订单操作环节,此时可以选择三个操作:
此时的订单尚未支付,选择去支付流程
下单玩玩,不去管支付流程,继续购物或者退出系统
不想买了或者商品拍错,选择取消订单
以上就是订单模块的流程,包括订单列表功能、订单详情功能、订单取消功能、选择支付方式、支付功能。确认收货
这是订单流程的最后一个环节,此环节需要商家来配合,用户在支付成功之后订单就进入到商家的管理系统中进行后续的处理,在操作了拣货和配送两个订单状态之后,用户就能够在订单详情页面点击确认收货,此时订单的正向流程就顺利完结了。
因为还有取消订单和关闭订单等操作,所以我们把下单->支付->确认收货这个流程称为正向流程。
界面如何设计?
项目亮点:
本章将会介绍在网页开发中常用的验证码功能,并具体讲解如何使用Spring Boot生成验证码并进行后续的验证操作。--验证码功能
Spring Boot整合easy-captcha生成验证码
生成验证码的方式和案例有很多,在新蜂商城项目中验证码的形式为传统输入式验证码,所选择的实现方案是easy-captcha工具包。
easy-captcha开源地址为:
github.com/whvcse/Easy…
它是一款国人开发的验证码工具,支持GIF、中文、算术等类型,可用于Java Web等项目,生成的验证码形式如下图所示。
接下来将会通过一个代码案例来讲解如何使用Spring Boot生成验证码并在网页中显示验证码。
添加easy-captcha依赖
Spring Boot整合easy-captcha的第一步就是增加依赖。首先需要将easy-captcha的依赖配置文件增加到pom.xml文件中,此时的pom.xml文件代码如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ltd.newbee.mall</groupId>
<artifactId>newbee-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>newbee-mall</name>
<description>captcha-demo</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
如果是在本地开发的话,只需要等待 jar 包及相关依赖下载完成即可。
asy-captcha验证码格式
easy-captcha验证码工具支持GIF、中文、算术等类型,分别通过以下几个实例对象实现:SpecCaptcha、GifCaptcha、ChineseCaptcha、ChineseGifCaptcha和ArithmeticCaptcha。
他们依次为PNG类型的静态图片验证码、GIF类型的图片验证码、中文类型的图片验证码、中文GIF类型的图片验证码和算数类型的验证码。读者可以针对自身项目的情况选择合适的验证码生成方式。一般常用SpecCaptcha和GifCaptcha两种方式来生成验证码。
// 生成验证码对象
SpecCaptcha captcha = new SpecCaptcha(130, 48, 5);
// 设置验证码的字符类型
captcha.setCharType(Captcha.TYPE_ONLY_NUMBER);
只有SpecCaptcha和GifCaptcha设置才有效果。
easy-captcha字体设置
easy-captcha默认提供了10种内置字体easy-captcha验证码图片输出
这里可以选择输出为文件流,这是比较常见的处理方式。当然,也有一些Web项目会使用base64编码的图片。这两种方式easy-captcha都支持。输出base64编码如下所示:
specCaptcha.toBase64();
// 如果不想要base64的头部data:image/png;base64,加一个空的参数即可
// specCaptcha.toBase64("");
输出到文件流如下所示:
// 输出到磁盘上
FileOutputStream outputStream = new FileOutputStream(new File("/home/project/captcha.png"));
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
specCaptcha.out(outputStream);
该段代码为生成一张图片并保存到磁盘目录中,这里可以使用easy-captcha工具自带的.out()方法输出。而在开发Web项目时,则会使用Response对象的输出流进行验证码的输出,接下来会结合代码进行详细讲解。
后端逻辑实现:生成并输出验证码
在controller包中新建KaptchaController类,就可以新建一个方法。在方法里使用GifCaptcha可以生成一个PNG类型的验证码对象,并以图片流的方式输出到前端以供显示,代码如下所示:
package ltd.newbee.mall.controller;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class KaptchaController {
@GetMapping("/kaptcha")
public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/gif");
// 三个参数分别为宽、高、位数
SpecCaptcha captcha = new SpecCaptcha(75, 30,4);
// 设置类型为数字和字母混合
captcha.setCharType(Captcha.TYPE_DEFAULT);
// 设置字体
captcha.setCharType(Captcha.FONT_9);
// 验证码存入session
httpServletRequest.getSession().setAttribute("verifyCode", captcha.text().toLowerCase());
// 输出图片流
captcha.out(httpServletResponse.getOutputStream());
}
}
前端逻辑实现:在页面中展示验证码
在static目录中新建kaptcha.html页面,在该页面中显示验证码,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>验证码显示</title>
</head>
<body>
<img src="/kaptcha" onclick="this.src='/kaptcha?d='+new Date()*1" />
</body>
</html>
这里访问后端验证码路径/kaptcha,由于验证码是图片形式,所以将其显示在img标签中。然后定义onclick方法,在点击该img标签时可以动态切换显示一个新的验证码。点击时访问的路径为'/kaptcha?d='+new Date()*1,即原来的验证码路径后面带上一个时间戳参数 d。时间戳是会变化的,所以每次点击都会是一个与之前不同的请求。如果不这样处理的话,由于浏览器的缓存机制,在点击刷新验证码后可能不会重新发送请求,将导致在一段时间内一直显示同一张验证码图片。
在编码完成后,启动Spring Boot项目。在启动成功后打开浏览器并输入验证码显示的测试页面地址:
验证码的输入验证
在验证码的显示完成后,紧接着要做的就是对用户输入的验证码进行比对和验证。
一般的做法是在后端生成验证码后,先对当前生成的验证码内容进行保存,可以选择保存在session对象中,或者保存在缓存中,或者保存在数据库中。然后,返回验证码图片并显示到前端页面。用户在识别验证码后,在页面对应的输入框中填写验证码并向后端发送请求,后端在接到请求后会对用户输入的验证码进行验证。如果用户输入的验证码与之前保存的验证码不相等的话,则返回“验证码错误”的提示消息且不会进行后续的流程,只有验证成功才会继续后续的流程。
后端逻辑实现
在KaptchaController类中新增verify()方法,代码如下所示:
@GetMapping("/verify")
@ResponseBody
public String verify(@RequestParam("code") String code, HttpSession session) {
if (!StringUtils.hasLength(code)) {
return "验证码不能为空";
}
String kaptchaCode = session.getAttribute("verifyCode") + "";
if (!StringUtils.hasLength(kaptchaCode) || !code.toLowerCase().equals(kaptchaCode)) {
return "验证码错误";
}
return "验证成功";
}
该方法所拦截处理的路径为/verify,请求参数为code,即用户输入的验证码。在进行基本的非空验证后,与之前保存在session中的verifyCode值进行比较,如果两个字符串不相等则返回“验证码错误”的提示,二者相同则返回“验证码成功”的提示。
前端逻辑实现
在static目录中新建verify.html,该页面会显示验证码,同时也包含供用户输入验证码的输入框和提交按钮,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>验证码测试</title>
</head>
<body>
<img src="/kaptcha" onclick="this.src='/kaptcha?d='+new Date()*1" />
<br>
<input type="text" maxlength="5" id="code" placeholder="请输入验证码" />
<button id="verify">验证</button>
<br>
<p id="verifyResult">
</p>
</body>
<!-- 下方地址为字节跳动提供的jQuery cdn地址 -->
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
// 验证按钮的点击事件
$('#verify').click(function () {
var code = $('#code').val();
$.ajax({
type: 'GET', // 方法类型
url: '/verify?code=' + code,
success: function (result) {
// 将验证结果显示在p标签中
$('#verifyResult').html(result);
},
error: function () {
alert('请求失败');
},
});
});
});
</script>
</html>
登录流程设计
用户登录状态
客户端(通常是浏览器)在连上Web服务器后,如果想获得Web服务器中的各种资源,就需要遵守一定的通讯格式。Web项目通常使用的是HTTP协议,它用于定义客户端与Web服务器通讯的格式。而HTTP协议是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的、没有任何关联的。一个请求就只是一个请求。
以新蜂商城的后台管理系统为例,它拥有多张页面。在页面跳转过程中和通过接口在进行数据交互时,系统需要知道用户的状态,尤其是用户登录的状态,以便服务器验证用户状态是否正常。这样系统才能判断是否可以让当前用户使用某些功能或者获取某些数据。
这时,就需要在每个页面对用户的身份进行验证和确认。但现实情况却不能如此。一个网站不可能让用户在每个页面上都输入用户名和密码。这是一个违反操作逻辑的设计,也不会有用户使用有这种设计的系统。
因此,在设计登录流程时,需要让用户只进行一次登录操作即可。为了实现这一功能就需要一些辅助技术,用得最多的技术就是浏览器的Cookie。而在Java Web开发中,比较常见的是使用Session技术来实现。将用户登录的信息存放在Cookie或Session中,这样就可以通过读取在Cookie或Session中的用户登录信息,达到记录用户状态、验证用户状态的目的。
分页设计 即设计子模块
19.3.2 后端分页功能设计
分页功能在前端页面的执行是渲染数据和分页信息展示,在后端页面则需要按照前端传输而来的请求,将分页所需的数据正确地查询出来并返回给前端。两端的侧重点并不相同。比如,前端需要展示所有页码,而后端则只需要提供总页数即可,不需要对总页数进行其他操作。再比如前端需要根据用户的操作记录当前页码的参数,以便对页码信息进行调整和限制,而后端只需要接收前端传输过来的页码并进行相应判断和查询即可。
后端分页必不可少的两个参数:页码、条数(每页)。
在实现分页功能时,使用不同的数据库实现方式也不同。因为不同数据库实现分页功能的关键字有些差别,比如SQL Server的top关键字、Oracle的rownum关键字、MySQL的limit关键字。limit关键字如下所示:
//下面是mysql的实现语句:
select * from tb_xxxx limit 10,20
分页功能的最终实现就是通过页码和条数,确定数据库需要查询的数据。比如查询第1页且每页20条的数据,就是查询数据库中从第1到20条的数据;查询第4页且每页10条的数据就是查询数据库中第30到40条的数据。因此,对于后端代码的实现来说,页码和条数两个参数就显得特别重要,缺少这两个参数查询逻辑就不成立,分页数据也就无从查起。
此外,为了前端分页区的展示,还要将数据总量或者总页数返回给前端。数据总量是必不可少的,而总页数可以计算出来(即数据总量除以每页条数)。数据总量的获取方式:
select count(*) from tb_xxxx
之后将数据封装,并返回给前端即可。
19.4 分页功能的编码实现
这里实现的分页功能是一个通用的分页接口。笔者将分页数据封装到一个返回结果对象中,并通过JSON格式返回,目的是让读者理解分页功能的主要逻辑和简单的代码实现。
接下来将结合tb_user表进行简单的查询与分页功能的实现,即在前端请求对应的页数时,返回那一页的所有数据。
Spring Boot+Thymeleaf+MyBatis--推荐一个后端练手极佳的商城项目的更多相关文章
- spring boot +Thymeleaf+mybatis 集成通用PageHelper,做分页
controller: /** * 分页查询用户 * @param request * @param response * @return * @throws Exception */ @ ...
- spring boot + thymeleaf 乱码问题
spring boot + thymeleaf 乱码问题 hellotrms 发布于 2017/01/17 15:27 阅读 1K+ 收藏 0 答案 1 开发四年只会写业务代码,分布式高并发都不会还做 ...
- spring boot集成mybatis(1)
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Spring Boot(5)一个极简且完整的后台框架
https://blog.csdn.net/daleiwang/article/details/75007588 Spring Boot(5)一个极简且完整的后台框架 2017年07月12日 11:3 ...
- spring boot + druid + mybatis + atomikos 多数据源配置 并支持分布式事务
文章目录 一.综述 1.1 项目说明 1.2 项目结构 二.配置多数据源并支持分布式事务 2.1 导入基本依赖 2.2 在yml中配置多数据源信息 2.3 进行多数据源的配置 三.整合结果测试 3.1 ...
- Spring Boot Thymeleaf 实现国际化
开发传统Java WEB工程时,我们可以使用JSP页面模板语言,但是在SpringBoot中已经不推荐使用了.SpringBoot支持如下页面模板语言 Thymeleaf FreeMarker Vel ...
- Spring Boot 实战 —— MyBatis(注解版)使用方法
原文链接: Spring Boot 实战 -- MyBatis(注解版)使用方法 简介 MyBatis 官网 是这么介绍它自己的: MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过 ...
- spring boot集成mybatis(2) - 使用pagehelper实现分页
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Spring Boot的MyBatis注解:@MapperScan和@Mapper(十七)
1.Spring Boot与MyBatis融合的矛盾问题: Spring家族的使命就是为了简化而生,但是随着Spring的发展壮大,有点事与愿违了.为了坚持初心,Spring家族祭出了一大杀器---S ...
- Spring Boot 整合 Mybatis 实现 Druid 多数据源详解
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “清醒时做事,糊涂时跑步,大怒时睡觉,独处时思考” 本文提纲一.多数据源的应用场景二.运行 sp ...
随机推荐
- 【发现一个问题】extjs-gpl-7.0: 当修改 store 对象的字段后,再次 loadPage() 后字段映射错误。
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 因为查询中需要每次返回数目不确定的 fields ,这就需 ...
- 【JS 逆向百例】元素ID定位加密位置,某麻将数据逆向
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某在线麻将 ...
- 【第4个渗透靶机项目】 Tr0ll
每天都要加油哦! 0x00 信息搜集 首先找到一下靶机的ip,并扫描端口. 靶机和kali都是桥接. 那么先看一下kali的ifconfig信息. nmap -sn 192.168.0.0/24 ...
- minIO系列文章04---windows下安装及在.netcore使用
一.minio下载与启动 下载后会有一个minio.exe文件,放到指定的目录 在该目录下运行:minio.exe server D:\minio\file 出现如下的提示代码启动动成功: 浏览器中 ...
- 从零开始配置vim(23)——lsp基础配置
上一章,我们初步认识了lsp,并且对 nvim-treesitter插件进行了配置,为编辑器提供了代码着色.自动格式化以及增量选中功能.算是初步体验了 lsp的相关功能.从这篇开始我们通过lsp的功能 ...
- TienChin 渠道管理-渠道类型
在上一篇文章当中,表里面有一个渠道类型,我们这节主要是将这个渠道类型创建好,首先我们来看看字典表. sys_dict_type 表: 字段名 数据类型 注释 dict_id bigint 字典主键 d ...
- 18.3 NPCAP自定义数据包过滤
NPCAP 库是一种用于在Windows平台上进行网络数据包捕获和分析的库.它是WinPcap库的一个分支,由Nmap开发团队开发,并在Nmap软件中使用.与WinPcap一样,NPCAP库提供了一些 ...
- Linux 文件目录操作命令
Linux 基础的文件目录操作命令,融合多部Linux经典著作,去除多余部分,保留实用部分. 显示目录或文件: 显示目标列表,在Linux系统中是使用率较高的命令.ls命令的输出信息可以进行彩色加亮显 ...
- C# 通过VMI接口获取硬件ID
使用C#语言实现通过VMI(虚拟机监控器)接口来获取硬件ID的过程.VMI是一种用于虚拟化环境的接口,用于管理虚拟机和宿主机之间的通信和资源共享.具体实现中,需要通过添加System.Manageme ...
- UIWindow的概念与使用
UIWindow的作用 UIWindow是UIView的子类用于显示程序内容.每一个UIView想要将内容显示到屏幕上都需要依赖于一个UIWindow. iOS应用程序要想正常运行至少要有一个UIWi ...