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

通过BeanFactotyPostProcessor动态修改@FeignClient的path

最近项目有个需求,要在启动后,动态修改@FeignClient的请求路径,网上找到的基本都是在@FeignClient里使用${…},通过配置文件来定义Feign的接口路径,这并不能满足我们的需求

由于某些特殊原因,我们的每个接口都有一个interfacePath,定义在接口上的自定义注解中
也就是说@FeignClient定义的接口继承自其他模块,而其他模块的接口上有个自定义注解,描述了该接口的interfacePath,如下:

@FeignClient(value = "x-module")
public interface XXXService extends XApi{
}
@XXXMapping("/member")
public interface XApi {

所以我们需要在每个@FeignClient中将这个@XXXMapping的值添加到path属性,作为跨服务调用的前缀,如果要手动处理每个@FeignClient前缀,未免太不友好,我们希望这个能由程序自动处理

首先看下@FeignClient扫描的过程,看看有没有合适的时机来处理这个问题

使用Feign的项目,一般会在启动类添加注解@EnableFeignClients,先点进这个注解看下

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {String[] value() default {};String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<?>[] defaultConfiguration() default {};Class<?>[] clients() default {};
}

它通过@Import注解导入了一个类FeignClientsRegistrar

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

这个类实现了ImportBeanDefinitionRegistrar接口,这个接口用于在Spring容器初始化过程中,向容器注册一些BeanDefinition,这属于Spring源码的范畴这里就不再赘述,直接看它的registerBeanDefinitions方法实现

    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {this.registerDefaultConfiguration(metadata, registry);this.registerFeignClients(metadata, registry);}

只有两行代码,看名字第二行是注册FeignClient,那么我们的@FeignClient基本可以确定是这行代码在处理了,点进去

这个方法比较长,这里就只贴些关键代码

	LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());............ClassPathScanningCandidateComponentProvider scanner = this.getScanner();scanner.setResourceLoader(this.resourceLoader);scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));Set<String> basePackages = this.getBasePackages(metadata);Iterator var8 = basePackages.iterator();while(var8.hasNext()) {String basePackage = (String)var8.next();candidateComponents.addAll(scanner.findCandidateComponents(basePackage));}Iterator var13 = candidateComponents.iterator();while(var13.hasNext()) {BeanDefinition candidateComponent = (BeanDefinition)var13.next();if (candidateComponent instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = this.getClientName(attributes);this.registerClientConfiguration(registry, name, attributes.get("configuration"));this.registerFeignClient(registry, annotationMetadata, attributes);}}............

先定义了一个扫描器,通过@FeignClient过滤出一组BeanDefinition,也就是上面的candidateComponents,然后遍历,其中有一行代码

Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());

这里就获取了@FeignClient里定义的各种属性,比如value 、path 、contextId等等
然后调用registerFeignClient方法完成注册,进入这个方法

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();Class clazz = ClassUtils.resolveClassName(className, (ClassLoader)null);ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory)registry : null;String contextId = this.getContextId(beanFactory, attributes);String name = this.getName(attributes);FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();factoryBean.setBeanFactory(beanFactory);factoryBean.setName(name);factoryBean.setContextId(contextId);factoryBean.setType(clazz);factoryBean.setRefreshableClient(this.isClientRefreshEnabled());BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {factoryBean.setUrl(this.getUrl(beanFactory, attributes));factoryBean.setPath(this.getPath(beanFactory, attributes));factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));Object fallback = attributes.get("fallback");if (fallback != null) {factoryBean.setFallback(fallback instanceof Class ? (Class)fallback : ClassUtils.resolveClassName(fallback.toString(), (ClassLoader)null));}Object fallbackFactory = attributes.get("fallbackFactory");if (fallbackFactory != null) {factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class)fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), (ClassLoader)null));}return factoryBean.getObject();});definition.setAutowireMode(2);definition.setLazyInit(true);this.validate(attributes);AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();beanDefinition.setAttribute("factoryBeanObjectType", className);beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);boolean primary = (Boolean)attributes.get("primary");beanDefinition.setPrimary(primary);String[] qualifiers = this.getQualifiers(attributes);if (ObjectUtils.isEmpty(qualifiers)) {qualifiers = new String[]{contextId + "FeignClient"};}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);this.registerOptionsBeanDefinition(registry, contextId);}

这个方法虽然长但很清晰,先定义了一个FeignClientFactoryBean,然后生成一个BeanDefinitionBuilder,通过lambda传入了一个InstanceSupplier,其持有了FactoryBean,在InstanceSupplier中,通过设置FactoryBean的url、path属性,确定了@FeignClient请求的路径

我们通过debug观察一下最终生成的BeanDefinition长什么样子,进入registerBeanDefinition方法,先获取了beanName,这个名字就是我们自己接口的全路径名
第二行代码真正地往容器注册了BeanDefinition,在这行打个断点,并设置断点的条件,方便定位到我们的@FeignClient类
在这里插入图片描述
在这里插入图片描述

可以看到生成的BeanDefinition有个instanceSupplier属性

在这里插入图片描述

其内部的AnnotationAttributes就是从@FeignClient注解中解析到的配置,包括value、path等,是 一个Map结构

在这里插入图片描述

看到这里,便已经有了大致思路了,这里的InstanceSupplier,持有了从@FeignClinent中解析到的各种属性,并在将来实例化的时候,将这些属性处理为FeignClient的请求路径

那么我们只要在这步之后,实例化之前,将InstanceSupplier持有的属性修改掉,就可以实现动态修改@FeignClient的请求path了

ImportBeanDefinitionRegistrar的处理发生在BeanFactoryPostProcessor的处理流程中,那么我们可以自定义一个BeanFactoryPostProcessor,来获取Feign处理后的BeanDefinition,取其InstanceSupplier,反射修改其属性

自定义一个BeanFactoryPostProcessor

public class FeignClientProcessor implements BeanFactoryPostProcessor, ResourceLoaderAware, EnvironmentAware {private String feignClientPackage;private ResourceLoader resourceLoader;private Environment environment;............

实现ResourceLoaderAware和EnvironmentAware接口 是为了扫描得到加了@FeignClient注解的类,因为Feign注册的BeanDefinition的名字就是我们接口的全路径名,所以可以扫描后到容器里根据类名取,上面看到有Feign扫描的过程,就直接copy过来用了

其中在EnvironmentAware的回调中,设置了一个Feign的扫描路径,因为此时还在Spring容器刷新的早期阶段,通过@Value注解是取不到配置的

	@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;this.feignClientPackage = environment.getProperty("feign.client.package");}

扫描的代码基本上就是Feign的源码,略微修改,扫描的路径是自定义的,而不是从根路径扫,因为我们自己的项目,Feign接口是在指定位置的,然后扫描到的BeanDefinition,转化为类名,这样就得到了所有@FeignClient标注的类名列表

	private List<String> scanFeignClient() {ClassPathScanningCandidateComponentProvider scanner = this.getScanner();scanner.setResourceLoader(this.resourceLoader);scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(feignClientPackage);return candidateComponents.stream().map(BeanDefinition::getBeanClassName).collect(Collectors.toList());}private ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {boolean isCandidate = false;if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {isCandidate = true;}return isCandidate;}};}

然后针对每个类进行处理
先通过类实现的interface获取自定义注解的接口路径,然后通过类名从容器中获取到Feign处理过的BeanDefinition,取其InstanceSupplier,反射找到存储@FeignClient属性的map,拼接请求前缀作为path添加到map中

	List<String> feignClientList = scanFeignClient();feignClientList.forEach(item -> {GenericBeanDefinition beanDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition(item);Class<?> clazz = beanDefinition.getBeanClass();Class<?> apiInterface = Arrays.stream(clazz.getInterfaces()).filter(i -> i.getName().startsWith("com.xxx") && i.getName().endsWith("Api")).findAny().orElseThrow(() -> new RuntimeException("基础路径未定义"));XXXMapping annotation = apiInterface.getAnnotation(XXXMapping.class);String interfacePath = annotation.value();Supplier<?> instanceSupplier = beanDefinition.getInstanceSupplier();try {Field[] declaredFields = instanceSupplier.getClass().getDeclaredFields();for (Field field : declaredFields) {if (field.getType().isAssignableFrom(Map.class)) {field.setAccessible(true);Map<String, String> map = (Map)field.get(instanceSupplier);String basePath = map.get("value");map.put("path", basePath + interfacePath);}}} catch (Exception e) {log.error("初始化FeignClient失败:", e);}});

完整代码

@Component
@Log4j2
public class FeignClientProcessor implements BeanFactoryPostProcessor, ResourceLoaderAware, EnvironmentAware {private String feignClientPackage;private ResourceLoader resourceLoader;private Environment environment;@Override@SuppressWarnings("unchecked")public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {List<String> feignClientList = scanFeignClient();feignClientList.forEach(item -> {GenericBeanDefinition beanDefinition = (GenericBeanDefinition)configurableListableBeanFactory.getBeanDefinition(item);Class<?> clazz = beanDefinition.getBeanClass();Class<?> apiInterface = Arrays.stream(clazz.getInterfaces()).filter(i -> i.getName().startsWith("com.aic") && i.getName().endsWith("Api")).findAny().orElseThrow(() -> new RuntimeException("基础路径未定义"));XXXMapping annotation = apiInterface.getAnnotation(XXXMapping.class);String interfacePath = annotation.value();Supplier<?> instanceSupplier = beanDefinition.getInstanceSupplier();try {Field[] declaredFields = instanceSupplier.getClass().getDeclaredFields();for (Field field : declaredFields) {Class<?> type = field.getType();if (type.isAssignableFrom(Map.class)) {field.setAccessible(true);Map<String, String> map = (Map)field.get(instanceSupplier);String basePath = map.get("value");map.put("path", basePath + interfacePath);}}} catch (Exception e) {log.error("初始化FeignClient失败:", e);}});}private List<String> scanFeignClient() {ClassPathScanningCandidateComponentProvider scanner = this.getScanner();scanner.setResourceLoader(this.resourceLoader);scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(feignClientPackage);return candidateComponents.stream().map(BeanDefinition::getBeanClassName).collect(Collectors.toList());}private ClassPathScanningCandidateComponentProvider getScanner() {return new ClassPathScanningCandidateComponentProvider(false, this.environment) {protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {boolean isCandidate = false;if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {isCandidate = true;}return isCandidate;}};}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;this.feignClientPackage = environment.getProperty("feign.client.package");}
}

相关文章:

通过BeanFactotyPostProcessor动态修改@FeignClient的path

最近项目有个需求&#xff0c;要在启动后&#xff0c;动态修改FeignClient的请求路径&#xff0c;网上找到的基本都是在FeignClient里使用${…}&#xff0c;通过配置文件来定义Feign的接口路径&#xff0c;这并不能满足我们的需求 由于某些特殊原因&#xff0c;我们的每个接口…...

数据结构与算法系列-二分查找

二分查找 什么是二分查找&#xff1f; 二分查找是一种针对有序集合&#xff0c;每次将要查找的区间缩小一半&#xff0c;直到找到查找元素&#xff0c;或区间被缩小为0。 如何实现二分查找&#xff1f; 实现有3个注意点&#xff1a; 终止条件是 low < high 2.求中点的算…...

CSS 毛玻璃特效运用目录

主要是记录毛玻璃相关的特效实践案例和实现思路。 章节名称完成度难度文章地址完整代码下载地址Glassmorphism 登录表单完成一般文章链接代码下载Glassmorphism 按钮悬停效果完成一般文章链接代码下载Glassmorphism 计算器完成一般文章链接代码下载Glassmorphism 卡片悬停效果…...

如何在Qt6中引入Network模块

2023年10月1日&#xff0c;周日凌晨 2023年10月2日&#xff0c;周一下午 第一次更新 目录 如果用的是CMakeQt Console ApplicationQt Widgets Application如果用的是qmake 如果用的是CMake find_package(Qt6 COMPONENTS Network REQUIRED) target_link_libraries(mytarget…...

2023/10/4 QT实现TCP服务器客户端搭建

服务器端&#xff1a; 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QMessageBox> #include <QDebug>QT_BEGIN_NAMESPACE namespace Ui { cla…...

云原生边缘计算KubeEdge安装配置

1. K8S集群部署&#xff0c;可以参考如下博客 请安装k8s集群&#xff0c;centos安装k8s集群 请安装k8s集群&#xff0c;ubuntu安装k8s集群 2.安装kubEedge 2.1 编辑kube-proxy使用ipvs代理 kubectl edit configmaps kube-proxy -n kube-system #修改kube-proxy#大约在40多行…...

【LeetCode热题100】--35.搜索插入位置

35.搜索插入位置 使用二分查找&#xff1a; class Solution {public int searchInsert(int[] nums, int target) {int low 0,high nums.length -1;while(low < high){//注意每次循环完都要计算midint mid (low high)/2;if(nums[mid] target){return mid;}if(nums[mid]…...

mysql面试题13:MySQL中什么是异步复制?底层实现?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲mysql中什么是异步复制?底层实现? MySQL中的异步复制(Asynchronous Replication)是一种复制模式,主服务器将数据写入二进制日志后,无…...

SpringBoot-Shiro安全权限框架

Apache Shiro是一个强大而灵活的开源安全框架&#xff0c;它干净利落地处理身份认证&#xff0c;授权&#xff0c;企业会话管理和加密。 官网&#xff1a; http://shiro.apache.org/ 源码&#xff1a; https://github.com/apache/shiro Subject&#xff1a;代表当前用户或…...

PostgreSQL基础语法

当谈到关系型数据库管理系统&#xff08;RDBMS&#xff09;时&#xff0c;PostgreSQL是一个备受推崇的选择。它是一个开源的、强大的RDBMS&#xff0c;具有广泛的功能和支持。本文将介绍一些PostgreSQL的基础语法&#xff0c;以帮助您入门。 1. 安装和配置 在开始使用PostgreS…...

编程前置:处理Excel表格,定位单元格位置,输入文字前,让AI机器人知道我说什么

原提问&#xff1a; input输入表头 &#xff08;input内除了/&#xff0c;空格 回车 标点符号等 全部作为单元格分隔符&#xff09; 由我设置input输入的是行or列 给选项 1. 行 2. 列 默认回车或没输入值是列由我设置起始位置行列 例如 3,2 表示3行2列 当我输入3,2 就表示在第…...

Linux基本指令介绍系列第四篇

文章目录 前言一、Linux基本指令介绍1、more指令2、less指令3、head指令4、tail指令5、bc指令6、管道文件介绍7、与时间相关的指令 总结 前言 本文介绍Linux使用时的部分指令&#xff0c;读者如果想了解更多基本指令的使用&#xff0c;可以关注博主的后续的文章。 博主使用的实…...

读取vivo手机截图尺寸移动.jpg等文件

这个代码的设计初衷是为了解决图片处理过程中的一些痛点。想象一下&#xff0c;我们都曾遇到过这样的情况&#xff1a;相机拍摄出来的照片、网络下载的图片&#xff0c;尺寸五花八门&#xff0c;大小不一。而我们又渴望将它们整理成一套拥有统一尺寸的图片&#xff0c;让它们更…...

Web前端-Vue2+Vue3基础入门到实战项目-Day2(指令补充, computed计算属性, watch侦听器, 水果购物车)

Web前端-Vue2Vue3基础入门到实战项目-Day2 指令补充指令修饰符v-bind 对样式控制的增强控制class案例 - 京东秒杀tab导航高亮控制style案例 - 控制进度条 v-model 应用于其他表单元素 computed计算属性基本使用computed计算属性 vs methods方法计算属性完整写法案例 - 成绩 wat…...

ffmpeg之去除视频水印

ffmpeg去除水印使用delogo视频滤镜。 delogo参数: x,y,w,h分别表示logo区域的左上角位置及宽度和高度&#xff1b; show:0表示不显示logo区域&#xff0c;1表示显示logo区域。 执行下面的命令&#xff1a; ffmpeg -i 1.mp4 -vf delogox300:y10:w80:h30:show0 out.mp4 效果…...

第二章 线性表

线性表 线性表的基本概念线性表的顺序存储线性表顺序存储的类型定义线性表基本运算在顺序表上的实现顺序表实现算法的分析 线性表的链接存储单链表的类型定义线性表的基本运算在单链表上的实现 其他运算在单链表上的实现建表删除重复结点 其他链表循环链表双向循环链表 顺序实现…...

Java 超高频常见字符操作【建议收藏】

文章目录 前言1. 字符串拼接2. 字符串查找3. 字符串截取4. 字符串替換5. 字符串分割6. 字符串比较7. 字符串格式化8. 字符串空格处理 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一…...

MongoDB数据库网站网页实例-编程语言Python+Django

程序示例精选 PythonDjangoMongoDB数据库网站网页实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoMongoDB数据库网站网页实例》编写代码&#xff0c;代码整洁&#xff0c;…...

开箱报告,Simulink Toolbox库模块使用指南(七)——S-Fuction Builter模块

S-Fuction Builter S-Fuction Builter模块&#xff0c;Mathworks官方Help对该部分内容的说明如下所示。 DFT算法的原理讲解和模块开发在前几篇文章中已经完成了&#xff0c;本文介绍如何使用S-Fuction Builter模块一步到位地自动开发DFT算法模块&#xff0c;包括建立C MEX S-Fu…...

spring-boot 操作 mongodb 数据库

如何使用 spring-boot 操作 mongodb 数据库 配置文件&#xff1a; spring:data:mongodb:host: 127.0.0.1database: fly_articleDbport: 27017# 可以采取 mysql 写法# uri: mongodb://127.0.0.1/fly_articleDb依赖信息: <?xml version"1.0" encoding"UTF-…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...