shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。
文章目录
- 前言
- 1. 直接上代码最后在讲解
- 1.1 新增的pom依赖
- 1.2 RedisCache.java
- 1.3 RedisCacheManager.java
- 1.4 jwt的三个类
- 1.5 ShiroConfig.java新增Bean
- 2. 源码讲解。
- 2.1 shiro 缓存的代码流程。
- 2.2 缓存流程
- 2.2.1 认证和授权简述
- 2.2.2 AuthenticatingRealm.getAuthenticationInfo() 认证缓存。
- 2.2.3 AuthorizingRealm.getAuthorizationInfo() 授权缓存。
- 2.3 redis缓存的设置入口(个人认为是重点)
- 3. 问题解决
- 3.1 授权的流程是怎么做的
- 3.2 @RequiresPermissions("sys:user:list")是如何工作的
- **1. 基本原理**
- **2. 具体流程**
- **1. 用户登录阶段**
- **2. 调用 `listUsers` 方法时**
- **3. 匹配机制**
前言
-
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
-
shiro学习二:shiro的加密认证详解,加盐与不加盐两个版本。
-
shiro学习三:shiro的源码分析
-
密码专辑:对密码加盐加密,对密码进行md5加密,封装成密码工具类
-
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
-
代码所在地:https://github.com/fengfanli/springboot-shiro 记得给个星哦。
-
本文详细介绍了在Java Spring Boot项目中使用Apache Shiro进行权限管理的实现方式。通过整合Redis作为缓存管理器,实现了用户权限的缓存,提高了权限验证的效率。文章还解析了@RequiresPermissions注解的工作原理,说明了如何通过AOP机制拦截方法,并进行权限字符串的匹配校验,确保只有具备相应权限的用户能够访问受保护的方法。整体上,文章为Shiro在Spring Boot项目中的应用提供了全面的技术指导。
1. 直接上代码最后在讲解
1.1 新增的pom依赖
<!--JWT--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
1.2 RedisCache.java
package com.feng.shiro;import com.alibaba.fastjson.JSON;
import com.feng.constant.Constant;
import com.feng.jwt.JwtTokenUtil;
import com.feng.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;import java.util.*;
import java.util.concurrent.TimeUnit;/*** @ClassName: RedisCache* @Description: 缓存工具类* @createTime: 2020/2/9 20:42* @Author: 冯凡利* @UpdateUser: 冯凡利* @Version: 0.0.1*//*** 这里的 RedisUtils 不能注入** @param <K>* @param <V>*/
@Slf4j
public class RedisCache<K, V> implements Cache<K, V> {private final static String PREFIX = "shiro-cache:";private String cacheKey;private long expire = 24; // 24 小时private RedisUtil redisUtil;public RedisCache(String name, RedisUtil redisUtil) {
// this.cacheKey=PREFIX+name+":";this.cacheKey = Constant.IDENTIFY_CACHE_KEY;this.redisUtil = redisUtil;}/*** 根据 key 值 获取 权限信息** @param key jwt* @return* @throws CacheException*/@Overridepublic V get(K key) throws CacheException {log.info("Shiro 从缓存中获取数据 KEY 值[{}]", key);if (key == null) {return null;}try {String redisCacheKey = getRedisCacheKey(key);Object rawValue = redisUtil.get(redisCacheKey); // 根据key 获取 数据if (rawValue == null) {return null;}SimpleAuthorizationInfo info = JSON.parseObject(rawValue.toString(), SimpleAuthorizationInfo.class);V value = (V) info;return value;} catch (Exception e) {throw new CacheException(e);}}/*** 存值** @param key jwt* @param value* @return* @throws CacheException*/@Overridepublic V put(K key, V value) throws CacheException {log.info("put key [{}]", key);if (key == null) {log.warn("Saving a null key is meaningless, return value directly without call Redis.");return value;}try {String redisCacheKey = getRedisCacheKey(key); // cacheKey + userIdredisUtil.set(redisCacheKey, value != null ? value : null, expire, TimeUnit.HOURS);return value;} catch (Exception e) {throw new CacheException(e);}}/*** 根据 key 值 删除缓存的值** @param key* @return* @throws CacheException*/@Overridepublic V remove(K key) throws CacheException {log.info("remove key [{}]", key);if (key == null) {return null;}try {String redisCacheKey = getRedisCacheKey(key);Object rawValue = redisUtil.get(redisCacheKey);V previous = (V) rawValue;redisUtil.delete(redisCacheKey);return previous;} catch (Exception e) {throw new CacheException(e);}}/*** 清除 所有的值** @throws CacheException*/@Overridepublic void clear() throws CacheException {log.debug("clear cache");Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get keys error", e);}if (keys == null || keys.size() == 0) {return;}for (String key : keys) {redisUtil.delete(key);}}/*** 获取 redis 所存的 缓存数的大小** @return*/@Overridepublic int size() {int result = 0;try {result = redisUtil.keys(this.cacheKey + "*").size();} catch (Exception e) {log.error("get keys error", e);}return result;}/*** 获取key值** @return*/@SuppressWarnings("unchecked")@Overridepublic Set<K> keys() {Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get keys error", e);return Collections.emptySet();}if (CollectionUtils.isEmpty(keys)) {return Collections.emptySet();}Set<K> convertedKeys = new HashSet<>();for (String key : keys) {try {convertedKeys.add((K) key);} catch (Exception e) {log.error("deserialize keys error", e);}}return convertedKeys;}/*** 获取 value值** @return*/@Overridepublic Collection<V> values() {Set<String> keys = null;try {keys = redisUtil.keys(this.cacheKey + "*");} catch (Exception e) {log.error("get values error", e);return Collections.emptySet();}if (CollectionUtils.isEmpty(keys)) {return Collections.emptySet();}List<V> values = new ArrayList<V>(keys.size());for (String key : keys) {V value = null;try {value = (V) redisUtil.get(key);} catch (Exception e) {log.error("deserialize values= error", e);}if (value != null) {values.add(value);}}return Collections.unmodifiableList(values);}/*** 获取 redis 中的 缓存 key ,很重要,** @param key* @return*/private String getRedisCacheKey(K key) {if (null == key) {return null;} else {return this.cacheKey + JwtTokenUtil.getUserId(key.toString());}}
}
1.3 RedisCacheManager.java
package com.feng.shiro;import com.feng.utils.RedisUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;/*** @ClassName: RedisCacheManager* @Description: 描述* @createTime: 2020/2/9 21:14* @Author: 冯凡利* @UpdateUser: 冯凡利* @Version: 0.0.1*/
public class RedisCacheManager implements CacheManager {@Autowiredprivate RedisUtil redisUtil;@Overridepublic <K, V> Cache<K, V> getCache(String s) throws CacheException {return new RedisCache<>(s, redisUtil);}
}
1.4 jwt的三个类
我不贴了,不是核心代码,可直接看GitHub。
四个部分:
- JwtTokenUtil.java:jwt的工具类
- JwtPropertiesConfig.java:application.properties 中的jwt的属性读取类
- InitializerJwtPropertiesConfig.java:将 JwtPropertiesConfig.java进行注入。
- application.properties 中的jwt的自定义属性配置。
1.5 ShiroConfig.java新增Bean
新增了redisCacheManager bean,然后再 getShiroRealm() 函数中 ,通过 Realm(ShiroRealm) 进行设置 缓存。
/*** shiro 的 缓存管理器* 需要在 ShiroRealm bean 中进行设置* @return*/@Bean(name = "redisCacheManager")public RedisCacheManager redisCacheManager() {return new RedisCacheManager();}/*** 登录的 认证域** @param hashedCredentialsMatcher* @return*/@Bean(name = "shiroRealm")public ShiroRealm getShiroRealm(@Qualifier("shiroHashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher,@Qualifier("redisCacheManager") RedisCacheManager redisCacheManager) {ShiroRealm shiroRealm = new ShiroRealm();// 自定义 处理 token 过滤shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);// 自定义 缓存管理器shiroRealm.setCacheManager(redisCacheManager);return shiroRealm;}
shiro 新增的 shiro 缓存 代码到此结束。
其他代码没有展示,是因为 shiro 缓存的代码只有这些。
2. 源码讲解。
2.1 shiro 缓存的代码流程。
- 首先编写 RedisCache.java:实现 Cache 接口,再这里重写redis的一些操作函数
- 编写RedisCacheManager.java 类:实现 CacheManager 接口,将RedisCache.java 类加入到 缓存管理中。
- 编写ShiroConfig.java 类:将RedisCacheManager注入生Bean,然后再Realm(本案例是ShiroRealm)中设置
缓存管理
,同时也是设置资格匹配
的地方。
2.2 缓存流程
shiro的核心是再 数据域中,也就是Realm,本案例的是ShiroRealm,继承 AuthorizingRealm
授权域, 授权域 继承了 AuthenticatingRealm
认证域,认证域 又继承了 CachingRealm
缓存域。这三个类是shiro 认证、授权、缓存 三个最重要的类。
2.2.1 认证和授权简述
继承了 AuthorizingRealm
授权域,就要重写两个最重要的方法
-
认证函数 doGetAuthenticationInfo(),既然认证,重写的就是
AuthenticatingRealm
认证域 中的函数。在函数getAuthenticationInfo()
中。源码截图如下:
-
授权函数 doGetAuthorizationInfo(),既然授权,重写的就是
AuthorizingRealm
授权域 中的函数。在函数getAuthorizationInfo()
中,源码截图如下:
在 Shiro 中,认证缓存(authentication cache) 和 授权缓存(authorization cache) 是两个不同的缓存,它们用于不同的目的:
- 认证缓存 (authenticationCache):用于缓存用户的认证信息(如,用户是否存在,凭证是否正确等)。这通常是一个比较频繁更新的缓存,因为用户登录的凭证(如密码)是动态变化的,可能会变得无效。
- 授权缓存 (authorizationCache):用于缓存用户的授权信息(如,用户的角色、权限等)。授权信息通常不太会频繁变动,因此可以缓存很长时间。它一般是用于存储用户的角色和权限信息,以减少每次请求时对数据库或外部系统的查询。
2.2.2 AuthenticatingRealm.getAuthenticationInfo() 认证缓存。
该函数的第一句就是:AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
该函数就是从缓存中获取认证信息。
先说答案:shiro不推荐、默认不支持 认证从缓存中获取。
代码中可以看到 在AuthenticatingRealm类属性 this.authenticationCachingEnabled = false;
在构造函数中 默认为 false,不开启缓存。
究其原因:用于缓存用户的认证信息(如,用户是否存在,凭证是否正确等)。这通常是一个比较频繁更新的缓存,因为用户登录的凭证(如密码)是动态变化的,可能会变得无效。
所以不推荐缓存,通过debug,也可以看到。
2.2.3 AuthorizingRealm.getAuthorizationInfo() 授权缓存。
授权缓存 在shiro 中是支持的。因为 AuthorizingRealm 授权域类中的 类属性 this.authorizationCachingEnabled = true;
在构造函数中默认为true的。
getAuthorizationInfo() 函数就是从缓存中进行获取的,没有获取到在 进入 到 doGetAuthorizationInfo() 函数中进行获取、授权。
可以通过debug的方式一点点去查看。
如果没有设置redis作为缓存管理,默认可以配置缓存,但是shiro并没有帮我指定缓存,估计是怕默认指定之后怕内存使用过高,需要自行设定,接下来讲解。
2.3 redis缓存的设置入口(个人认为是重点)
前边讲的都是逻辑流程,如何从缓存中获取。我在读源码的时候我就很好奇,那设置redis作为缓存的地方在哪里呢?默认是用内存作为缓存的设置又在哪里呢?抱着这些疑问继续走。
答案:入口就在ShiroConfig中 Realm 的配置中。
ShiroRealm shiroRealm = new ShiroRealm();// 自定义 处理 token 过滤shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);// 自定义 缓存管理器shiroRealm.setCacheManager(redisCacheManager);
new ShiroRealm()时 ;会执行 AuthorizingRealm、AuthenticatingRealm的构造函数。构造函数中会设置 缓存启动情况、缓存的配置情况。
-
其中
AuthenticatingRealm
认证域的构造函数中,会默认指定SimpleCredentialsMatcher
作为 凭证匹配器 的 类,shiro还提供了HashedCredentialsMatcher
作为 凭证匹配器 类(具体有几个,看CredentialsMatcher
类的实现类有几个即可,下图所示)。后面我们自定义了 凭证匹配器 类CustomHashedCredentialsMatcher
,其中的doCredentialsMatch()
函数就是默认 认证的最核心的函数。
-
AuthorizingRealm 授权域的构造函数中,会默认指定 缓存管理(在CachingRealm 类中)、凭证匹配器(认证域类中) 的初始化,默认都是null。
-
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
:调用 认证域类 中的方法设置凭证匹配器
。 -
shiroRealm.setCacheManager(redisCacheManager);
:调用缓存域类
中的方法设置缓存管理器 以及 实现缓存的方式(第二行语句),该方法是个接口,有两个实现方法:一个是 认证域,一个是授权域。往下走。 -
这里就是 授权域 中的
afterCacheManagerSet()
函数,- 第一个函数调用父类也就是认证域上的函数,会继续调用
this.getAvailableAuthenticationCache()
。认证域前边说了,认证缓存shiro默认是关闭的,debug走一边流程就明白了;重点就是授权域了。 - 在第二个函数上,先判断是否有缓存,为null,然后再看 授权域 是否支持 授权缓存(默认支持,前边说了);然后进入懒加载缓存(下面第二张图);到获取缓存的地方,缓存管理不为null,是redis的,接着从158行,获取缓存,158行往下debug,就是上面的1.3小节的
RedisCacheManager.java
,将redis返回。
- 第一个函数调用父类也就是认证域上的函数,会继续调用
-
我又来问题了,如果没有redis,走的是哪个呢?首先我也不知道,得先把redis缓存管理给去掉。看看使用的哪个。CacheManager.java 是接口,其实现类截图如下,第一个是我实现的,其余三个都是shiro自带的(第二个应该不是,看源码)。
-
通过源码查看,shiro并没有默认使用缓存配置,仅仅是打开了缓存配置,可以供我们使用,如果想用使用缓存需要自行配置缓存,想上面配置 redis一样,将下面的第二个或者第四个配置到缓存管理中去。
到这里为止源码讲解完啦。
3. 问题解决
3.1 授权的流程是怎么做的
这个需要有背景的。
我基于这个项目来说一下
前端 LayUI+thymeleaf
后端使用springboot、shiro作为权限管理。
根据请求 /api/users,进行举例。
前端发请求后,会先认证,此处略。
然后会先从前端标签获取 权限字符串 sys:user:list,这里使用到了 thymeleaf,然后 通过 doGetAuthorizationInfo()获取所有权限字符串和角色,然后与从前端拿到的 权限字符串 sys:user:list进行对比,若包含,这通过。否则失败
3.2 @RequiresPermissions(“sys:user:list”)是如何工作的
1. 基本原理
@RequiresPermissions("sys:user:list")
是 Apache Shiro 提供的权限控制注解,用于声明访问某个方法或类需要的特定权限。
它的工作机制如下:
-
拦截注解:
- Shiro 提供了
AuthorizationAttributeSourceAdvisor
和 AOP(切面编程)机制来拦截被注解标记的类或方法。 - 当调用被标记的方法时,Shiro 拦截并执行权限校验。
- Shiro 提供了
-
获取当前用户的权限:
- Shiro 从当前登录用户的
Subject
(主体)中获取用户的权限信息(通常是一组权限字符串)。
- Shiro 从当前登录用户的
-
权限字符串匹配:
- Shiro 将注解中的权限字符串(如
"sys:user:list"
)与当前用户的权限列表进行比对。 - 如果用户的权限列表中包含注解指定的权限字符串,则校验通过;否则抛出
AuthorizationException
,拒绝访问。
- Shiro 将注解中的权限字符串(如
2. 具体流程
假设你有以下代码:
@RequiresPermissions("sys:user:list")
public void listUsers() {// 获取用户列表逻辑
}
1. 用户登录阶段
- 用户通过登录接口认证成功后,Shiro 会根据用户身份(如用户名)从数据库或缓存中加载用户的所有权限,并存储在
Subject
中。 - 示例权限列表:
["sys:user:list", "sys:user:edit", "sys:user:delete"]
2. 调用 listUsers
方法时
- Shiro 拦截
@RequiresPermissions
注解,通过当前用户的Subject
获取权限列表。 - 调用
Subject.isPermitted("sys:user:list")
方法,具体逻辑如下:- Shiro 将注解中的权限字符串
"sys:user:list"
传递给AuthorizingRealm
的doGetAuthorizationInfo
方法。 doGetAuthorizationInfo
返回用户的权限列表。- Shiro 比较
"sys:user:list"
是否存在于用户的权限列表中:- 存在:校验通过,继续执行方法。
- 不存在:抛出异常
AuthorizationException
。
- Shiro 将注解中的权限字符串
3. 匹配机制
- 权限字符串可以使用通配符(Wildcard)进行匹配:
- 完全匹配:
"sys:user:list"
对比"sys:user:list"
,通过。 - 通配符匹配:
"sys:user:*"
对比"sys:user:list"
,通过。 - 多级通配符:
"sys:*:*"
对比"sys:user:list"
,通过。
- 完全匹配:
相关文章:
shiro学习五:使用springboot整合shiro。在前面学习四的基础上,增加shiro的缓存机制,源码讲解:认证缓存、授权缓存。
文章目录 前言1. 直接上代码最后在讲解1.1 新增的pom依赖1.2 RedisCache.java1.3 RedisCacheManager.java1.4 jwt的三个类1.5 ShiroConfig.java新增Bean 2. 源码讲解。2.1 shiro 缓存的代码流程。2.2 缓存流程2.2.1 认证和授权简述2.2.2 AuthenticatingRealm.getAuthentication…...
Python案例--养兔子
兔子繁殖问题是一个经典的数学问题,最早由意大利数学家斐波那契在13世纪提出。这个问题不仅在数学领域具有重要意义,还广泛应用于计算机科学、生物学和经济学等领域。本文将通过一个具体的Python程序,深入探讨兔子繁殖问题的建模和实现&#…...
【搜索回溯算法】:BFS的魔力--如何使用广度优先搜索找到最短路径
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索(BFS)解决最短路…...
JavaSE第十一天——集合框架Collection
一、List接口 List接口是一个有序的集合,允许元素有重复,它继承了Collection接口,提供了许多额外的功能,比如基于索引的插入、删除和访问元素等。 常见的List接口的实现类有ArrayList、LinkedList和Vector。 List接口的实现类 …...
Three城市引擎地图插件Geo-3d
一、简介 基于Three开发,为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系,支持坐标转换,支持影像、地形、geojson建筑、道路,植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…...
深度学习|表示学习|卷积神经网络|详细推导每一层的维度变化|14
如是我闻: 一个经典的卷积神经网络(CNN)架构,呈现的是输入图像通过多个卷积层、池化层以及全连接层,最终输出分类结果的过程。整个过程的核心是理解输入特征图的尺寸如何在每一层发生变化,我们可以通过卷积…...
多级缓存(亿级并发解决方案)
多级缓存(亿级流量(并发)的缓存方案) 传统缓存的问题 传统缓存是请求到达tomcat后,先查询redis,如果未命中则查询数据库,问题如下: (1)请求要经过tomcat处…...
BOM对象location与数组操作结合——查询串提取案例
BOM对象location与数组操作结合——查询串提取案例 前置知识 1. Location 对象 Location 对象是 JavaScript 提供的内置对象之一,它表示当前窗口或框架的 URL,并允许你通过它操作或获取 URL 的信息。可以通过 window.location 访问。 主要属性&#…...
读书笔记--分布式服务架构对比及优势
本篇是在上一篇的基础上,主要对共享服务平台建设所依赖的分布式服务架构进行学习,主要记录和思考如下,供大家学习参考。随着企业各业务数字化转型工作的推进,之前在传统的单一系统(或单体应用)模式中&#…...
GOGOGO 枚举
含义:一种类似于类的一种结构 作用:是Java提供的一个数据类型,可以设置值是固定的 【当某一个数据类型受自身限制的时候,使用枚举】 语法格式: public enum 枚举名{…… }有哪些成员? A、对象 public …...
【Linux】Linux基础开发工具
1 Linux 软件包管理器 yum 1.1软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在一个服务器上,通过包管理器可以很方便的…...
嵌入式C语言:结构体的多态性之结构体中的void*万能指针
目录 一、void*指针在结构体中的应用 二、实现方式 2.1. 定义通用结构体 2.2. 定义具体结构体 2.3. 初始化和使用 三、应用场景 3.1. 内存管理函数 3.2. 泛型数据结构(链表) 3.3. 回调函数和函数指针 3.4. 跨语言调用或API接口(模拟…...
重构进行时:一秒告别 !=null 判空
重构进行时:一秒告别 !null 判空 空指针异常(NullPointerException)是Java开发中常见的错误之一。 许多开发者在遇到空指针问题时,往往会习惯性地使用! null来进行判断。 然而,当代码中频繁出现这种判断时ÿ…...
React 中hooks之useSyncExternalStore使用总结
1. 基本概念 useSyncExternalStore 是 React 18 引入的一个 Hook,用于订阅外部数据源,确保在并发渲染下数据的一致性。它主要用于: 订阅浏览器 API(如 window.width)订阅第三方状态管理库订阅任何外部数据源 1.1 基…...
Semaphore 与 线程池 Executor 有什么区别?
前言:笔者在看Semaphone时 突然脑子宕机,啥啥分不清 Semaphore 和 Executor 的作用,故再次记录。 一、什么是Semaphore? Semaphore 是 Java 并发编程(JUC)中一个重要的同步工具,它的作用是 控…...
Rust:高性能与安全并行的编程语言
引言 在现代编程世界里,开发者面临的最大挑战之一就是如何平衡性能与安全性。在许多情况下,C/C这样的系统级编程语言虽然性能强大,但其内存管理的复杂性导致了各种安全漏洞。为了解决这些问题,Rust 作为一种新的系统级编程语言进入…...
论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(六)(完结)
Understanding Diffusion Models: A Unified Perspective(六)(完结) 文章概括指导(Guidance)分类器指导无分类器引导(Classifier-Free Guidance) 总结 文章概括 引用: …...
Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图: 为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视…...
电脑怎么格式化?格式化详细步骤
格式化是我们在日常使用电脑时可能会用到的一种操作,无论是清理磁盘空间、安装新系统,还是解决磁盘读写错误,都可能需要格式化。不过,对于一些不熟悉电脑操作的用户来说,格式化听起来可能有些复杂。其实,只…...
TikTok广告投放优化策略:提升ROI的核心技巧
在短许多品牌和商家纷纷投入广告营销,争夺这片潜力巨大的市场。然而,在激烈的竞争环境中,如何精准有效地投放广告,优化广告效果,实现更高的投资回报率(ROI)成为了广告主关注的核心。 一. 精准受…...
视觉语言模型 (VLMs):跨模态智能的探索
文章目录 一. VLMs 的重要性与挑战:连接视觉与语言的桥梁 🌉二. VLMs 的核心训练范式:四种主流策略 🗺️1. 对比训练 (Contrastive Training):拉近正例,推远负例 ⚖️2. 掩码方法 (Masking):重构…...
第05章 08 绘制脑部体绘制图的阈值等值面
绘制脑部体绘制图的阈值等值面,例如肌肉和头骨骼,需要对医学图像数据进行阈值处理,并使用体绘制技术来可视化这些结构。以下是一个基于VTK/C的示例代码,展示如何读取DICOM图像数据,应用阈值过滤器来提取特定组织&#…...
类和对象(4)——多态:方法重写与动态绑定、向上转型和向下转型、多态的实现条件
目录 1. 向上转型和向下转型 1.1 向上转型 1.2 向下转型 1.3 instanceof关键字 2. 重写(overidde) 2.1 方法重写的规则 2.1.1 基础规则 2.1.2 深层规则 2.2 三种不能重写的方法 final修饰 private修饰 static修饰 3. 动态绑定 3.1 动态绑…...
动态规划<九>两个数组的dp
目录 引例 LeetCode经典OJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 引例 OJ传送门LeetCode<1143>最长公共子序列 画图分析: 使用动态规划解决 1.状态表示 ------经验题目要求 经验为选取第一个字符串的[0,i]区间以及第二个字…...
Go:基于Go实现一个压测工具
文章目录 写在前面整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理 客户端模块Http客户端处理Grpc客户端处理Websocket客户端处理 连接处理模块GrpcHttp 统计数据模块统计原理实现过程 写在前面 本篇主要是基于Go来实现一个压测的工具,关于压测的内…...
2025年数学建模美赛 A题分析(2)楼梯使用频率数学模型
2025年数学建模美赛 A题分析(1)Testing Time: The Constant Wear On Stairs 2025年数学建模美赛 A题分析(2)楼梯磨损分析模型 2025年数学建模美赛 A题分析(3)楼梯使用方向偏好模型 2025年数学建模美赛 A题分…...
在Ubuntu上用Llama Factory命令行微调Qwen2.5的简单过程
半年多之前写过一个教程:在Windows上用Llama Factory微调Llama 3的基本操作_llama-factory windows-CSDN博客 如果用命令行做的话,前面的步骤可以参考上面这个博客。安装好环境后, 用自我认知数据集微调Lora模块:data/identity.j…...
虹科分享 | 汽车NVH小课堂之听音辨故障
随着车主开始关注汽车抖动异响问题,如何根据故障现象快速诊断异响来源,成了汽修人的必修课。 一个比较常用的方法就是靠“听”——“听音辨故障”。那今天,虹科Pico也整理了几个不同类型的异响声音,一起来听听看你能答对几个吧 汽…...
RoboVLM——通用机器人策略的VLA设计哲学:如何选择骨干网络、如何构建VLA架构、何时添加跨本体数据
前言 本博客内解读不少VLA模型了,包括π0等,且如此文的开头所说 前两天又重点看了下openvla,和cogact,发现 目前cogACT把openvla的动作预测换成了dit,在模型架构层面上,逼近了π0那为了进一步逼近&#…...
【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念,今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码…...
0.91英寸OLED显示屏一种具有小尺寸、高分辨率、低功耗特性的显示器件
0.91英寸OLED显示屏是一种具有小尺寸、高分辨率、低功耗特性的显示器件。以下是对0.91英寸OLED显示屏的详细介绍: 一、基本参数 尺寸:0.91英寸分辨率:通常为128x32像素,意味着显示屏上有128列和32行的像素点,总共409…...
【insert函数】
在 C 中,std::string::insert 是一个功能强大的成员函数,用于在字符串的指定位置插入内容。它有多个重载版本,支持插入 字符、字符串、子字符串 等。以下是 insert 所有相关函数的详细介绍: 1. 插入字符串 函数签名: …...
Python 如何进行文本匹配:difflib| python 小知识
Python 如何进行文本匹配:difflib| python 小知识 difflib是Python标准库中的一个工具,用于比较和处理文本差异。它提供了一组用于比较和处理文本差异的功能,可以用于比较字符串、文件等。本文将详细介绍difflib模块的用法和实现细节&#x…...
MySQL误删数据怎么办?
文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时,误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障,还是不小心执行了删除命…...
可以称之为“yyds”的物联网开源框架有哪几个?
有了物联网的发展,我们的生活似乎也变得更加“鲜活”、有趣、便捷,包具有科技感的。在物联网(IoT)领域中,也有许多优秀的开源框架支持设备连接、数据处理、云服务等,成为被用户们广泛认可的存在。以下给大家…...
为AI聊天工具添加一个知识系统 之74 详细设计之15 正则表达式 之2
本文要点 要点 本项目(为AI聊天工具添加一个知识系统)中的正则表达式。 正则表达式的三“比”。正则表达式被 一、排比为三种符号(元符号-圈号,特殊符号-引号,普通符号-括号) 引号<<a线性回归bo…...
Java 注解与元数据
Java学习资料 Java学习资料 Java学习资料 一、引言 在 Java 编程中,注解(Annotation)和元数据(Metadata)是两个重要的概念。注解为程序提供了一种在代码中嵌入额外信息的方式,这些额外信息就是元数据。元…...
【橘子Kibana】Kibana的分析能力Analytics简易分析
一、kibana是啥,能干嘛 我们经常会用es来实现一些关于检索,关于分析的业务。但是es本身并没有UI,我们只能通过调用api来完成一些能力。而kibana就是他的一个外置UI,你完全可以这么理解。 当我们进入kibana的主页的时候你可以看到这样的布局。…...
度小满前端面试题及参考答案
<form>标签使用过哪些 tag? <form>标签中常使用的标签有很多。 <input>:这是最常用的标签之一,用于创建各种类型的输入字段,如文本框、密码框、单选按钮、复选框、文件上传框等。通过设置type属性来指定输入类型,例如type="text"创建文本输入…...
Padas进行MongoDB数据库CRUD
在数据处理的领域,MongoDB作为一款NoSQL数据库,以其灵活的文档存储结构和高扩展性广泛应用于大规模数据处理场景。Pandas作为Python的核心数据处理库,能够高效处理结构化数据。在MongoDB中,数据以JSON格式存储,这与Pandas的DataFrame结构可以很方便地互相转换。通过这篇教…...
LQ1052 Fibonacci斐波那契数列
题目描述 Fibonacci斐波那契数列也称为兔子数列,它的递推公式为:FnFn-1Fn-2,其中F1F21。 当n比较大时,Fn也非常大,现在小蓝想知道,Fn除以10007的余数是多少,请你编程告诉她。 输入 输入包含一…...
华硕笔记本装win10哪个版本好用分析_华硕笔记本装win10专业版图文教程
华硕笔记本装win10哪个版本好用?华硕笔记本还是建议安装win10专业版。Win分为多个版本,其中家庭版(Home)和专业版(Pro)是用户选择最多的两个版本。win10专业版在功能以及安全性方面有着明显的优势ÿ…...
编译器gcc/g++ --【Linux基础开发工具】
文章目录 一、背景知识二、gcc编译选项1、预处理(进行宏替换)2、编译(生成汇编)3、汇编(生成机器可识别代码)4、链接(生成可执行文件或库文件) 三、动态链接和静态链接四、静态库和动态库1、动静态库2、编译…...
八股文 (一)
文章目录 项目地址一、前端1.1 大文件上传,预览1.2 首页性能优化1.2 流量染色,灰度发布1.3 Websock心跳机制,大数据实时数据优化1.4 Gpu 加速 fps优化1.5 echarts包大小优化和组件封装1.6 前端监控系统1.7 超大虚拟列表卡顿1. 实现2. 相关问题(1) 什么是虚拟化列表,为什么要…...
c语言无符号的变量不能和有符号的直接比较,或者使用移项解决符号问题
使用移项解决问题,简单来说就是无符号运行不要有减号,使用移项后的加号代替 if(uEventDirLimitSize > uEventAndNormalDirSize) {if((uEventDirLimitSize - uEventAndNormalDirSize) > pStConfig->stParam.stUserParam.uEventRemain){return O…...
安卓日常问题杂谈(一)
背景 关于安卓开发中,有很多奇奇怪怪的问题,有时候这个控件闪一下,有时候这个页面移动一下,这些对于快速开发中,去查询,都是很耗费时间的,因此,本系列文章,旨在记录安卓…...
电力晶体管(GTR)全控性器件
电力晶体管(Giant Transistor,GTR)是一种全控性器件,以下是关于它的详细介绍:(模电普通晶体管三极管进行对比学习) 基本概念 GTR是一种耐高电压、大电流的双极结型晶体管(BJT&am…...
【美】Day 1 CPT申请步骤及信息获取指南(Day1 CPT)
参考文章:【美】境外申请Day 1 CPT完整流程(境外Day 1 CPT) 文章目录 **一、申请前准备:了解Day 1 CPT基本要求****二、选择Day 1 CPT学校****1. 搜索学校****2. 筛选标准** **三、申请流程****1. 提交学校申请****2. 转SEVIS记录…...
梯度下降优化算法-动量法
1. 动量法的数学原理 1.1 标准梯度下降回顾 在标准梯度下降中,参数的更新公式为: θ t 1 θ t − η ⋅ ∇ θ J ( θ t ) \theta_{t1} \theta_t - \eta \cdot \nabla_\theta J(\theta_t) θt1θt−η⋅∇θJ(θt) 其中: θ t …...
游戏引擎介绍:Game Engine
简介 定义:软件框架,一系列为开发游戏的工具的集合 可协作创意生产工具,复杂性艺术,注重realtime实时 目的 为艺术家,设计师,程序员设计工具链 游戏引擎开发参考书 推荐:Game Engine Archite…...