微服务day04
网关


网关路由
快速入门


创建新模块:hm-gateway继承hmall父项目。
引入依赖:引入网关依赖和nacos负载均衡的依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.heima</groupId><artifactId>hmall</artifactId><version>1.0.0</version></parent><groupId>com.wmmczk</groupId><artifactId>hm-gateway</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--网关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos discovery--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--负载均衡--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
编写配置文件:一个模块中有多个collection类就可以写多个判断路径。
server:port: 8080
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.21.101:8848gateway:routes:- id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务- Path=/items/**,/search/** # 这里是以请求路径作为判断规则- id: carturi: lb://cart-servicepredicates:- Path=/carts/**- id: useruri: lb://user-servicepredicates:- Path=/users/**,/addresses/**- id: tradeuri: lb://trade-servicepredicates:- Path=/orders/**- id: payuri: lb://pay-servicepredicates:- Path=/pay-orders/**
路由属性



网关登陆校验


自定义过滤器


GlobaFilter自定义过滤器

//Ordered接口为spring中的排序接口,为核心接口
//GlobalFilter要在NettyRoutingFilter之前,NettyRoutingFilter的Ordered的值为int的最大值,确保其最后执行
public class MyGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//获取requestServerHttpRequest request = exchange.getRequest();//获取header头HttpHeaders headers = request.getHeaders();System.out.println("headers = " + headers);return chain.filter(exchange);//将exchange传给下一个过滤器}@Overridepublic int getOrder() {// 过滤器执行顺序,值越小,优先级越高return 0;}
}
GatewayFilter自定义过滤器

登录校验
JWT工具
登录校验需要用到JWT,而且JWT的加密需要秘钥和加密工具。这些在hm-service中已经有了,我们直接拷贝过来:

具体作用如下:
-
AuthProperties:配置登录校验需要拦截的路径,因为不是所有的路径都需要登录才能访问 -
JwtProperties:定义与JWT工具有关的属性,比如秘钥文件位置 -
SecurityConfig:工具的自动装配 -
JwtTool:JWT工具,其中包含了校验和解析token的功能 -
hmall.jks:秘钥文件
其中AuthProperties和JwtProperties所需的属性要在application.yaml中配置
hm:jwt:location: classpath:hmall.jks # 秘钥地址alias: hmall # 秘钥别名password: hmall123 # 秘钥文件密码tokenTTL: 30m # 登录有效期auth:excludePaths: # 无需登录校验的路径- /search/**- /users/login- /items/**
创建自定义拦截器:
package com.hmall.gateway.Filte;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.AntPathMatcher;
import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.util.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {//用于读取配置文件中要放行的路径private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();private final JwtTool jwtTool;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//获取requsetServerHttpRequest request = exchange.getRequest();//获取path进行判断是否需要拦截RequestPath path = request.getPath();//使工具类进行判断if (isPath(path)){//放行return chain.filter(exchange);}//获取tokenString token = null;HttpHeaders headers = request.getHeaders();List<String> list = headers.get("Authorization");if (!CollUtil.isEmpty(list)){//给token赋值token = list.get(0);}//校验tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {//token无效进行拦截//获取Response进行编辑ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();//该状态可以使后面的过滤器不在执行,返回错误}//TODO 传递用户信息System.out.println("userId = " + userId);return chain.filter(exchange);}private boolean isPath(RequestPath path) {//转为String类型String string = path.toString();//将配置文件中定义的路径进行遍历for (String excludePath : authProperties.getExcludePaths()) {if (antPathMatcher.match(excludePath, string)){//使用工具类进行匹配 antPathMatcher.match ,匹配成功返回truereturn true;}}return false;}@Overridepublic int getOrder() {return 0;}
}
网关传递用户信息


//TODO 传递用户信息String userinfo = userId.toString();//exchange提供了修改请求头信息的方法mutateServerWebExchange build = exchange.mutate().request(b -> b.header("user-info",userinfo)).build();System.out.println("userId = " + userId);return chain.filter(build);
package com.hmall.gateway.Filte;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.AntPathMatcher;
import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.util.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.util.List;@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {//用于读取配置文件中要放行的路径private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();private final JwtTool jwtTool;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//获取requsetServerHttpRequest request = exchange.getRequest();//获取path进行判断是否需要拦截RequestPath path = request.getPath();//使工具类进行判断if (isPath(path)){//放行return chain.filter(exchange);}//获取tokenString token = null;HttpHeaders headers = request.getHeaders();List<String> list = headers.get("Authorization");if (!CollUtil.isEmpty(list)){//给token赋值token = list.get(0);}//校验tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {//token无效进行拦截//获取Response进行编辑ServerHttpResponse response = exchange.getResponse();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();//该状态可以使后面的过滤器不在执行,返回错误}//TODO 传递用户信息String userinfo = userId.toString();//exchange提供了修改请求头信息的方法mutateServerWebExchange build = exchange.mutate().request(b -> b.header("user-info",userinfo)).build();System.out.println("userId = " + userId);return chain.filter(build);}private boolean isPath(RequestPath path) {//转为String类型String string = path.toString();//将配置文件中定义的路径进行遍历for (String excludePath : authProperties.getExcludePaths()) {if (antPathMatcher.match(excludePath, string)){//使用工具类进行匹配 antPathMatcher.match ,匹配成功返回truereturn true;}}return false;}@Overridepublic int getOrder() {return 0;}
}

定义拦截器:
拦截器实现了HandlerInterceptor接口
package com.hmall.common.interceptor;import cn.hutool.core.util.StrUtil;
import com.hmall.common.utils.UserContext;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;// 拦截器需要进行注册
public class UserINfointerceptor implements HandlerInterceptor {//请求到达时执行获取用户信息@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StrUtil.isNotBlank(userInfo)) {// 不为空,保存到ThreadLocalUserContext.setUser(Long.valueOf(userInfo));System.out.println(userInfo);}// 3.放行return true;}//业务执行后的操作,删除线程存储中的数据@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserContext.removeUser();}
}
由于拦截器需要配置类进行注册才可以启用,创建一个配置类:
由于网关模块并没有使用springMVC进行编写,所以网关就会报错,因此使用该注解:@ConditionalOnClass(DispatcherServlet.class)//用于判断是否存在某个类,存在就加载,不存在就不加载,使得网关模块不在加载该配置类,避免报错。
package com.hmall.common.config;import com.hmall.common.interceptor.UserINfointerceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration//配置类的指定注释
@ConditionalOnClass(DispatcherServlet.class)//用于判断是否存在某个类,存在就加载,不存在就不加载
public class MyUserInfoConfig implements WebMvcConfigurer {//将拦截器添加到容器中@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserINfointerceptor ());}
}
在hm-common模块下的文件 spring.factories 中添加路径,在启动时扫描该配置
添加该代码:
com.hmall.common.config.MyUserInfoConfig,\
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MyUserInfoConfig,\com.hmall.common.config.JsonConfig
OpenFeign传递用户

在Api模块中的
DefaultFeignConfig配置类中添加拦截器,拦截所有的okhttp请求即所有的微服务之间的请求
使用匿名内部类进行配置
由于需要获取用户信息,添加hm-common的依赖获取UserContext对象。
<!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency>
@Beanpublic RequestInterceptor MyRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate requestTemplate) {Long user = UserContext.getUser();if (user != null){requestTemplate.header("user-info",user.toString());}}};}
package com.hmall.api.config;import com.hmall.common.utils.UserContext;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.FULL;}@Beanpublic RequestInterceptor MyRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate requestTemplate) {Long user = UserContext.getUser();if (user != null){requestTemplate.header("user-info",user.toString());}}};}
}
微服务的登录解决方案:

配置管理


配置共享
我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。分为两步:
-
在Nacos中添加共享配置
-
微服务拉取配置
添加共享配置:
1、在nacos的配置列表下进行新建共享配置
- Data ID:表示共享配置文件的名称,即在项目中引入是的名字
- 使用默认分组
- 配置内容即要共享的配置,可以使用占位符进行动态的设置配置数据

jdbc的更共享配置:shared-jdbc.yaml ,有数据库设置和mp的配置使用占位符来进行不同的设置。
占位符后面的 :表示默认字符,即没有设置的默认数据
spring:datasource:url: jdbc:mysql://${hm.db.host:192.168.21.101}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: ${hm.db.user:root}password: ${hm.db.pw:123}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto
日志的共享配置: shared-log.yaml
logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"
swagger的共享配置;shared-swagger.yaml
knife4j:enable: trueopenapi:title: ${hm.swagger.title:黑马商城接口文档}description: ${hm.swagger.description:黑马商城接口文档}email: ${hm.swagger.email:zhanghuyi@itcast.cn}concat: ${hm.swagger.concat:虎哥}url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- ${hm.swagger.package}
接下来,我们要在微服务拉取共享配置。将拉取到的共享配置与本地的application.yaml配置合并,完成项目上下文的初始化。
不过,需要注意的是,读取Nacos配置是SpringCloud上下文(ApplicationContext)初始化时处理的,发生在项目的引导阶段。然后才会初始化SpringBoot上下文,去读取application.yaml。
也就是说引导阶段,application.yaml文件尚未读取,根本不知道nacos 地址,该如何去加载nacos中的配置文件呢?
SpringCloud在初始化上下文的时候会先读取一个名为bootstrap.yaml(或者bootstrap.properties)的文件,如果我们将nacos地址配置到bootstrap.yaml中,那么在项目引导阶段就可以读取nacos中的配置了。

/2、在项目中引入依赖:
<!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>
创建bootstrap.yml
spring:application:name: cart-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.21.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置
修改原有的配置文件,为占位符赋值 application.yaml
server:port: 8082
feign:okhttp:enabled: true # 开启OKHttp连接池支持
hm:swagger:title: 购物车服务接口文档package: com.hmall.cart.controllerdb:database: hm-cart
配置热更新

注意文件的dataId格式:
[服务名]-[spring.active.profile].[后缀名]
文件名称由三部分组成:
-
服务名:我们是购物车服务,所以是cart-service -
spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置 -
后缀名:例如yaml
设置nacos配置热更新文件:
名字为:cart-service
hm:cart:maxAmount: 10
在代码中创建配置类:,设置配置文件关联。
创建包: config
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {private Integer maxAmount;
}
修改业务文件中判断购物车最大数量:
private void checkCartsFull(Long userId) {int count = Math.toIntExact(lambdaQuery().eq(Cart::getUserId, userId).count());if (count >= cartProperties.getMaxAmount()) {throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", cartProperties.getMaxAmount()));}}
动态路由
动态路由的相关操作
相关文章:
微服务day04
网关 网关路由 快速入门 创建新模块:hm-gateway继承hmall父项目。 引入依赖:引入网关依赖和nacos负载均衡的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"…...
Spring Boot 集成JWT实现Token验证详解
文章目录 Spring Boot 集成JWT实现Token验证详解一、引言二、JWT和Token基础1、什么是Token2、什么是JWT3、JWT的结构4、JWT的工作原理 三、集成JWT1、引入JWT依赖2、创建Token工具类3、创建拦截器4、注册拦截器 四、总结 Spring Boot 集成JWT实现Token验证详解 一、引言 在现…...
如何快速搭建一个spring boot项目
一、准备工作 1.1 安装JDK:确保计算机上已安装Java Development Kit (JDK) 8或更高版本、并配置了环境变量 1.2 安装Maven:下载并安装Maven构建工具,这是Spring Boot官方推荐的构建工具。 1.3 安装代码编辑器:这里推荐使用Inte…...
学习笔记:黑马程序员JavaWeb开发教程(2024.11.9)
9.1 Mybatis-基础操作-环境准备 这里也没做,到时候写案例,如果需要环境配置什么的,可以看看这个 9.2 Mybatis-基础操作-删除 删除需要动态获取需要删除的id,使用方法传参,#{}的方式实现 在编写的delete方法中&a…...
【软考】系统分析师第二版 新增章节 第20章微服务系统分析与设计
微服务系统是一类基于微服务架构风格的分布式系统,它将应用程序拆分成多个独立的小型服务,每个服务都运行在独立的进程中,并采用轻量级通信协议进行通信。这些服务可以由不同的团队开发、不同的编程语言编写,并且可以按需部署。微…...
抓包工具WireShark使用记录
目录 网卡选择: 抓包流程: 捕获过滤器 常用捕获过滤器: 抓包数据的显示 显示过滤器: 常用的显示过滤器: 实际工作中,在平台对接,设备对接等常常需要调试接口,PostMan虽然可以进…...
C++上机实验|多态性编程练习
1.实验目的 (1)理解多态性的概念。 (2)掌握如何用虚函数实现动态联编 (3)掌握如何利用虚基类。 2.实验内容 设计一个飞机类 plane,由它派生出歼击机类fighter和轰炸机类 bomber,歼击机类fighter 和轰炸机类bomber 又共同派生出歼轰机(多用途战斗机)。利用虚函数和虚基类描述…...
php伪协议介绍
PHP伪协议共有12种,具体如下: file:// — 访问本地文件系统http:// — 访问 HTTP(s) 网址ftp:// — 访问 FTP(s) URLsphp:// — 访问各个输入/输出流(I/O streams)php://stdin, php://stdout 和 php://stderrphp://inputphp://outputphp://memory 和 php://tempphp://filte…...
『事善能』MySQL基础 — 2.MySQL 5.7安装(一)
1、通过msi安装软件进行MySQL安装 (1)点击运行MySQL安装文件 (2)选择安装类型 我们选择自定义安装,点击Next。 说明 Develop Default:默认开发类型,安装MySQL服务器以及开发MySQL应用所需要的工…...
漫谈分布式唯一ID
文章目录 本系列前言UUIDDB自增主键Redis incr命令号段模式雪花算法 本系列 漫谈分布式唯一ID(本文)分布式唯一ID生成(二):leaf分布式唯一ID生成(三):uid-generator(待完…...
【复旦微FM33 MCU 开发指南】ADC
前言 本系列基于复旦微FM33LC0系列单片机的DataSheet编写,旨在提供手册解析和开发指南。 本文章及本系列其他文章将持续更新,本系列其它文章请跳转【复旦微FM33 MCU 外设开发指南】总集篇 本文章最后更新日期:2024/11/09 全文字数ÿ…...
ORB_SLAM3安装
ORB_SLAM3安装 一.前期准备1.1ubuntu查看当前版本的命令1.2 根据ubuntu版本,更新下载软件源1.3 先下载git1.4 vim语法高亮1.5 常见的linux命令 二.ORB-SLAM3下载2.1 ORB_SLAM3源码下载2.2 安装依赖库2.2.1 依赖库2.2.2 安装pangolin2.2.3 安装opencv2.2.4 Eigen3安装…...
GoLang协程Goroutiney原理与GMP模型详解
本文原文地址:GoLang协程Goroutiney原理与GMP模型详解 什么是goroutine Goroutine是Go语言中的一种轻量级线程,也成为协程,由Go运行时管理。它是Go语言并发编程的核心概念之一。Goroutine的设计使得在Go中实现并发编程变得非常简单和高效。 以下是一些…...
全文检索ElasticSearch到底是什么?
学习ElasticSearch之前,我们先来了解一下搜索 1 搜索是什么 ① 概念:用户输入想要的关键词,返回含有该关键词的所有信息。 ② 场景: 1互联网搜索:谷歌、百度、各种新闻首页; 2 站内搜索ÿ…...
FPGA实现串口升级及MultiBoot(五)通过约束脚本添加IPROG实例
本文目录索引 一个指令和三种方式通过约束脚本添加Golden位流工程MultiBoot位流工程验证example1总结代码缩略词索引: K7:Kintex 7V7:Vertex 7A7:Artix 7MB:MicroBlaze上一篇文章种总结了MultiBoot 关键技术,分为:一个指令、二种位流、三种方式、四样错误。针对以上四句话我…...
文献阅读 | Nature Methods:使用 STAMP 对空间转录组进行可解释的空间感知降维
文献介绍 文献题目: 使用 STAMP 对空间转录组进行可解释的空间感知降维 研究团队: 陈金妙(新加坡科学技术研究局) 发表时间: 2024-10-15 发表期刊: Nature Methods 影响因子: 36.1࿰…...
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(1-3)
在前端开发中,模块化是一个重要的概念,不同的模块化标准有不同的特点和适用场景。webpack 同时支持 CommonJS 和 ES6 Module,因此需要理解它们在互操作时 webpack 是如何处理的。 同模块化标准 如果导出和导入使用的是同一种模块化标准&…...
[NewStar 2024] week5完结
每次都需要用手机验证码登录,题作的差不多就没再进过。今天把week5解出的部分记录下。好像时间过去很久了。 Crypto 没e也能完 这题给了e,p,q,dp,dq。真不清楚还缺啥 long_to_bytes(pow(c,dp,p)) 格格你好棒 给了a,b和提示((p2*r) * 3*a q) % b < 70 其中r…...
IntelliJ IDEA的快捷键
IntelliJ IDEA 是一个非常强大的集成开发环境,它提供了大量的快捷键来加速开发者的日常工作。这里为您整理了一份 IntelliJ IDEA 的快捷键大全,包含了编辑、导航、重构、运行等多个方面的快捷键。请注意,这些快捷键是基于 Windows 版本的 Int…...
暮雨直播 1.3.2 | 内置直播源,频道丰富,永久免费
暮雨直播是一款内置直播源的电视直播应用程序,提供丰富的频道内容,包括教学、首页、一线、博主、解说、动漫、堆堆等。该应用的内置直播源持续更新维护,确保用户可以稳定地观看各种电视频道。暮雨直播承诺永久免费,为用户提供了一…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
