【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制
【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制
- Spring SpEL
- 基本表达式
- 类相关表达式
- 表达式模板
- SpEL表达式实现权限控制
- PreAuth
- AuthFun
- PreAuthAspect
- UserController
- SpelParserUtils
Spring SpEL
Spring 表达式语言 SpEL 是一种非常强大的表达式语言,它支持在运行时查询和操作对象图。 它提供了许多高级功能,例如方法调用和基本的字符串模板功能。表达式语言给静态Java语言增加了动态功能。
Spring 表达式语言最初是为 Spring 社区创建的,它拥有一种受良好支持的表达式语言,可用于 Spring 产品组合中的所有产品。 虽然 SpEL 是 Spring 产品组合中表达式评估的基础,但它不直接与 Spring 绑定,可以独立使用
Spring Security框架中启用prePost注解的支持就是使用SpEL表达式实现的权限控制
- @PreAuthorize(“hasAuthority(‘save’)”)
- @PreAuthorize(“isAnonymous()”)
- @PreAuthorize(“isAuthenticated()”)
SpEL支持如下表达式:
- 基本表达式: 字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式
- 类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用
- 集合相关表达式:内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义
- 其他表达式:模板表达式。
注:SpEL表达式中的关键字是不区分大小写的。
基本表达式
/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test01() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("1+2");// 3)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue());String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);int int1 = parser.parseExpression("1").getValue(Integer.class);long long1 = parser.parseExpression("-1L").getValue(long.class);float float1 = parser.parseExpression("1.1").getValue(Float.class);double double1 = parser.parseExpression("1.1E+2").getValue(double.class);int hex1 = parser.parseExpression("0xa").getValue(Integer.class);long hex2 = parser.parseExpression("0xaL").getValue(long.class);boolean true1 = parser.parseExpression("true").getValue(boolean.class);boolean false1 = parser.parseExpression("false").getValue(boolean.class);Object null1 = parser.parseExpression("null").getValue(Object.class);System.out.println("str1=" + str1);System.out.println("int1=" + int1);System.out.println("long1=" + long1);System.out.println("float1=" + float1);System.out.println("double1=" + double1);System.out.println("hex1=" + hex1);System.out.println("hex2=" + hex2);System.out.println("true1=" + true1);System.out.println("false1=" + false1);System.out.println("null1=" + null1);
}
/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test02() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");// 3)构造上下文:准备比如变量定义等等表达式需要的上下文数据。EvaluationContext context = new StandardEvaluationContext();context.setVariable("end", "!");// 4)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue(context));
}
类相关表达式
@Data
@Component("ss")
public class User {private String username;private String address;private Integer age;public String sayHello(String username) {return "hello " + username;}public String sayHello(Integer age) {return "hello " + username + ";age=" + age;}public String sayHello() {return "hello " + username;}
}
/**
* 类相关表达式: 变量定义及引用
*/
@Test
public void test03() {String expression = "#user.username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);;ctx.setVariable("user", user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
@Test
public void test04() {String expression = "username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);// user 对象设置为 rootObject,那么表达式中就不需要 #user.ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*/
@Test
public void test05() {String expression = "sayHello(99)";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*
* 调用无参的 sayHello
*/
@Test
public void test06() {String expression = "sayHello()";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: Bean引用
*/
@Test
public void test07() {// 通过 SpEL 表达式来调用这个名为 ss 的 bean 中的 sayHello 方法String expression = "@ss.sayHello('spel bean引用')";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();// 给配置的上下文环境设置一个 bean 解析器,这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
表达式模板
/**
* 表达式模板:
* 模板表达式就是由字面量与一个或多个表达式块组成。每个表达式块由“前缀+表达式+后缀”形式组成,如“${1+2}”即表达式块
*
*/
@Test
public void test08() {//创建解析器SpelExpressionParser parser = new SpelExpressionParser();//创建解析器上下文ParserContext context = new TemplateParserContext("%{", "}");Expression expression = parser.parseExpression("你好:%{#name},我们正在学习:%{#lesson}", context);//创建表达式计算上下文EvaluationContext evaluationContext = new StandardEvaluationContext();evaluationContext.setVariable("name", "zysheep");evaluationContext.setVariable("lesson", "SpEL表达式!");//获取值String value = expression.getValue(evaluationContext, String.class);System.out.println(value);
}
SpEL表达式实现权限控制
PreAuth
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {/***** permissionAll()-----只要配置了角色就可以访问* hasPermission("MENU.QUERY")-----有MENU.QUERY操作权限的角色可以访问* permitAll()-----放行所有请求* denyAll()-----只有超级管理员角色才可访问* hasAuth()-----只有登录后才可访问* hasTimeAuth(1,,10)-----只有在1-10点间访问* hasRole(‘管理员’)-----具有管理员角色的人才能访问* hasAllRole(‘管理员’,'总工程师')-----同时具有管理员、总工程师角色的人才能访问** Spring el* 文档地址:https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/core.html#expressions*/String value();}
AuthFun
/*** <p>* 类相关表达式: Bean引用,参数要用单引号包括** @PreAuth("@af.hasPermission('ADMIN, USER')")* </p>** @author : lyw* @since : 2023/11/23 16:01*/
@Component("af")
public class AuthFun {/*** 判断角色是否具有接口权限** @return {boolean}*/public boolean permissionAll() {//TODO 读取数据库权限数据return true;}/*** 判断角色是否具有接口权限** @param permission 权限编号,对应菜单的MENU_CODE* @return {boolean}*/public boolean hasPermission(String permission) {return hasRole(permission);}/*** 放行所有请求** @return {boolean}*/public boolean permitAll() {return true;}/*** 只有超管角色才可访问** @return {boolean}*/public boolean denyAll() {return hasRole("ADMIN");}/*** 是否已授权** @return {boolean}*/public boolean hasAuth() {return true;}/*** 是否有时间授权** @param start 开始时间* @param end 结束时间* @return {boolean}*/public boolean hasTimeAuth(Integer start, Integer end) {Integer hour = DateUtil.hour(new Date(), true);return hour >= start && hour <= end;}/*** 判断是否有该角色权限** @param role 单角色* @return {boolean}*/public boolean hasRole(String role) {return hasAnyRole(role);}/*** 判断是否具有所有角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAllRole(String... role) {for (String r : role) {if (!hasRole(r)) {return false;}}return true;}/*** 判断是否有该角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAnyRole(String... role) {return Arrays.stream(role).anyMatch(item -> StringUtils.equals("ADMIN", item));}
}
PreAuthAspect
@Aspect
@Component
public class PreAuthAspect {@Autowiredprivate BeanFactory applicationContext;private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();@Pointcut("@annotation(cn.zysheep.annotation.PreAuth) || @within(cn.zysheep.annotation.PreAuth)")public void pointcut() {}@Around("pointcut()")public Object preAuth(ProceedingJoinPoint point) throws Throwable {if (handleAuth(point)) {return point.proceed();}throw new BizException("用户无权限,非法用户!");}private boolean handleAuth(ProceedingJoinPoint point) {MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;Method method = ms.getMethod();// 读取权限注解,优先方法上,没有则读取类PreAuth preAuth = method.getAnnotation( PreAuth.class);// 判断表达式String condition = preAuth.value();if (StringUtils.isNotBlank(condition)) {// @PreAuth("@af.hasPermission('ADMIN, USER')")Expression expression = EXPRESSION_PARSER.parseExpression(condition);// 方法参数值Object[] args = point.getArgs();StandardEvaluationContext context = getEvaluationContext(method, args);// 获取解析计算的结果return expression.getValue(context, Boolean.class);}return false;}/*** 获取方法上的参数** @param method 方法* @param args 变量* @return {SimpleEvaluationContext}*/private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {// 初始化Spel表达式上下文,并设置 AuthFunStandardEvaluationContext context = new StandardEvaluationContext();// 设置表达式支持spring beancontext.setBeanResolver(new BeanFactoryResolver(applicationContext));// 可以从session中获取登录用户的权限
// for (int i = 0; i < args.length; i++) {
// // 读取方法参数
// MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// // 设置方法 参数名和值 为spel变量
// context.setVariable(methodParam.getParameterName(), args[i]);
// }return context;}
}
UserController
@RestController
@RequestMapping("/api")
@Slf4j
public class UserController {@GetMapping("/save")@PreAuth("@af.hasPermission('ADMIN')")public R save() {log.info("====执行保存业务逻辑=====");return R.success();}@GetMapping("/get")@PreAuth("@af.hasPermission('USER')")public R get() {log.info("====执行保存业务逻辑=====");return R.success();}}
SpelParserUtils
public final class SpelParserUtils {private static final String EXPRESSION_PREFIX = "#{";private static final String EXPRESSION_SUFFIX = "}";/*** 表达式解析器*/private static ExpressionParser expressionParser = new SpelExpressionParser();/*** 参数名解析器,用于获取参数名*/private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();private SpelParserUtils(){}/*** 解析spel表达式** @param method 方法* @param args 参数值* @param spelExpression 表达式* @param clz 返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz, T defaultResult) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 解析spel表达式** @param method 方法* @param args 参数值* @param spelExpression 表达式* @param clz 返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param 参数名* @param paramValue 参数值* @param spelExpression 表达式* @param clz 返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue, String spelExpression, Class<T> clz) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param 参数名* @param paramValue 参数值* @param spelExpression 表达式* @param clz 返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue,String spelExpression, Class<T> clz, T defaultResult) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 获取spel表达式后的结果** @param context 解析器上下文接口* @param spelExpression 表达式* @param clz 返回结果的类型* @return 执行spel表达式后的结果*/private static <T> T getResult(EvaluationContext context,String spelExpression, Class<T> clz){try {//解析表达式Expression expression = parseExpression(spelExpression);//获取表达式的值return expression.getValue(context, clz);} catch (Exception e) {log.error(e.getMessage(),e);}return null;}/*** 解析表达式* @param spelExpression spel表达式* @return*/private static Expression parseExpression(String spelExpression){// 如果表达式是一个#{}表达式,需要为解析传入模板解析器上下文if(spelExpression.startsWith(EXPRESSION_PREFIX) && spelExpression.endsWith(EXPRESSION_SUFFIX)){return expressionParser.parseExpression(spelExpression,new TemplateParserContext());}return expressionParser.parseExpression(spelExpression);}}
相关文章:
【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制
【SpringBoot应用篇】【AOP注解】SpringBootSpEL表达式基于注解实现权限控制 Spring SpEL基本表达式类相关表达式表达式模板 SpEL表达式实现权限控制PreAuthAuthFunPreAuthAspectUserControllerSpelParserUtils Spring SpEL Spring 表达式语言 SpEL 是一种非常强大的表达式语言…...
Java研学-HTTP 协议
一 概述 1 概念和作用 概念:HTTP 是 HyperText Transfer Protocol (超文本传输协议)的简写,它是 TCP/IP 协议之上的一个应用层协议。简单理解就是 HTTP 协议底层是对 TCP/IP 协议的封装。 作用:用于规定浏览器和服务器之间数据传输的格式…...
差生文具多之(二): perf
栈回溯和符号解析是使用 perf 的两大阻力,本文以应用程序 fio 的观测为例子,提供一些处理它们的经验法则,希望帮助大家无痛使用 perf。 前言 系统级性能优化通常包括两个阶段:性能剖析和代码优化: 性能剖析的目标是寻…...
【SPI和API有什么区别】
✅什么是SPI,和API有什么区别 ✅典型解析🟢拓展知识仓🟢如何定义一个SPI🟢SPI的实现原理 ✅SPI的应用场景SpringDubbo ✅典型解析 Java 中区分 API和 SPI,通俗的进: API和 SPI 都是相对的概念,他们的差别只…...
Day67力扣打卡
打卡记录 美丽塔 II(前缀和 单调栈) 链接 class Solution:def maximumSumOfHeights(self, maxHeights: List[int]) -> int:n len(maxHeights)stack collections.deque()pre, suf [0] * n, [0] * nfor i in range(n):while stack and maxHeights…...
什么是网站监控?
网站监控是跟踪网站的可用性和性能,以最小化宕机时间,优化性能并确保顺畅的用户体验。维护网站正常运行对于任何企业来说都是至关重要的,因而对大多数业务来说,网站应用监控都是一个严峻的挑战。Applications Manager网站应用监控…...
游戏软件提示d3dcompiler_43.dll的五个解决方法,亲测靠谱
在使用电脑进行工作,玩游戏的时候,我们常常会遇到一些错误提示,其中之一就是“D3DCompiler_43.dll丢失”的提示。D3DCompiler_43.dll是一个非常重要的动态链接库文件。它是由DirectX SDK提供的,用于编译和优化DirectX着色器代码的…...
python使用opencv提取视频中的每一帧、最后一帧,并存储成图片
提取视频每一帧存储图片 最近在搞视频检测问题,在用到将视频分帧保存为图片时,图片可以保存,但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行,在检查代码、检查路径等措施均无果后&…...
说说对React refs 的理解?应用场景?
先了解,是什么? React 中的 Refs提供了一种方式,允许我们访问 DOM节点或在 render方法中创建的 React元素。 本质为ReactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的do…...
Pytorch 读取t7文件
Pytorch 1.0以上可以使用: import torchfileth_path r"./path/xx.t7" data torchfile.load(th_path)print(data.shape)若data的尺寸为0,则将torch版本降为0.4.1,并使用以下函数: from torch.utils.serialization im…...
【YOLOV8预测篇】使用Ultralytics YOLO进行检测、分割、姿态估计和分类实践
目录 一 安装Ultralytics 二 使用预训练的YOLOv8n检测模型 三 使用预训练的YOLOv8n-seg分割模型 四 使用预训练的YOLOv8n-pose姿态模型 五 使用预训练的YOLOv8n-cls分类模型 <...
[Linux] MySQL数据库之索引
一、索引的相关知识 1.1 索引的简介 索引是一个排序列表,包含索引值和包含该值的数据行的物理地址(类似于 c 语言链表,通过指针指向数据记录的内存地址)。 使用索引后可以不用扫描全表来定位某行的数据,而是先通过索…...
【期末考试】计算机网络、网络及其计算 考试重点
个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ 计算机网络及其计算 期末考点 🚀数…...
力扣labuladong——一刷day79
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣785. 判断二分图二、力扣886. 可能的二分法 前言 给你一幅「图」,请你用两种颜色将图中的所有顶点着色,且使得任意一条边的两个…...
【数据结构入门精讲 | 第十篇】考研408排序算法专项练习(二)
在上文中我们进行了排序算法的判断题、选择题的专项练习,在这一篇中我们将进行排序算法中编程题的练习。 目录 编程题R7-1 字符串的冒泡排序R7-1 抢红包R7-1 PAT排名汇总R7-2 统计工龄R7-1 插入排序还是堆排序R7-2 龙龙送外卖R7-3 家谱处理 编程题 R7-1 字符串的冒…...
【ES实战】Elasticsearch6开始的CCR
【ES实战】学习使用Elasticsearch6开始的CCR 本文涉及官网文章地址 OverviewRequirements for leader indicesAutomatically following indicesGetting started with cross-cluster replicationUpgrading clusters CCR > Cross-cluster replication 文章目录 【ES实战】学…...
Deployment Pay
axure watermark...
MySQL创建member表失败
最近在做一个项目,在台式机上可以跑通,也测试了各个已完成的接口,提交到了GitHub后想着用宿舍的电脑跑一下,在测试member表相关接口时就出错了。报了SQL语法错误,但SQL语句很简单,就根据手机号查询不至于出…...
使用minio实现大文件断点续传
部署 minio 拉取镜像 docker pull minio/minio docker images新建映射目录 新建下面图片里的俩个目录 data(存放对象-实际的数据) config 存放配置开放对应端口 我使用的是腾讯服务器所以 在腾讯的安全页面开启 9000,9090 两个端口就可以了(根据大家实际…...
插入排序之C++实现
描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分,每次从未排序序列中取出一个元素,然后将它插入到已排序序列的适当位置,直到所有元素都插入完毕,即完成排序。 实现思路…...
pyecharts本地静态资源部署终极指南:告别网络依赖,实现高速可视化
pyecharts本地静态资源部署终极指南:告别网络依赖,实现高速可视化 【免费下载链接】pyecharts-assets 🗂 All assets in pyecharts 项目地址: https://gitcode.com/gh_mirrors/py/pyecharts-assets pyecharts-assets 是一个专为pyecha…...
使用 Taotoken 后 API 调用延迟与稳定性体验分享
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用 Taotoken 后 API 调用延迟与稳定性体验分享 作为一名日常需要频繁调用大模型 API 的开发者,服务的稳定性和响应速…...
深度解析网易游戏NPK文件解包:从二进制迷宫到资源提取的完整实战指南
深度解析网易游戏NPK文件解包:从二进制迷宫到资源提取的完整实战指南 【免费下载链接】unnpk 解包网易游戏NeoX引擎NPK文件,如阴阳师、魔法禁书目录。 项目地址: https://gitcode.com/gh_mirrors/un/unnpk 你是否曾经好奇网易热门游戏如《阴阳师》…...
手把手教你用YOLOv5训练VisDrone2019数据集:搞定无人机航拍小目标检测
无人机视角下的目标检测实战:YOLOv5与VisDrone2019数据集深度适配指南 无人机航拍图像的目标检测一直是计算机视觉领域的难点与热点。VisDrone2019作为当前最权威的无人机视角数据集之一,包含了丰富的场景变化和极具挑战性的小目标检测任务。本文将带您从…...
基于OpenClaw构建AI智能体:从RAG到自动化工作流的实战指南
1. 项目概述:一个开源AI应用案例的“藏宝图”最近在GitHub上闲逛,发现了一个挺有意思的仓库,叫awesome-openclaw-usecases-zh。光看名字,就能拆解出几个关键信息:“awesome”系列(意味着是精选合集…...
为Hermes Agent配置自定义Provider指向Taotoken聚合服务的操作方法
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为Hermes Agent配置自定义Provider指向Taotoken聚合服务的操作方法 Hermes Agent 是一个功能强大的AI代理框架,它支持通…...
5个电脑硬件问题,这款开源工具帮你轻松解决
5个电脑硬件问题,这款开源工具帮你轻松解决 【免费下载链接】LibreHardwareMonitor Libre Hardware Monitor is free software that can monitor the temperature sensors, fan speeds, voltages, load and clock speeds of your computer. 项目地址: https://git…...
3分钟解锁B站评论区的“读心术“:揭秘用户真实身份的完整指南
3分钟解锁B站评论区的"读心术":揭秘用户真实身份的完整指南 【免费下载链接】bilibili-comment-checker B站评论区自动标注成分,支持动态和关注识别以及手动输入 UID 识别 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-comment-c…...
C++高性能服务器框架----Servlet模块
Servlet模块HTTP Servlet包括两部分,第一部分是Servlet对象,每个Servlet对象表示一种处理HTTP消息的方法,第二部分是ServletDispatch,它包含一个请求路径到Servlet对象的映射,用于指定一个请求路径该用哪个Servlet来处…...
录音转文字在线版有哪些?这几款免费录音转文字在线工具怎么选?
很多人做录音转文字的时候默认用专业级的转录服务,其实像提词匠这样的轻量工具已经够用了。特别是如果你只是偶尔需要把会议录音、课堂笔记、视频素材转成文字,不必非要上手深度学习复杂的专业软件。下面我梳理了目前市面上主流的录音转文字在线版工具,既有微信小程序也有网页版…...
