当前位置: 首页 > news >正文

用户上下文打通+本地缓存Guava

文章目录

  • 🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)
    • 🌟 亮点功能
    • 📦 spring cloud模块概览
      • 常用工具
    • 🔗 更多信息
    • 1.设计
        • 1.链路流程
        • 2.详细设计
    • 2.网关过滤器获取唯一标识放到Header
        • 1.LoginFilter.java
        • 2.SubjectCategoryController.java 测试
        • 3.进行测试
          • 1.用户登录
          • 2.携带token访问后端接口
    • 3.基于ThreadLocal实现上下文传递
        • 1.目录结构
        • 2.ThreadLocal的工具类 LoginContextHolder.java
        • 3.拦截器将网关传递的信息放到 LoginInterceptor.java
        • 4.配置类将自定义拦截器注册 GlobalConfig.java
        • 5.LoginUtil.java
        • 6.测试 SubjectCategoryController.java
    • 4.OpenFeign的使用
        • 1.目录结构
          • 1.sun-club-auth
        • 2.sun-club-auth-api引入依赖
        • 3.UserFeignService.java 将controller层的接口暴露
        • 4.AuthUserDTO.java 从controller剪切到api层
        • 5.Result.java和ResultCodeEnum.java从common包中剪切过来
        • 6.sun-club-auth-application-controller 引入api层的依赖,DTO和Result都使用api层的
        • 7.sun-club-subject微服务调用sun-club-auth微服务
          • 1.sun-club-infra引入sun-club-auth微服务的api包
          • 2.在sun-club-infra模块的UserRpc.java进行rpc调用
          • 3.创建接受信息的entity,UserInfo.java
          • 4.在启动类加注解,启动Feign
          • 5.测试TestFeignController.java
          • 6.测试
    • 5.OpenFeign拦截器打通微服务上下文(由调用方来写)
        • 1.FeignRequestInterceptor.java 将唯一标识放到Header中传递到其他微服务
        • 2.FeignConfiguration.java 将拦截器注入容器
        • 3.在被调用方使用过滤器将用户唯一标识存到ThreadLocal(跟前面写过的一样)
          • 1.目录结构
          • 2.ThreadLocal的工具类LoginContextHolder.java
          • 3.自定义拦截器获取Header中的唯一标识,并放到ThreadLocal中 LoginInterceptor.java
          • 4.将拦截器注入容器 GlobalConfig.java
          • 5.UserController.java 测试获取调用方传来的唯一标识
        • 4.用户上下文整体流程测试
          • 1.网关过滤器将唯一标识从redis中取出,放到Header,传到微服务模块
          • 2.Feign的调用微服务的登录拦截器,将唯一标识放到ThreadLocal中
          • 3.在调用Feign之前,进入Feign拦截器,将唯一标识放到Header中,传递给被调用方
          • 4.被调用方的登录拦截器,读取Header中的唯一标识,放到ThreadLocal中
          • 5.被调用方从ThreadLocal中可以获取到唯一标识
    • 6.本地缓存guava使用
        • 1.SubjectCategoryDomainServiceImpl.java
        • 2.测试
        • 3.本地缓存工具类
          • 1.ListLocalCacheUtil.java
          • 2.使用方式
            • 1.依赖注入
            • 2.使用
            • 3.测试

🌞 Sun Frame:SpringBoot 的轻量级开发框架(个人开源项目推荐)

Sun Frame Banner

轻松高效的现代化开发体验

Sun Frame 是我个人开源的一款基于 SpringBoot 的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。

我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟
您的支持是我们持续改进的动力。

🌟 亮点功能

  • 组件化开发:灵活选择,简化流程。
  • 高性能:通过异步日志和 Redis 缓存提升性能。
  • 易扩展:支持多种数据库和消息队列。

📦 spring cloud模块概览

  • Nacos 服务:高效的服务注册与发现。
  • Feign 远程调用:简化服务间通信。
  • 强大网关:路由与限流。

常用工具

  • 日志管理:异步处理与链路追踪。
  • Redis 集成:支持分布式锁与缓存。
  • Swagger 文档:便捷的 API 入口。
  • 测试支持:SpringBoot-Test 集成。
  • EasyCode:自定义EasyCode模板引擎,一键生成CRUD。

🔗 更多信息

  • 开源地址:Gitee Sun Frame
  • 详细文档:语雀文档
    在这里插入图片描述

1.设计

1.链路流程

img

2.详细设计

img

2.网关过滤器获取唯一标识放到Header

1.LoginFilter.java
package com.sunxiansheng.club.gateway.filter;import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;/*** Description: 网关过滤器,在用户登录之后再次请求网关就会携带token,可以通过网关过滤器通过redis获取用户的唯一标识然后放到Header中传递到后端* 过滤器的优先级是比配置文件中配置的路由要低* @Author sun* @Create 2024/6/15 15:28* @Version 1.0*/
@Component
@Slf4j
public class LoginFilter implements GlobalFilter {@Override@SneakyThrowspublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取请求ServerHttpRequest request = exchange.getRequest();// 获取一个很全的东西ServerHttpRequest.Builder mutate = request.mutate();// 获取urlString url = request.getURI().getPath();log.info("网关过滤器:用户请求的url:" + url);// 如果发现是用户登录的请求,直接放行,注意先走的网关配置,所以前缀会被删除if (url.equals("/user/doLogin")) {return chain.filter(exchange);}// 通过sa-token框架获取用户的tokenInfo,可以通过这个对象来获取用户信息SaTokenInfo tokenInfo = StpUtil.getTokenInfo();// 根据token获取用户的唯一标识String loginId = (String) tokenInfo.getLoginId();if (StringUtils.isBlank(loginId)) {log.info("网关过滤器获取用户唯一标识失败!");}// 如果到这了,就说明已经获取到了用户的唯一标识,将其放到Header中mutate.header("loginId", loginId);// 将携带了用户唯一标识的request发送到其他微服务模块return chain.filter(exchange.mutate().request(mutate.build()).build());}
}
2.SubjectCategoryController.java 测试

image-20240615155811919

3.进行测试
1.用户登录
2.携带token访问后端接口

image-20240615161700008

3.基于ThreadLocal实现上下文传递

1.目录结构

image-20240615172226319

2.ThreadLocal的工具类 LoginContextHolder.java
package com.sunxiansheng.subject.application.context;import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 上下文对象(ThreadLocal)* @Author sun* @Create 2024/6/15 16:27* @Version 1.0*/
public class LoginContextHolder {// 这个ThreadLocal持有一个Mapprivate static final InheritableThreadLocal<Map<String, Object>> THREAD_LOCAL= new InheritableThreadLocal<>();/*** 为ThreadLocal持有的Map设值* @param key* @param val*/public static void set(String key, Object val) {Map<String, Object> map = getThreadLocalMap();map.put(key, val);}/*** 从ThreadLocal持有的Map取值* @param key* @return*/public static Object get(String key) {Map<String, Object> map = THREAD_LOCAL.get();return map.get(key);}/*** 清除ThreadLocal*/public static void remove() {THREAD_LOCAL.remove();}/*** 初始化一个ThreadLocal持有的Map,要保证这个Map是单例的* @return*/public static Map<String, Object> getThreadLocalMap() {// 获取到ThreadLocal的MapMap<String, Object> map = THREAD_LOCAL.get();// 如果是空的再创建一个Map,然后放进去if (Objects.isNull(map)) {map = new ConcurrentHashMap<>();THREAD_LOCAL.set(map);}// 放到ThreadLocal中return map;}// 以下为获取用户信息的方法public static String getLoginId() {return (String) getThreadLocalMap().get("loginId");}}
3.拦截器将网关传递的信息放到 LoginInterceptor.java
package com.sunxiansheng.subject.application.interceptor;import com.sunxiansheng.subject.application.context.LoginContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Description: 处理用户上下文的拦截器* @Author sun* @Create 2024/6/15 16:20* @Version 1.0*/
public class LoginInterceptor implements HandlerInterceptor {/*** 当请求到这里了,就说明,网关已经将用户的loginId放到了Header里了* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String loginId = request.getHeader("loginId");// 将loginId放到ThreadLocal里面LoginContextHolder.set("loginId", loginId);return true;}/*** 在操作结束后清除ThreadLocal,因为如果线程复用并且没清除,这个ThreadLocal还会存在,造成数据污染* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginContextHolder.remove();}
}
4.配置类将自定义拦截器注册 GlobalConfig.java

image-20240615172339191

5.LoginUtil.java
package com.sunxiansheng.subject.application.util;import com.sunxiansheng.subject.application.context.LoginContextHolder;/*** Description: 用户登录的util* @Author sun* @Create 2024/6/15 17:12* @Version 1.0*/
public class LoginUtil {/*获取loginId*/public static String getLoginId() {return LoginContextHolder.getLoginId();}
}
6.测试 SubjectCategoryController.java

image-20240615172455473

image-20240615172514330

4.OpenFeign的使用

1.目录结构
1.sun-club-auth

image-20240616134039744

2.sun-club-auth-api引入依赖
<dependencies><!-- OpenFeign依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>3.0.7</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId><version>3.0.6</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency>
</dependencies>
3.UserFeignService.java 将controller层的接口暴露
package com.sunxiansheng.auth.api;import com.sunxiansheng.auth.entity.AuthUserDTO;
import com.sunxiansheng.auth.entity.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;/*** Description: 用户服务feign* @Author sun* @Create 2024/6/16 13:20* @Version 1.0*/
@FeignClient("sub-club-auth") // 服务名
public interface UserFeignService {@RequestMapping("/user/getUserInfo")public Result<AuthUserDTO> getUserInfo(@RequestBody AuthUserDTO authUserDTO);
}
4.AuthUserDTO.java 从controller剪切到api层
5.Result.java和ResultCodeEnum.java从common包中剪切过来
6.sun-club-auth-application-controller 引入api层的依赖,DTO和Result都使用api层的
<!-- 引入api层 -->
<dependency><groupId>com.sun.club</groupId><artifactId>sun-club-auth-api</artifactId><version>1.0-SNAPSHOT</version><scope>compile</scope>
</dependency>
7.sun-club-subject微服务调用sun-club-auth微服务
1.sun-club-infra引入sun-club-auth微服务的api包
<!-- 引入鉴权模块的api层,用于微服务间的调用 -->
<dependency><groupId>com.sun.club</groupId><artifactId>sun-club-auth-api</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
2.在sun-club-infra模块的UserRpc.java进行rpc调用
package com.sunxiansheng.subject.infra.rpc;import com.sunxiansheng.auth.api.UserFeignService;
import com.sunxiansheng.auth.entity.AuthUserDTO;
import com.sunxiansheng.auth.entity.Result;
import com.sunxiansheng.subject.infra.eneity.UserInfo;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** Description:* @Author sun* @Create 2024/6/16 13:45* @Version 1.0*/
@Component
public class UserRpc {// 直接注入FeignService@Resourceprivate UserFeignService userFeignService;/*** 根据用户名来获取用户信息* @param userName* @return*/public UserInfo getUserInfo(String userName) {AuthUserDTO authUserDTO = new AuthUserDTO();authUserDTO.setUserName(userName);// 向调用方法一样远程调用auth微服务的接口并得到结果Result<AuthUserDTO> result = userFeignService.getUserInfo(authUserDTO);// 创建一个自己的eneity用于存放dataUserInfo userInfo = new UserInfo();// 没有成功响应,直接返回一个空的对象if (!result.getSuccess()) {return userInfo;}// 成功获取信息,将获取到的信息放到自己定义的Entity中// 获取到dataAuthUserDTO data = result.getData();// 将data中的数据封装到自己定义的entity中userInfo.setUserName(data.getUserName());userInfo.setNickName(data.getNickName());return userInfo;}
}
3.创建接受信息的entity,UserInfo.java
package com.sunxiansheng.subject.infra.eneity;import lombok.Data;/*** Description:* @Author sun* @Create 2024/6/16 13:47* @Version 1.0*/
@Data
public class UserInfo {private String userName;private String nickName;
}
4.在启动类加注解,启动Feign

image-20240616153405974

5.测试TestFeignController.java
package com.sunxiansheng.subject.application.controller;import com.sunxiansheng.subject.infra.eneity.UserInfo;
import com.sunxiansheng.subject.infra.rpc.UserRpc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Description: 题目分类控制器* @Author sun* @Create 2024/5/24 9:33* @Version 1.0*/
@RestController
@RequestMapping("/subject/category")
@Slf4j
public class TestFeignController {@Resourceprivate UserRpc userRpc;@RequestMapping("/testFeign")public void testFeign() {UserInfo userInfo = userRpc.getUserInfo("鸡翅");log.info("testFeign:" + userInfo);}
}
6.测试

image-20240616145116795

image-20240616145124958

5.OpenFeign拦截器打通微服务上下文(由调用方来写)

1.FeignRequestInterceptor.java 将唯一标识放到Header中传递到其他微服务
package com.sunxiansheng.subject.application.interceptor;import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Objects;/*** Description: feign的拦截器,当使用feign调用另一个微服务时需要将用户的唯一标识放到Header中* @Author sun* @Create 2024/6/16 15:01* @Version 1.0*/
@Component
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {// 首先,从网关过来的请求,在Header中一定存着用户的唯一标识,从请求中获取ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();if (Objects.nonNull(request)) {// 从请求获取唯一标识String loginId = request.getHeader("loginId");if (StringUtils.isNotBlank(loginId)) {// 如果获得的标识不为空,就将其放到header中requestTemplate.header("loginId", loginId);}}}
}
2.FeignConfiguration.java 将拦截器注入容器
package com.sunxiansheng.subject.application.config;import com.sunxiansheng.subject.application.interceptor.FeignRequestInterceptor;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;/*** Description:* @Author sun* @Create 2024/6/16 15:12* @Version 1.0*/
@Configuration
public class FeignConfiguration extends WebMvcConfigurationSupport {// 将Feign的拦截器注入进去@Beanpublic RequestInterceptor requestInterceptor() {return new FeignRequestInterceptor();}
}
3.在被调用方使用过滤器将用户唯一标识存到ThreadLocal(跟前面写过的一样)
1.目录结构

image-20240616153838689

2.ThreadLocal的工具类LoginContextHolder.java
package com.sunxiansheng.auth.application.context;import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 上下文对象(ThreadLocal)* @Author sun* @Create 2024/6/15 16:27* @Version 1.0*/
public class LoginContextHolder {// 这个ThreadLocal持有一个Mapprivate static final InheritableThreadLocal<Map<String, Object>> THREAD_LOCAL= new InheritableThreadLocal<>();/*** 为ThreadLocal持有的Map设值* @param key* @param val*/public static void set(String key, Object val) {Map<String, Object> map = getThreadLocalMap();map.put(key, val);}/*** 从ThreadLocal持有的Map取值* @param key* @return*/public static Object get(String key) {Map<String, Object> map = THREAD_LOCAL.get();return map.get(key);}/*** 清除ThreadLocal*/public static void remove() {THREAD_LOCAL.remove();}/*** 初始化一个ThreadLocal持有的Map,要保证这个Map是单例的* @return*/public static Map<String, Object> getThreadLocalMap() {// 获取到ThreadLocal的MapMap<String, Object> map = THREAD_LOCAL.get();// 如果是空的再创建一个Map,然后放进去if (Objects.isNull(map)) {map = new ConcurrentHashMap<>();THREAD_LOCAL.set(map);}// 放到ThreadLocal中return map;}// 以下为获取用户信息的方法public static String getLoginId() {return (String) getThreadLocalMap().get("loginId");}}
3.自定义拦截器获取Header中的唯一标识,并放到ThreadLocal中 LoginInterceptor.java
package com.sunxiansheng.auth.application.interceptor;import com.sunxiansheng.auth.application.context.LoginContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Description: 处理用户上下文的拦截器* @Author sun* @Create 2024/6/15 16:20* @Version 1.0*/
public class LoginInterceptor implements HandlerInterceptor {/*** 当请求到这里了,就说明,网关已经将用户的loginId放到了Header里了* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String loginId = request.getHeader("loginId");// 将loginId放到ThreadLocal里面LoginContextHolder.set("loginId", loginId);return true;}/*** 在操作结束后清除ThreadLocal,因为如果线程复用并且没清除,这个ThreadLocal还会存在,造成数据污染* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginContextHolder.remove();}
}
4.将拦截器注入容器 GlobalConfig.java

image-20240616154236431

5.UserController.java 测试获取调用方传来的唯一标识

image-20240616154256468

4.用户上下文整体流程测试
1.网关过滤器将唯一标识从redis中取出,放到Header,传到微服务模块

image-20240616154819177

2.Feign的调用微服务的登录拦截器,将唯一标识放到ThreadLocal中

image-20240616160615477

3.在调用Feign之前,进入Feign拦截器,将唯一标识放到Header中,传递给被调用方

image-20240616160721862

4.被调用方的登录拦截器,读取Header中的唯一标识,放到ThreadLocal中

image-20240616160801869

5.被调用方从ThreadLocal中可以获取到唯一标识

image-20240616160911141

6.本地缓存guava使用

1.SubjectCategoryDomainServiceImpl.java
/*** 注意:这里使用本地缓存的原因是分类和标签的改动不大* @param subjectCategoryBO* @return*/
@Override
public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {// 构建一个本地缓存的keyString cacheKey = "categoryAndLabel." + subjectCategoryBO.getId();// 根据缓存key来获取id对应的分类列表String content = localCache.getIfPresent(cacheKey);// 如果缓存中没有,则从数据库中查List<SubjectCategoryBO> subjectCategoryBOS = new LinkedList<>();if (StringUtils.isBlank(content)) {subjectCategoryBOS = getSubjectCategoryBOS(subjectCategoryBO.getId());// 将其放到本地缓存中localCache.put(cacheKey, JSON.toJSONString(subjectCategoryBOS));} else {// 如果缓存中有,则将其序列化然后返回subjectCategoryBOS = JSON.parseArray(content, SubjectCategoryBO.class);}return subjectCategoryBOS;
}
2.测试

image-20240616171528673

image-20240616171541514

3.本地缓存工具类
1.ListLocalCacheUtil.java
package com.sunxiansheng.subject.domain.util;import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;/*** Description: List本地缓存工具类(guava)* @Author sun* @Create 2024/6/16 17:20* @Version 1.0*/
@Component
public class ListLocalCacheUtil<V> {// 使用谷歌提供的本地缓存,value的类型是String,存什么数据直接序列化即可private Cache<String, String> localCache =CacheBuilder.newBuilder().maximumSize(5000).expireAfterWrite(10, TimeUnit.SECONDS).build();/*** 根据key来获取本地缓存中的value,如果有就直接获取,如果没有,就从db中查询* @param cacheKey 构建的本地缓存的key* @param clazz 要返回的类型* @param supplier 函数式接口,没有参数,返回值为DB查询结果* @return*/public List<V> getResult(String cacheKey, Class<V> clazz, Supplier<List<V>> supplier) {// 要返回的listList<V> resultList = new LinkedList<>();// 根据key,获取本地缓存的内容String content = localCache.getIfPresent(cacheKey);if (StringUtils.isNotBlank(content)) {// 如果缓存中有,就将其序列化resultList = JSON.parseArray(content, clazz);} else {// 缓存中没有,List<V>作为返回值,交给数据库中查,具体逻辑交给函数式接口resultList = supplier.get();if (!CollectionUtils.isEmpty(resultList)) {// 如果从数据库查出来的不是空,就序列化,然后放到缓存中localCache.put(cacheKey, JSON.toJSONString(resultList));}}return resultList;}}
2.使用方式
1.依赖注入

image-20240617121126924

2.使用
/*** 注意:这里使用本地缓存的原因是分类和标签的改动不大* @param subjectCategoryBO* @return*/
@Override
public List<SubjectCategoryBO> queryCategoryAndLabel(SubjectCategoryBO subjectCategoryBO) {// 要查询的分类idLong categoryId = subjectCategoryBO.getId();// 构建一个本地缓存的key,结果是根据id查询,所以key也根据这个构建String cacheKey = "categoryAndLabel." + categoryId;// 使用本地缓存工具类,本地缓存中获取数据,如果没有再从数据库中获取,然后将其放到本地缓存中List<SubjectCategoryBO> subjectCategoryBOS = listLocalCacheUtil.getResult(cacheKey, SubjectCategoryBO.class,() -> {// 函数式接口,参数:空,返回值:List<SubjectCategoryBO>,对象逻辑:从db中查List<SubjectCategoryBO>,然后返回return getSubjectCategoryBOS(subjectCategoryBO.getId());});return subjectCategoryBOS;
}
3.测试

image-20240617121034988

相关文章:

用户上下文打通+本地缓存Guava

文章目录 &#x1f31e; Sun Frame&#xff1a;SpringBoot 的轻量级开发框架&#xff08;个人开源项目推荐&#xff09;&#x1f31f; 亮点功能&#x1f4e6; spring cloud模块概览常用工具 &#x1f517; 更多信息1.设计1.链路流程2.详细设计 2.网关过滤器获取唯一标识放到Hea…...

Windows图形界面(GUI)-MFC-C/C++ - 树形视图(Tree Control) - CTreeCtrl

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 树形视图(Tree Control) - CTreeCtrl 创建和初始化 添加和删除项 获取和设置项属性 操作项 项选择变化 项双击 项展开 示例代码 树形视图(Tree Control) - CTreeCtrl 创建和初始…...

C语言 --- 枚举、位运算

&#xff08;一&#xff09;枚举 1.概念&#xff1a;枚举是指将变量的值一一列举出来&#xff0c;变量的值只限于列举出来的值的范围 2.作用&#xff1a;a.提高代码可读性&#xff1b;b.提高代码的安全性 3.枚举类型&#xff1a; enum 枚举名 { 列举各种值 //枚举元素或枚…...

12322222222

当您和老王不在同一个网段时&#xff0c;您们之间的通信需要通过路由器来实现。这是因为不同的网段被视为不同的网络&#xff0c;而路由器的作用之一就是连接不同的网络并负责数据包的转发。下面是详细的通信流程&#xff1a; 本地网络通信尝试&#xff1a;您的设备&#xff0…...

知识改变命运:Java 语言 【可变参数】

可变参数 概念&#xff1a;Java允许一个类中多个同名同功能但是参数不同的方法&#xff0c;封装为一个方法。 基本语法&#xff1a; 访问修饰符 返回值 方法名 (数据类型...参数名) { ...... }案例&#xff1a;写一个类名DyMethod 方法名sum 计算两个整数和&#xff0c;三个整…...

Spring及相关框架的重要的问题

Java框架 问题一&#xff1a;Spring框架中的单例bean是线程安全的吗&#xff1f; 看下图&#xff0c;不能被修改的成员变量就是无状态的类&#xff0c;无状态的类没有线程安全问题&#xff0c;所以在开发中尽量避免可修改的成员变量。 回答&#xff1a;不是线程安全的&#xf…...

Linux Vim教程

Linux Vim 教程 Vim&#xff08;Vi IMproved&#xff09;是一个强大的文本编辑器&#xff0c;广泛用于编程和系统管理。本文将带你全面了解 Vim 的基础使用、常用命令、高级功能等。 1. 安装 Vim 在大多数 Linux 发行版中&#xff0c;Vim 已经预装。如果没有&#xff0c;可以…...

【学习笔记】多进程信号量控制

目录 1、CreateSemaphore 2、ReleaseSemaphore 3、CreateEvent 4、SetEvent 5、WaitForSingleObject 程序案例1&#xff1a; 程序案例2&#xff1a; 1、CreateSemaphore 创建一个计数信号量对象&#xff0c;成功时返回信号量对象的句柄&#xff1b;失败时返回NULL&…...

Redis与Memorycache的区别

Redis与Memorycache主要是持久线程和持久化的区别 1、从性能方面来说&#xff1a; Redis是单线程的&#xff0c;优点是CPU开销小&#xff0c;省去多线程线程之间切换的开销&#xff0c;但是相对于Memorycache来说海量数据的相对较低 Memorycache使用了多线程技术&#xff0c;数…...

docker和Helm Chart的基本命令和操作

一、docker基本命令和操作 1. docker login【登录】 登录 docker client&#xff0c;登录成功之后会显示 Login Succeeded。 docker login登陆到指定的镜像仓库&#xff0c;docker pull 和 docker push 操作都需要预先执行 docker login 操作&#xff1b; 指令&#xff1a;&a…...

Node中的CSRF攻击和防御

Node中的CSRF攻击和防御 假设有一个网上银行系统&#xff0c;用户可以通过该系统进行转账操作。转账功能的URL可能是这样的&#xff1a; https://www.bank.com/transfer?toAccount123456&amount1000当用户登录到银行系统&#xff0c;并在浏览器中访问这个URL时&#xff…...

CSS 多按钮根据半圆弧度排列

需求 多个按钮根据弧度&#xff0c;延边均匀排列。 实现 HTML 分两级&#xff1b;第一级&#xff0c;外层定义按钮的 compose-container 宽度&#xff1b;第二级&#xff0c;按钮集合&#xff0c;使用方法 styleBtn(index)&#xff0c;根据索引计算&#xff1b; <div c…...

【Linux】网络编程套接字Scoket:UDP网络编程

目录 一、了解UDP协议 二、了解端口和IP地址 三、套接字概述与Socket的概念 四、Socket的类型 五、 Socket的信息数据结构 六、网络字节序与主机字节序的互相转换 七、地址转换函数 八、UDP网络编程流程及相关函数 socket函数 bind函数 recvfrom函数 sendto函数 …...

基于模糊PID控制器的puma560机器人控制系统的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 PUMA 560机器人的数学模型 4.2 PID控制原理 4.3 模糊PID控制器的设计 5.完整工程文件 1.课题概述 基于模糊PID控制器的puma560机器人控制系统的simulink建模与仿真&#xff0c;对比传统的PID控制器…...

C语言文件操作超详解

文章目录 1. 为什么使用文件2. 什么是文件2. 1 程序文件2. 2 数据文件2. 3 文件名3. 二进制文件和文本文件? 4. 文件的打开和关闭4. 1 流和标准流4. 1. 1 流4. 1. 2 标准流 4. 2 文件指针4. 3 文件的打开和关闭 5. 文件的顺序读写5. 1 顺序读写函数介绍5. 2 对比一组函数: 6. …...

表字段显示tip

需求背景&#xff1a; 生成的报表&#xff0c;前端只展示字段名称&#xff0c;计算逻辑没有解释&#xff0c;使用方频繁“骚扰”&#xff0c;实在受不了&#xff0c;增加一个字段tip&#xff0c;实现效果&#xff08;下图&#xff09;&#xff1a; 代码 结合使用el-table-colu…...

十二、享元模式

文章目录 1 基本介绍2 案例2.1 Digit 接口2.2 Color 枚举2.3 BigDigit 类2.4 DigitFactory 类2.5 Client 类2.6 Client 类的测试结果2.7 总结 3 各角色之间的关系3.1 角色3.1.1 Flyweight ( 抽象享元 )3.1.2 ConcreteFlyweight ( 具体享元 )3.1.3 UnsharedFlyweight ( 非享元 )…...

黑马Java零基础视频教程精华部分_18_Arrays各种方法

系列文章目录 文章目录 系列文章目录Arrays简介Arrays各种方法toString代码示例binarySearch代码示例copyOf代码示例copyOfRange和fill代码示例sort代码示例 Arrays简介 操作数组的工具类。 Arrays各种方法 toString代码示例 int[]arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; //to…...

RAG私域问答场景超级详细方案(第一期方案)[1]:工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块)

RAG私域问答场景整体夏详细方案(第一期方案):工业级别构建私域问答(知识处理、知识召回排序、搜索问答模块) 大模型性能的跳阶式增长给文本摘要、信息检索、信息抽取、语义问答等自然语言处理任务带来了卓越的性能提升。同时,LangChain 作为一种基于 LLM 的框架,能够快速…...

【AI在医疗领域的应用】AI在疾病诊断、个性化治疗等领域的应用

AI在医疗领域的应用 AI在疾病诊断、个性化治疗等领域的应用 引言 人工智能&#xff08;AI&#xff09;技术正在迅速改变各个行业&#xff0c;而医疗领域无疑是AI应用最广泛、影响最深远的领域之一。AI在医疗中的应用不仅能够提高诊断的准确性和效率&#xff0c;还为个性化治疗…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

高考志愿填报管理系统---开发介绍

高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发&#xff0c;采用现代化的Web技术&#xff0c;为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## &#x1f4cb; 系统概述 ### &#x1f3af; 系统定…...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi&#xff0c;还有多少人在用&#xff1f; 去年&#xff0c;月之暗面创始人杨植麟别提有多风光了。90后清华学霸&#xff0c;国产大模型六小虎之一&#xff0c;手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水&#xff0c;单月光是投流就花费2个亿。 疯…...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目

应用场景&#xff1a; 1、常规某个机器被钓鱼后门攻击后&#xff0c;我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后&#xff0c;我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...

篇章一 论坛系统——前置知识

目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构​编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...