苍穹外卖学习记录
苍穹外卖学习
文章目录
- 苍穹外卖学习
- 知识前提:
- **<font color="red">Nginx**
- **<font color="red">Swagger**
- 1.管理员登录
- 思路:
- 详细步骤:
- 1.1新增员工
- 问题1:在新增员工时,需要将当前登录人的id存入表中对应的创建人id和修改人id。
- 问题2:录入的用户名已存在时,抛出异常后并未处理,直接停止服务了。
- 1.2员工信息分页查询
- 问题1:分页查询时,Records中的时间与前端需要的不符
- 知识点:序列化和反序列化
- 1.3修改员工时信息回显
- 1.4 修改员工信息
- 2.删除分类注意事项
- 2.1公共字段自动填充
- 实现思路
- 2.2文件上传功能(阿里云OSS存储)
- 2.3新增菜品功能注意
- DTO/VO/DO/PO理解:
- 2.4分页查询sql
- 2.5删除菜品
- 4.1.3 表设计
- 2.5新增菜品接口
- 3.新增套餐
- 4.SpringCache缓存
知识前提:
Nginx
前端请求通过Nginx转发到后端服务器,从而实现反向代理。
Swagger
-
在配置类中加入 knife4j 相关配置
WebMvcConfiguration.java
/*** 通过knife4j生成接口文档* @return */@Beanpublic Docket docket() {ApiInfo apiInfo = new ApiInfoBuilder().title("苍穹外卖项目接口文档").version("2.0").description("苍穹外卖项目接口文档").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).paths(PathSelectors.any()).build();return docket;}
-
设置静态资源映射,否则接口文档页面无法访问
WebMvcConfiguration.java
/*** 设置静态资源映射* @param registry */ protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); }
1.管理员登录
思路:
- 前端发起登录请求:前端通过 HTTP 请求向后端发送登录信息,包括用户名和密码。
- 后端接收并处理请求:后端接收请求,将前端传来的数据封装成
EmployeeLoginDTO
对象,并进行登录校验。 - 业务逻辑处理:在业务层进行具体的登录逻辑处理,包括数据库查询和密码校验。
- 生成 JWT 令牌:登录成功后,生成 JWT 令牌,并将其与管理员信息一起封装成
EmployeeLoginVO
对象。 - 响应结果:将封装好的
EmployeeLoginVO
对象作为响应返回给前端。
详细步骤:
- 前端发起登录请求
- 前端通过 POST 请求向后端发送管理员的登录信息(用户名和密码)。
- 后端接收并处理请求
- 控制层接收前端传来的登录信息,并将其封装成
EmployeeLoginDTO
对象。 - 调用业务层的登录方法进行校验。
- 控制层接收前端传来的登录信息,并将其封装成
MD5加密:password = DigestUtils.md5DigestAsHex(password.getBytes()); //MD5加密
业务逻辑处理
- 在业务层通过用户名查询数据库
- 判断用户名是否存在或锁定
- 接着进行密码校验,校验完毕后,成功返回管理员对象,否则报异常(用户名不存在、密码不对、账号被锁定)
生成 JWT 令牌
-
此时验证成功后,生成包含管理员信息的 JWT 令牌,格式是Map键值对
-
通过
yaml
文件的配置生成JWT令牌。通常 JWT 令牌会包含管理员的 ID 和一些其他必要的信息。
封装响应对象
- 将管理员信息和 JWT 令牌封装进
EmployeeLoginVO
对象中。 EmployeeLoginVO
类实现了Serializable
接口,确保对象可以被序列化。- 使用 Lombok 的
@Data
、@Builder
等注解简化对象的创建和使用。
这里VO
对象封装是使用了构造器模式和序列化接口等技术,使对象的创建和使用更方便高效。
1.1新增员工
问题1:在新增员工时,需要将当前登录人的id存入表中对应的创建人id和修改人id。
可以通过ThreadLocal进行单一的线程存储,存储的id实在JWT令牌解析后,直接存进Threadlocal,因为线程都是一致的,所以可以直接使用。
问题2:录入的用户名已存在时,抛出异常后并未处理,直接停止服务了。
这里可以看出,报错的异常名为SQLIntegrityConstraintViolationException
Duplicate entry ‘zhangsan’ for key ‘employee.idx_username’ 意思就是重复的用户名,所以就需要设置自定义异常
1.2员工信息分页查询
注意事项:
- 请求参数类型为Query,不是json格式提交,在路径后直接拼接。/admin/employee/page?name=zhangsan
- 返回数据中records数组中使用Employee实体类对属性进行封装。
思考:
- 通过PageHelper分页插件进行分页查询,在业务层方面使用前端传入的DTO对象,将当前页码和每页的大小传入分页插件,通过Mapper进行分页查询后,需要将指定数据传回前端。
- 例如Total(总记录数/总页数)和Records(当前页数据集合),此时page中已经完成分页查询并动态计算出当前页面数据和总页数记录,只需get、set即可传入PageResult
- PageResult是一个封装分页查询结果的类
问题1:分页查询时,Records中的时间与前端需要的不符
- 方法1:通过@JsonFormat进行时间格式规范化
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
- 方法2:通过在WebMvcConfiguration中扩展SpringMVC的消息转换器,统一对日期类型进行格式处理
/*** 扩展Spring MVC框架的消息转化器* @param converters*/protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展消息转换器...");//创建一个消息转换器对象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据converter.setObjectMapper(new JacksonObjectMapper());//将自己的消息转化器加入容器中converters.add(0,converter);}
这里的JacksonObjectMapper是一个对象映射器,通过规范化定义时间格式确保日期和时间以特定的格式进行序列化和反序列化
知识点:序列化和反序列化
在这个场景中,序列化和反序列化指的是将 Java 对象转换为 JSON 格式的字符串(序列化),以及将 JSON 格式的字符串转换回 Java 对象(反序列化)的过程。
序列化 (Serialization)
- 序列化是将 Java 对象的状态信息转换为可以存储或传输的形式的过程。在这个例子中,序列化是将 Java 对象转换为 JSON 格式的字符串。
- 例如,如果你有一个 Employee 类,序列化会将 Employee 对象的信息转换为一个 JSON 字符串,如下所示:
Employee employee = new Employee("John Doe", 30, LocalDate.now());// 序列化为 JSON
String json = objectMapper.writeValueAsString(employee);
System.out.println(json);
输出的 JSON 字符串可能会类似于:
{"name": "John Doe","age": 30,"birthdate": "2024-08-14"
}
反序列化 (Deserialization)
反序列化是从序列化的表示形式中提取数据,并重新创建 Java 对象的过程。在这个例子中,反序列化是从 JSON 字符串中恢复出原始的 Java 对象。例如:
String json = “{“name”:“John Doe”,“age”:30,“birthdate”:“2024-08-14”}”;
// 反序列化为 Java 对象
Employee employee = objectMapper.readValue(json, Employee.class);
System.out.println(employee.getName());
这将输出:
John Doe
总结
序列化:将 Java 对象转换为 JSON 字符串。反序列化:将 JSON 字符串转换回 Java 对象。
自定义序列化器和反序列化器:通过 SimpleModule 添加自定义逻辑来处理特定类型的序列化和反序列化,例如日期和时间类型。
1.3修改员工时信息回显
通过点击当前员工修改按钮,获取该员工id,通过id查询数据库
1.4 修改员工信息
这里可以使用先前启用禁用员工账号的更新方法,万金油,通过参数值是null来排除修改的值,有值才会更修改,null直接跳过
省略类似的CRUD功能
2.删除分类注意事项
- 在删除分类之前判断是否有关联的菜品或套餐是为了保证数据的一致性和完整性。
- 如果直接删除一个被其他数据引用的分类,可能会导致数据库中的数据丢失或者出现“悬挂”引用的情况,即某些记录失去了与之相关的分类信息。
在实际应用中,这样的逻辑是非常重要的,因为它可以帮助开发者确保应用程序的数据一致性。例如,在餐饮管理系统中,如果一个分类被菜品或套餐引用,那么删除该分类可能会导致用户无法查看到这些菜品或套餐所属的分类信息,从而影响用户体验和系统的可靠性。因此,在删除分类之前进行这样的检查是非常必要的。
2.1公共字段自动填充
使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。
实现思路
在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:
序号 | 字段名 | 含义 | 数据类型 | 操作类型 |
---|---|---|---|---|
1 | create_time | 创建时间 | datetime | insert |
2 | create_user | 创建人id | bigint | insert |
3 | update_time | 修改时间 | datetime | insert、update |
4 | update_user | 修改人id | bigint | insert、update |
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
@Slf4j
@Aspect //定义一个切面
@Component
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知,在通知中进行公共字段赋值*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段的自动填充...");//获取到当前被拦截的方法上和数据库操作类型MethodSignature signature = (MethodSignature)joinPoint.getSignature(); //方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); //获得方法上的注解对象OperationType operationType = autoFill.value(); //获取数据库操作类型//获取到当前被拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if(args==null|| args.length==0){return; //判断如果你更新修改的时候里面没有值,我就不要你了}Object entity = args[0];//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根据当前不同的操作类型,为对应的属性通过反射来赋值if(operationType == OperationType.INSERT){try {//为4个公共字段赋值Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}else if(operationType == OperationType.UPDATE){try {//为2个公共字段赋值Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}}
}
理解:
- 通过给
AutoFillAspect
添加注解 @Aspect 说明这是一个切面类,这个 @Pointcut 是一个切入点,也就是作为拦截的,而execution
(定义切入点表达式)是设置拦截的范围是 com.sky.mapper 包下的所有类和所有方法, @annotation(com.sky.annotation.AutoFill) 表示它只拦截带有AutoFill
注解类型的方法。 - 此时,因为公共字段填充字段是需要在修改字段或者新增字段之前完成填充,所以是前置通知 @before , @Before(“autoFillPointCut()”) 这个参数就是告诉你,这个前置通知的方法是需要找范围和要求的,而这个范围就是这个切入点的范围和要求。
- 通过执行 autoFill 方法,这里参数是
joinPoint
,我认为这是获取的一个切入点,通过getSignature
获取当前切入点的方法签名,例如 update 方法,这只是方法签名,想要真正获取到方法上的注解,就需要进一步获取到该方法上的注解,所以使用getMethod
以及getAnnotation
去指定获取 AutoFill 自定义注解。 - 接下来获取到这个方法上的 AutoFill 注解了,但是我只需要这个数据库操作参数,所以我们可以发现它的
value
值正好就是数据库操作类型, autoFill 是以键值对存储这个操作类型的。 - 通过 getArgs 获取
joinPoint
的实体对象,此时这个实体对象其实就是前端获取到你输入的字段,比如新增的字段或者修改的字段,把它存进 args 里面,判断它非空且长度非零,然后将第一个元素赋给一个 Object 类的实体(其实里头最多就一个元素因为不考虑并发),然后进行准备赋值数据,使用 LocalDateTime.now() 和 BaseContext.getCurrentId() 来获取当前时间和当前操作人的ID。 - 判断操作类型,通过先前获取的数据库操作类型与 insert 和 update 进行比对。例子中是 update ,所以直接进入 else if 。这里通过反射机制获取实体类中的方法:自己定义一个
Method
对象setUpdateTime
,这个对象代表 entity 对象内的一个方法。 - 这个方法里面有两个参数: AutoFillConstant.SET_UPDATE_TIME 表示方法的名字,即 setUpdateTime 。 LocalDateTime.class 表示这个方法接受一个 LocalDateTime 类型的参数。通过
entity.getClass()
获取当前运行的类。再通过getDeclaredMethod
方法,我们获取到了当前实体类中声明的方法 setUpdateTime 。 - setUpdateTime.invoke(entity, now) 反射机制就反着看,
invoke
方法允许我们在运行时动态地调用对象的方法。我们实际上是调用了 entity 对象的 setUpdateTime 方法,并将当前时间 now 作为参数传递给这个方法。
3). 在 Mapper 的方法上加入 AutoFill 注解
2.2文件上传功能(阿里云OSS存储)
配置yaml文件,配置阿里云OSS的相关属性,包括endpoint、accessKeyId、accessKeySecret和bucketName,注意区分开发环境和生产环境的配置差异。创建AliOssProperties类,用@ConfigurationProperties
注解来自动读取这些配置以便后期用
其次,创建一个oss对象配置类,用来返回一个文件上传对象,这个对象里面存储了阿里云对应配置的属性。
接着创建一个AliOssUtil
工具类,里面有一个upload方法里面俩参数:字节数组和目标文件名,只需要搞懂,这个方法可以创建一个ossclient
实例,将oss属性参数存进去,因为存储路径由https://BucketName.Endpoint/ObjectName组成的,所以通过bucketName和endpoint以及最后的objectName获取到文件访问路径。
最后,在控制层进行上传业务,通过对应接口mapping进行上传操作,需要注意,在上传文件之前,需要注意文件名需要保证唯一性,通常的做法是使用UUID生成新的文件名,截取文件的后缀名,并保留原有的文件扩展名最后进行upload操作。
业务流程:
- 从
MultipartFile
对象中读取文件数据,使用getBytes()
方法获取文件的字节流。
生成唯一文件名:截取文件原始名称的扩展名,通常使用UUID来保证唯一性,生成一个新的文件名。 - 调用
aliOssUtil.upload(file.getBytes(), name)
方法上传文件。这里file.getBytes()
获取文件字节流,name
是新生成的唯一文件名。 - 上传成功,返回文件的访问路径,路径由
https://BucketName.Endpoint/ObjectName
组成。
2.3新增菜品功能注意
思考:初步分析新增菜品分为三部分:文件上传、新增菜品和新增菜品口味,所以新增的是两个表的数据
业务层逻辑是向菜品表和口味表插入数据,其中需要注意:
- DTO的类型转换和数据拷贝,插入菜品表是一行数据一行数据插入。
- 口味是有多种口味在一个属性内,就需要插入n条数据并判断非空。
- 口味表的dish_id就是菜品表的id,这里通过数据库的自动生成策略获取新插入菜品的ID,再将此ID设置到每个口味项的dishId上,并批量插入口味数据到dish_flavor表。
- 使用了@Transactional注解保证事务一致性,即菜品和口味的插入操作要么全部成功,要么全部失败。
DishMapper
接口定义了插入菜品数据的方法,并使用@AutoFill注解自动填充创建时间和更新时间等字段。对应XML文件提供了SQL语句实现插入操作,并启用useGeneratedKeys
返回自动生成的主键。
- useGeneratedKeys=“true”: 意思是Mybatis在执行完插入操作后,使用数据库自动生成的主键值填充返回的对象。
- keyProperty=“id”: 指定将自动生成的主键值填充到对象的哪个属性中。在这个例子中,id 是对象的主键属性名称。
DishFlavorMapper
接口用于操作口味数据,定义了批量插入口味数据的方法,其XML文件中通过foreach实现了一次性插入多条口味记录的功能。这里的separator表示分隔符为逗号,collection 属性用于指定集合名称,item表示集合中的每一个元素的别名。
DTO/VO/DO/PO理解:
通俗的解释:领域模型中的实体类分为四种模型:VO、DTO、DO和PO
VO:(View Object):视图对象,用于展示层。
DTO(Data Transfer Object):数据传输对象在后端,它的存在形式是java对象,也就是在controller里面定义的请求参数
DO(Domain Object):领域对象,它用来接收数据库对应的实体,是一种抽象化的数据状态,介于数据库与业务逻辑之间
PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系。
BO (Business Object) :业务对象 BO通常位于中间层或业务逻辑层。BO支持序列化和反序列化,用来是把业务逻辑封装为一个对象
方向:后端–>前端
VO:前端页面显示使用的数据,是后端传递给前端的。
方向:前端–>后端
DTO:前端调用后端接口的时候传递给后端
DO:controller中接收到DTO之后,新建一个DO传递给service,
PO:service接收到传递的DO之后,转换成一个PO,传给mapper的方法,进行持久化处理。
BO:用于微服务之间传输数据
2.4分页查询sql
左外连接:返回的是VO对象,用到了左外连接,连接条件是将category表中id字段等同为dish表中的category_id字段,并根据VO对象属性参数查询dish表和category的name字段。
动态 SQL:使用 标签动态构建 WHERE 子句,根据传入的参数决定是否添加过滤条件。
2.5删除菜品
业务规则:
- 可以一次删除一个菜品,也可以批量删除菜品
- 起售中的菜品不能删除
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除掉
4.1.3 表设计
在进行删除菜品操作时,会涉及到以下三张表。
注意事项:
- 在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
- setmeal_dish表为菜品和套餐关联的中间表。
- 若删除的菜品数据关联着某个套餐,此时,删除失败。
- 若要删除套餐关联的菜品数据,先解除两者关联,再对菜品进行删除。
删除代码优化:
2.5新增菜品接口
1.根据id查询菜品:
2.修改菜品
3.新增套餐
接口设计(共涉及到4个接口):
- 根据类型查询分类(已完成)
- 根据分类id查询菜品
- 图片上传(已完成)
- 新增套餐
业务规则:
- 套餐名称唯一
- 套餐必须属于某个分类
- 套餐必须包含菜品
- 名称、分类、价格、图片为必填项
- 添加菜品窗口需要根据分类类型来展示菜品
- 新增的套餐默认为停售状态
4.SpringCache缓存
其中store是cacheManager缓存,默认存储的一个HashMap类型数据
相关文章:

苍穹外卖学习记录
苍穹外卖学习 文章目录 苍穹外卖学习知识前提:**<font color"red">Nginx****<font color"red">Swagger** 1.管理员登录思路:详细步骤: 1.1新增员工问题1:在新增员工时,需要将当前登录…...

大数据成功应用商业解决方案的例子
大数据技术在商业领域的广泛应用已经成为现代商业决策和运营优化的关键驱动力。企业利用大数据分析获取洞察,从而提高运营效率、改善客户体验并实现更高的盈利。以下是几个典型的成功案例,这些企业通过大数据技术在各自领域中取得了显著的成果。 亚马逊…...

《Python使用sqlite3数据库》
《Python使用sqlite3数据库》 1、连接数据库2、创建游标3、执行SQL语句4、提交更改5、查询数据6、关闭连接 Python可以使用多种数据库,以下是一般步骤和示例: 1、连接数据库 首先要安装对应的数据库驱动。如使用MySQL数据库,要安装pymysql库…...

XHCI 1.2b 规范摘要(14)
系列文章目录 XHCI 1.2b 规范摘要(一) XHCI 1.2b 规范摘要(二) XHCI 1.2b 规范摘要(三) XHCI 1.2b 规范摘要(四) XHCI 1.2b 规范摘要(五) XHCI 1.2b 规范摘要…...
(蓝桥杯C/C++)——基础算法(下)
目录 一、时空复杂度 1.时间复杂度 2.空间复杂度 3.分析技巧 4.代码示例 二、递归 1.递归的介绍 2.递归如何实现 3.递归和循环的比较 4.代码示例 三、差分 1.差分的原理和特点 2.差分的实现 3.例题讲解 四、枚举 1.枚举算法介绍 2.解空间的类型 3. 循环枚举解…...

详解Rust标准库:VecDeque 队列
theme: github highlight: an-old-hope 查看本地官方文档 安装rust后运行 rustup doc查看The Standard Library即可获取标准库内容 std::connections::VecDeque定义 队列是遵循先入先出规则的线性数据结构,在内存中不一定连续 VecDeque定义:可增长…...

网络协议都有哪些?
网络协议是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。以下是一些常见的网络协议: TCP/IP协议:传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议、Internet国际互联网络的基础。由网络层的…...

非公平锁和公平锁的区别
公平锁(Fair Lock): 公平锁遵循 FIFO(先进先出)原则。当多个线程在等待锁时,公平锁会确保等待时间最长的线程优先获得锁。 这种锁机制可以避免线程饥饿(starvation),即某…...

11月7日星期四今日早报简报微语报早读
11月7日星期四,农历十月初七,早报#微语早读。 1、河南:旅行社组织1000人次境外游客在豫住宿2夜以上,可申请激励奖补; 2、主播宣称下播后商品恢复原价构成欺诈,广州市监:罚款5万元;…...

【Python】轻松实现机器翻译:Transformers库使用教程
轻松实现机器翻译:Transformers库使用教程 近年来,机器翻译技术飞速发展,从传统的基于规则的翻译到统计机器翻译,再到如今流行的神经网络翻译模型,尤其是基于Transformer架构的模型,翻译效果已经有了质的飞…...

【数据集】【YOLO】【目标检测】道路结冰数据集 1527 张,YOLO目标检测实战训练教程!
数据集介绍 【数据集】道路结冰数据集 1527 张,目标检测,包含YOLO/VOC格式标注。数据集中包含2种分类:“clear_road, ice_road”。数据集来自国内外图片网站和视频截图,部分数据经过数据增强处理。检测范围监控视角检测、无人机视…...

Java链表及源码解析
文章目录 创建一个ILindkedList接口创建方法(模拟实现链表方法)创建MyLinkedList来实现接口的方法创建链表节点addFirst方法(新增头部属性)addLast方法(新增到末尾一个属性)remove方法(删除指定属性)addInd…...

十、快速入门go语言之方法
文章目录 方法:one: 方法的概念:star2: 内嵌类型的方法和继承:star2: 多重继承 📅 2024年5月9日 📦 使用版本为1.21.5 方法 1️⃣ 方法的概念 ⭐️ 在Go语言中没有类这个概念,可以使用结构体来实现,那类方法呢?Go也…...

JVM 处理多线程并发执行
JVM(Java Virtual Machine)在处理多线程并发执行方面具有强大的支持,主要依赖于其内置的线程模型、内存模型以及同步机制。 JVM 通过以下关键机制和组件来管理多线程并发执行: 1. 线程模型 Java 线程与操作系统线程:…...

【D3.js in Action 3 精译_039】4.3 D3 面积图的绘制方法及其边界标签的添加
当前内容所在位置: 第四章 直线、曲线与弧线的绘制 ✔️ 4.1 坐标轴的创建(上篇) 4.1.1 D3 中的边距约定(中篇)4.1.2 坐标轴的生成(中篇) 4.1.2.1 比例尺的声明(中篇)4.1…...

布谷直播源码部署服务器关于数据库配置的详细说明
布谷直播源码搭建部署配置接口数据库 /public/db.php(2019年8月后的系统在该路径下配置数据库,老版本继续走下面的操作) 在项目代码中执行命令安装依赖库(⚠️注意:如果已经有了vendor内的依赖文件的就不用执行了&am…...

Xfce桌面设置右键菜单:用右键打开VSCode
前言 AlmaLinux安装VSCode之后始终没有找到如何用右键菜单打开VSCode,比Windows麻烦多了。每次都需要先找到文件夹,然后用系统自带的Open In Terminal打开终端,再输入code .,才能够在当前文件夹中快速打开VSCode。那么࿰…...

【NLP自然语言处理】深入探索Self-Attention:自注意力机制详解
目录 🍔 Self-attention的特点 🍔 Self-attention中的归一化概述 🍔 softmax的梯度变化 3.1 softmax函数的输入分布是如何影响输出的 3.2 softmax函数在反向传播的过程中是如何梯度求导的 3.3 softmax函数出现梯度消失现象的原因 &…...

Pytorch训练时报nan
0. 引言 Pytorch训练时在batchN时loss为nan。经过断点检查发现在batchN-1时,网络参数非nan,输出非nan,但梯度为nan,导致网络参数已经全部被更新为nan,遇到这种情况应该如何排查,如何避免?由于导…...

JavaScript定时器详解:setTimeout与setInterval的使用与注意事项
在JavaScript中,定时器用于在指定的时间间隔后或周期性地执行代码。JavaScript 提供了两种主要的定时器函数:setTimeout 和 setInterval。以下是它们的详细解释和实现方式: 1. setTimeout setTimeout 函数用于在指定的毫秒数后执行一次函数…...

CSS——选择器、PxCook软件、盒子模型
选择器 结构伪类选择器 作用:根据元素的结构关系查找元素。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&quo…...

Mysql 大表limit查询优化原理实战
文章目录 1 大表查询无条件优化&原理(入门)2 大表查询带 条件 优化&原理(进阶)2.1 where 后面的查询字段只有一个时,要求该字段是索引字段2.2 where 后面的查询字段有多个时,尽量让查询字段为索引字段且字段值基数大 3 大表查询带 排序 优化&…...

在vscode中开发运行uni-app项目
确保电脑已经安装配置好了node、vue等相关环境依赖 进行项目的创建 vue create -p dcloudio/uni-preset-vue 项目名 vue create -p dcloudio/uni-preset-vue uni-app 选择模版 这里选择【默认模版】 项目创建成功后在vscode中打开 第一次打开项目 pages.json 文件会报错&a…...

【JavaEE初阶 — 多线程】Thread的常见构造方法&属性
目录 Thread类的属性 1.Thread 的常见构造方法 2.Thread 的几个常见属性 2.1 前台线程与后台线程 2.2 setDaemon() 2.3 isAlive() Thread类的属性 Thread 类是JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread 对象与之关联&am…...

ctfshow(316)--XSS漏洞--反射性XSS
Web316 进入界面: 审计 显示是关于反射性XSS的题目。 思路 首先想到利用XSS平台解题,看其他师傅的wp提示flag是在cookie中。 当前页面的cookie是flagyou%20are%20not%20admin%20no%20flag。 但是这里我使用XSS平台,显示的cookie还是这样…...

ubuntu22.04安装conda
在 Ubuntu 22.04 上安装 Conda 可以通过以下步骤进行: 下载 Miniconda(轻量级版本的 Conda): 打开终端并运行以下命令以下载 Miniconda 安装脚本: wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-…...

D58【python 接口自动化学习】- python基础之异常
day58 异常捕获 学习日期:20241104 学习目标:异常 -- 74 自定义异常捕获:如何定义业务异常? 学习笔记: 自定义异常的用途 自定义异常的方法 # 抛出一个异常 # raise ValueError(value is error) # ValueError: val…...

Java项目实战II基于Spring Boot的便利店信息管理系统(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在快节奏的…...

Java-日期计算工具类DateCalculator
DateCalculator是用于日期计算的工具类。这个工具类将包括日期的加减、比较、周期计算、日期 范围生成等功能。 import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Period; import java.time.temporal.ChronoUnit;…...

单片机串口接收状态机STM32
单片机串口接收状态机stm32 前言 项目的芯片stm32转国产,国产芯片的串口DMA接收功能测试不通过,所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣,不过我的芯片已经主频跑…...