关于Spring和缓存雪崩、穿透、击穿、预热的最常见的十道面试题

面试题一:什么是缓存雪崩?如何解决缓存雪崩?

缓存雪崩指的是在短时间内,有大量的请求直接查询术后句酷,从而对数据库造成大量的压力,严重情况下可能导致数据库宕机的情况叫做缓存雪崩

我们可以看一下正常的情况下程序执行流程图:

 当出现缓存雪崩的时候,流程图如下:

由此可以看出缓存雪崩造成的影响,导致缓存雪崩的主要原因有以下几种:

  1. 缓存过期时间设置不合理:当大量缓存数据在同一时间失效时,会导致大量请求直接打到数据库或者后端服务
  2. 缓存服务器故障:如果缓存服务器发生故障,无法体哦共缓存服务,那么所有请求都会直接访问数据库或后端服务
  3. 缓存数据的热点分布不均:如果某些热门数据集中一部分缓存节点上,当这些节点发生故障或者数据失效的 ,会导致请求直接打到数据库或者后端服务

如何解决缓存雪崩

缓存雪崩的常见解决方法有以下几种:

  • 随机生成缓存过期时间:随机生成缓存过期时间,可以避免缓存同时过期,从而让避免缓存雪崩问题
//缓存原本的失效时间
int exTime = 10*60;
//随机数生成类
Random random = new Random();
//缓存设置
jedis.setex(cacheKey,exTime+random.nextInt(1000) ,value);
  • 使用多级缓存:可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务,例如可以设计二级换内存(分布式缓存+本地缓存),如图:

  • 缓存过期前预加载:在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量请求直接打到数据库或者后端服务
  • 开启限流或降级功能:当缓存发生雪崩时,采用限流或降级的机制来减轻服务器压力,保证系统可用性
  • 实时及监控和预警:通过监控缓存的状态和命中率,及时发现缓存的问题

面试题二:什么是缓存穿透?如何解决缓存穿透?

缓存穿透是指,当我们查询一个数据库和缓存中都不存在的数据时,由于数据库查询结果为空,出于容错考虑,我们通常不会将这个空结果保存到缓存中。因此,每次对这个数据的请求都会直接查询数据库,而不是缓存。这就导致数据库需要处理额外的查询压力,从而可能降低系统的整体性能

简单来说,缓存穿透就是指数据库查询没有数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库

缓存穿透执行流程如下:

其中红色路径代表缓存穿透的执行路径,可以看出缓存穿透会给数据库造成很大压力

如何解决缓存穿透

  1. 缓存空对象:对于查询结果为null或不存在的数据,也可以将它们以特殊值(如:NULL或特殊符号)进行缓存,并设置较短的过期时间。这样,短时间相同的查询请求就可以直接从缓存中获得响应,避免了对数据库的直接查询
  2. 布隆过滤器:在请求达到缓存之前,先通过布隆过滤器判断数据可能存在还是一定不存在。对于不存在的数据,可以直接返回;可能存在的则继续查询缓存和数据库。布隆过滤器是一种空间效率极高的概率型数据结构,他会给出“可能存在”或者“一定不存在”的答案
  3. 开启限流功能:当发现大量连续未命中的请求的时候,可以采用限流策略限制同一时间内向数据库发送的查询请求数量,减轻数据库压力

面试题三:什么是缓存击穿?如何解决缓存击穿?

缓存击穿是指某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求会给数据库造成巨大的压力

缓存击穿的执行流程:

缓存击穿主要的原因是热点数据在缓存中失效或被淘汰,并发请求同属访问该数据,导致缓存无法命中

如何解决缓存击穿 

  • 设置永不过期:对于某些热点缓存,我们可以设置成永不过期,这样就保证缓存的稳定性,但是需要注意在数据更改之后,要及时更新此热点缓存,不然会造成查询结果的误差
  • 缓存过期前预加载:在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量的请求直接打到数据库或者后端服务
  • 使用多级缓存:可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务。例如可以设计多级缓存,也就是使用分布式缓存(Redis)+本地缓存(Caffeine/Guava Cache) ,如下图所示:

  • 开启限流或降级功能:当缓存发生雪崩时,采用限流或降级的机制来减轻服务器压力,保证系统可用性

面试题四:什么是缓存预热?如何实现缓存预热?

缓存与炽热是指在系统启动、高峰期来临之前或者数据变更之后,提前将热门或者需要经常访问的数据加载到缓存中,以提高系统的响应性能和缓存命中率。通过缓存预热,可以避免在实际请求到来的时候出现缓存穿透和缓存击穿的情况,减少对后端存储的直接访问

实现缓存你预热的一般步骤如下:

  1. 确定热门数据:首先需要确定哪些数据是热门或者经常访问的数据。可以通过系统日志、业务需求、数据统计分析等方式进行评估
  2. 加载数据到缓存:在系统启动、高峰期来临之前或者数据变更之后,提前将热门数据加载到缓存中。可以通过定时任务、异步加载、批量加载等方式来实现数据加载
  3. 设置适当的过期时间:根据业务需求和数据的访问频率,设置适当的缓存过期时间。过期时间可以根据不同的数据进行灵活调整,以保证缓存数据的有效性
  4. 监控和维护:在缓存预热完成后,需要进行监控和维护。可以通过监控缓存命中率、缓存失效率指标来评估

缓存预热的实现

手动初始化:在程序启动阶段或者服务初始化的时候,通过编写代码主动的从数据库加载热点数据,并将其放入缓存中(如:Redis)

//初始化阶段加载热点数据
public void warmUpCache() {List<HotData> hotDatas = loadHotDataFromDatabase();for (HotData data : hotDatas) {string key = buildKey(data.getId());redisTemplate.opsForValue().set(key, data expirationTime TimeUnit.MINUTES);}
}

定时任务:使用定时任务定期刷新或者加载数据到缓存中,可以是固定时间间隔,也可以是在数变更后触发

事件驱动:当有新的数据添加到数据库时,触发一个时间来通知缓存系统加载数据

使用框架:某些框架或者中间件提供了缓存预热功能的支持。例如,在Spring Boot项目中,可以通过实现CommandLineRunner或ApplicationRunner接口,在应用启动自动加载数据到缓存

你的代码片段似乎有一些语法错误,我已经为你修正了。在Spring Boot中,一个类可以同时实现CommandLineRunnerApplicationRunner接口,并且可以在run方法中添加自定义的操作。以下是修正后的代码:

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class MyRunner implements CommandLineRunner, ApplicationRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("This is CommandLineRunner"); // 实现自定义操作}@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("This is ApplicationRunner"); // 实现自定义操作}
}

在这段代码中,MyRunner类实现了CommandLineRunnerApplicationRunner接口。当Spring Boot应用启动完成后,它会自动执行这两个接口的run方法。在这两个方法中,你可以添加自定义的操作,比如加载数据到缓存(即缓存预热)等

面试题五:在SpringBoot中如何实现缓存预热?

缓存预热是指在项目启动时,预先将数据加载到缓存系统(如Redis)中的一种机制。在Spring Boot项目中,可以通过以下几种方式实现缓存预热123: 

  1. 使用启动监听事件实现缓存预热:可以使用ApplicationListener监听ContextRefreshedEvent或ApplicationReadyEvent等应用上下文初始化完成事件,在这些事件触发后执行数据加载到缓存的操作。 

    @Component public class CacheWarmer implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 执行缓存预热业务... cacheManager.put("key", dataList); } } 

  2. 使用@PostConstruct注解实现缓存预热:在需要进行缓存预热的类上添加@Component注解,并在其方法中添加@PostConstruct注解和缓存预热的业务逻辑。 

    @Component public class CachePreloader { @Autowired private YourCacheManager cacheManager; @PostConstruct public void preloadCache() { // 执行缓存预热业务... cacheManager.put("key", dataList); } } 

  3. 使用CommandLineRunner或ApplicationRunner实现缓存预热:CommandLineRunner和ApplicationRunner都是Spring Boot应用程序启动后要执行的接口,它们都允许我们在应用启动后执行一些自定义的初始化逻辑,例如缓存预热。 

    @Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String ... args) throws Exception { // 执行缓存预热业务... cacheManager.put("key", dataList); } } 

面试题六:什么是IoC?它解决了什么问题?为什么要使用它?

IoC和AOP是Spring中最核心的两个概念。IoC全称为Inversion of Control,中文意为“控制反转”。它不是一种技术,而是一种设计思想。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制它

IoC解决了什么问题

其实IoC就是把具体实例化对象的步骤交给容器处理。这样可以降低对象之间的耦合度,是得资源变得更容易管理

为什么要用IoC

因为IoC可以帮助我们解决传统开发模式中遇到的问题,比如创建了许多重复的对象,造成大量的资源浪费,更换实现类需要改动多个地方,创建和配置组件工作繁杂,给组件调用方带来极大不便。通过使用IoC,我们可以将对象的控制权(创建、管理)交由IoC容器去管理,我们在使用的时候直接向IoC容器“要”就可以了。这样,我们就可以专注于业务逻辑的实现,而不需要关心对象的创建和管理等一系列的事情

面试题七:IoC和DI有什么关系?IoC的实现除了DI之外,还有其他实现方式吗?

IoC(Inversion of Control,控制反转)和DI(Dependency Injection,依赖注入)是两个密切相关的概念,它们都是面向对象编程和设计模式中的重要思想

IoC是一种设计思想,它基本思想是将对象的创建、销毁、依赖关系的维护等控制权从程序代码中转移出去,交给容器管理。这样可以降低对象之间的耦合度,使得资源变得容易管理,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活

DI是IoC的一种具体的实现方式,它是指将对象锁以来的其他对象(即依赖)通过构造方法、Setter方法或其他方式注入到对象中,从而消除对象之间的耦合关系.DI可以使对象之间的依赖关系更加清晰、简单和可维护

以下是如何在Spring中实现DI:

  1. 构造方法注入:
    @Component
    public class UserService {private final DbDriver dbDriver;public UserService(DbDriver dbDriver) {this.dbDriver = dbDriver;}public void doSomethingWithUser() {List<User> users = dbDriver.searchUsers();// 处理users}
    }
    
  2. Setter方法注入:
    @Component
    public class UserService {private DbDriver dbDriver;@Autowiredpublic void setDbDriver(DbDriver dbDriver) {this.dbDriver = dbDriver;}public void doSomethingWithUser() {List<User> users = dbDriver.searchUsers();// 处理users}
    }
    

总的来说,IoC是一种思想,它强调将对象之间的依赖关系的控制权交给容器来管理;DI则是一种具体的实现方式,它强调将对象所依赖的其他对象通过注入的方式来消除对象之间的耦合关系

面试题八:BeanFactory和FactoryBean有什么区别?

BeanFactory和FactoryBean完全不同的两个接口,BeanFactory是用来管理Bean对象的,而FactoryBean本质上是一个Bean,也是归BeanFactory管理的,但是使用FactoryBean可以来创建普通的Bean对象和AOP代理对象,它们具体区别如下:

BeanFactory:

  • BeanFactory 是 Spring 框架的核心接口之一,用于管理和查找 Spring Bean。
  • 它是一个工厂模式的实现,负责创建和管理 Bean 对象。在 Spring 容器中,BeanFactory 负责实例化、装配和管理 Bean 的生命周期。
  • BeanFactory 的实现类包括 XmlBeanFactoryDefaultListableBeanFactory

其中ApplicationContext就是BeanFactory的子类,咱们通常会使用ApplicationContext来获取某个Bean:

 BeanFactory使用示例:

// 创建BeanFactory容器
BeanFactory beanFactory = new XmlBeanFactory(newClassPathResource("applicationContext.xml"));
// 获取bean实例
YourBean yourBean =(YourBean) beanFactory.getBean("yourBeanName");

BeanFactory的主要使用场景:从IoC容器中获取Bean对象

FactoryBean:

  • FactoryBean 是一个接口,用于创建复杂的 Bean 实例,可以作为一种更高级别的工厂,允许在创建 Bean 时进行更多的控制。
  • 实现 FactoryBean 接口的类必须实现 getObject() 方法,该方法定义了创建和返回实际 Bean 实例的逻辑。
  • 当将实现了 FactoryBean 接口的类配置到 Spring 容器中时,实际上容器会管理这个 FactoryBean 实例,而不是它创建的实际 Bean。要获取实际 Bean,需要调用 FactoryBeangetObject() 方法。
import org.springframework.beans.factory.FactoryBean;public class MyBean implements FactoryBean<Object> {private String message;// 通过构造方法初始化实例public MyBean() {this.message = "通过构造方法初始化实例";}// 方法增强@Overridepublic Object getObject() throws Exception {// 通过 FactoryBean.getObject() 创建实例return new MyBean("通过 FactoryBean.getObject() 创建实例");}@Overridepublic Class<?> getObjectType() {return MyBean.class;}public String getMessage() {return message;}
}

面试题九:什么是AOP?实际工作中AOP的使用场景有哪些?

AOP(面向切片编程)可以说是OOP(面向对象编程)的补充和完善,OOP引入了封装、继承、多态等概念来建立一种公共对象处理的能力,当我们需要处理公共行为的时候,OOP就会显得无能为力,而AOP的出现正好解决了这个问题。比如统一的日志处理模块、授权验证模块等都可以使用AOP很轻松的处理

AOP主要有以下几种:

  1. 集中处理某一类问题,方便维护
  2. 逻辑更加清晰
  3. 降低模块之间的耦合度

AOP常见的使用场景有以下几种:

  1. 用户登录和鉴权
  2. 统一日志记录
  3. 统一方法执行时间统计
  4. 统一返回格式设置
  5. 统一的异常处理
  6. 声明式事务实现

我们用例子更好的理解,AOP的使用场景:

考虑一个面向对象的情景,有一个服务类 UserService 包含一些方法,如 createUserupdateUser 等。如果我们想要在每个方法执行前后记录日志,可能会倾向于在每个方法中添加日志记录的代码。这可能会导致代码重复,而且如果需要修改日志记录的方式,需要在每个方法中进行修改

public class UserService {public void createUser(User user) {// 日志记录 - 方法开始log.info("Creating user: " + user.getName());// 具体的创建用户逻辑// ...// 日志记录 - 方法结束log.info("User created successfully: " + user.getName());}public void updateUser(User user) {// 日志记录 - 方法开始log.info("Updating user: " + user.getName());// 具体的更新用户逻辑// ...// 日志记录 - 方法结束log.info("User updated successfully: " + user.getName());}// 其他方法...
}

在这种情况下,OOP 在处理日志记录时可能显得繁琐,因为我们需要在每个方法中添加相似的日志记录代码

现在,让我们使用 AOP 来处理这个横切关注点。我们可以创建一个日志切面,将日志记录的逻辑从服务类中分离出来。这样,我们只需在切面中定义一次日志记录的逻辑,然后通过配置将切面应用到需要的方法中

@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.UserService.*(..))")public void logMethodStart(JoinPoint joinPoint) {log.info("Method start: " + joinPoint.getSignature().toShortString());}@After("execution(* com.example.UserService.*(..))")public void logMethodEnd(JoinPoint joinPoint) {log.info("Method end: " + joinPoint.getSignature().toShortString());}
}

通过 AOP,我们将日志记录逻辑从业务代码中解耦,实现了统一的日志记录,避免了代码的重复和耦合。这是 AOP 在处理横切关注点时的一个实际应用例子。

面试题十:说一下AOP的底层实现原理?

AOP底层原理可以划分成四个阶段:创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段

第一阶段:创建代理对象阶段

  1. 通过getBean()方法创建Bean实例
  2. 根据AOP的配置匹配目标类的类名,判断是否满足切面规则,规则指的是:
    // 切面规则:匹配所有以 "Service" 结尾的类的所有方法
    execution(* com.example.*Service.*(..))
    
  3. 如果满足规则,调用ProxyFactory创建代理Bean并缓存到IoC容器中
  4. 根据目标对象的是否实现接口选择不同的代理策略,通常是JDK Proxy(基于接口的代理)或Cglib Proxy(基于类的代理)

目标对象:就是我们自己写的业务代码

第二阶段:拦截目标对象阶段

  1. 当用户调用目标对象的方法的时候,被一个名为AopProxy的对象拦截
  2. AopProxy对象封装了所有的调用策略,并且实现了IncationHandler接口
  3. 在IncationHandler的invoke()方法中,出发了MethodInvocation的proceed()方法
  4. proceed()方法按照顺序执行符合所有AOP拦截规则的拦截器链

其中invoke() 方法用于定义切面的逻辑,而 proceed() 方法用于在拦截器链中继续执行下一个拦截器或最终执行目标方法

MethodInvocation:负责执行拦截器链,在proceed()方法中执行;

第三阶段:调用代理对象阶段

  1. AOP拦截器链中的每个元素被称为MethodInterceptor,即切面配置中的Advice通知
  2. MethodInterceptor接口的invoke()方法被织入的代码片段
  3. 这些被织入的代码片段在这个阶段执行,通常是切片配置中定义的通知方法

织入代码:就是要在我们自己写的业务代码增加的代码片段;

切面通知:就是封装织入代码片段的回调方法;

负责执行织入的代码片段,在invoke()方法中执行。

第四阶段:调用目标对象阶段

  1. MethodInterceptor接口中的invoke()方法触发对目标对象方法的调用,即反射调用目标对象的方法,例如:
    public class MyInterceptor implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Before method execution");// 通过反射调用目标对象的方法Object result = invocation.proceed();System.out.println("After method execution");return result;}
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/646378.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CANoe使用大全】——DBC数据库制作

文章目录 1.DBC数据库选择1.1.DBC模板选择1.3. 新建报文1.4. 新建信号1.5.数值表建立 2. DBC导入 1.DBC数据库选择 首先找到DBC编辑器入口 1.1.DBC模板选择 举例说明&#xff1a; 新建选择CANFD的模板 1.3. 新建报文 注意上图中报文周期“Cycle Time”处于不可编辑状态…

初探二分法

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读题目解法一解法二 题目 题目&#xff1a;给定一个 n 个元素有序的&#xff0…

BGP路由反射-数据中心IDC项目经验

一、背景描述 R1,R2,R3在AS200区域内&#xff0c;R1和R2,R1和R3建立OSPF&#xff0c;宣告接口互联. AS200区域内&#xff0c;R1和R2建立IBGP, R1和R3建立IBGP R2和R4建立EBGP, R3和R5建立EBGP。 网络拓扑&#xff1a; 二、故障现象 R1和R2可以收到来自AS100区域R4的E…

nsenter比docker exec更底层的命令

文章目录 nsenter介绍安装方法简单使用 nsenter介绍 nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。典型的用途就是进入容器的网络命令空间。相当多的容器为了轻量级&#xff0c;是不包含较为基础的命令的&#xff0c;比如说ip addr…

系统移植,GNU命令,Uboot移植

一.GNU命令 1、addr2line 把程序地址转换为文件名和行号 做调试 2、ar 建立&#xff0c;修改&#xff0c;提取归档文件 3、Id:GNU arm-none-linux-gnueabi-ld start.o main.o -Tmap.lds -o uart.elf 链接器 4、as 主要用来编译GNU编译器gcc输出的汇编文件&a…

嵌入式-stm32-江科大-EXTI外部中断

一&#xff1a;EXTI外部中断&#xff08;external interrupt&#xff09; 1.1 STM32 中断系统 中断是指在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前的程序&#xff0c;转而去处理中断程序&#xff0c;…

字符金字塔(C语言刷题)

个人博客主页&#xff1a;https://blog.csdn.net/2301_79293429?typeblog 专栏&#xff1a;https://blog.csdn.net/2301_79293429/category_12545690.html 题目描述 请打印输出一个字符金字塔&#xff0c;字符金字塔的特征请参考样例 输入描述: 输入一个字母&#xff0c;保…

5.6 误差相关统计与计算

文章目录 1、平均值2、首次作为参考基准3、绝对差值4、方差、均方差、CV值4.1 方差&#xff08;variance&#xff09;4.2 均方差、标准差&#xff08;Standard Deviation&#xff09;4.3 CV值 1、平均值 Excel 公式&#xff1a;AVERAGE(C4:C20) 2、首次作为参考基准 Excel 操作…

ITSS服务工程师:开启IT职业生涯的金钥匙

&#x1f525;ITSS是中国电子技术标准化研究院推出的&#xff0c;涵盖了“IT服务工程师”和“IT服务经理”的系列培训。它不仅满足GB/T 28827.1的符合性评估要求&#xff0c;还助力IT服务资质升级。 &#x1f3af;“IT服务工程师”培训从服务技术、服务技巧和服务规范三大板块&…

java复习篇 数据结构:链表第二节 哨兵

目录 单向链表哨兵 初始 头插 思路 代码 尾插 思路 遍历 遍历验证头插 尾插代码 尾插测试 get 思路 代码 测试 insert 思路 代码 测试 remove 移除头结点 提问 移除指定位置 测试 单向链表哨兵 单向链表里面有一个特殊的节点称为哨兵节点&#xff0c;…

C# AsyncLocal 是如何实现 Thread 间传值

一&#xff1a;背景 1. 讲故事 这个问题的由来是在.NET高级调试训练营第十期分享ThreadStatic底层玩法的时候&#xff0c;有朋友提出了AsyncLocal是如何实现的&#xff0c;虽然做了口头上的表述&#xff0c;但总还是会不具体&#xff0c;所以觉得有必要用文字图表的方式来系统…

强化数据资产管理,迎接数据资产 “入表” 新时代

2024年伊始&#xff0c;数据要素产业利好政策密集出台&#xff0c;数据资产“入表”成为了大势所趋。数据要素顶层设计方案加速落地&#xff0c;推动企业数字化转型提档加速&#xff0c;提升数据管理能力、实现数据资产价值成为企业下一阶段核心竞争力构建的关键。 01 数据资产…

pytorch与tensorflow如何选择?

目录 1.动态图和静态图1.1 tensorflow是静态图1.2 pytorch动态图 2. 易用性3. 编程语言4. 性能和扩展性5. 社区支持和生态系统 1.动态图和静态图 1.1 tensorflow是静态图 如上图&#xff1a; 定义计算图&#xff08;公式&#xff0c;包括定义变量x,y ,zx*y&#xff09;给公式…

Docker中安装 RabbitMQ

1、下载 RabbitMQ 镜像 下载最新版本的镜像&#xff1a; docker pull rabbitmq更多版本的镜像可以访问 Docker 官网&#xff1a;https://hub.docker.com/_/rabbitmq?tabtags 2、创建并运行 RabbitMQ 容器 启动命令&#xff1a; docker run -d -p 15672:15672 -p 5672:567…

垃圾回收小程序:环保与便捷的完美结合

一、引言 随着科技的发展&#xff0c;移动应用程序已经成为人们日常生活中不可或缺的一部分。其中&#xff0c;废品回收小程序以其独特的价值和功能&#xff0c;日益受到人们的关注和青睐。本文将探讨废品回收小程序开发的重要性、功能特点、技术实现和未来发展趋势。 二、废…

爬虫笔记(二):实战58二手房

第一&#xff1a;给大家推荐一个爬虫的网课哈&#xff0c;码起来 第二&#xff1a;今夜主题&#xff1a;通过xpath爬取58二手房的title信息&#xff0c;也就是标红的位置~ 第三&#xff1a;先分析一波title所在的位置 打开按下f12打开抓包工具&#xff0c;即可看到网站的源码…

JVM-初始JVM

什么是JVM JVM 全称是 Java Virtual Machine&#xff0c;中文译名 Java虚拟机。JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 Java源代码执行流程如下&#xff1a; JVM的功能 1 - 解释和运行 2 - 内存管理 3 - 即时编译 解释和运行 解释…

【二】车载FrameWork添加系统服务

自定义系统服务 常见的AMS、PWS、WMS等等都是系统服务&#xff0c;运行于system_server进程&#xff0c;并且向servicemanager进程注册其Binder以便其他进程获取binder与对应的服务进行通信。为了新增自定义系统服务&#xff0c;我们可以参考AMS等原生系统服务编写如下文件&am…

Python教程:拆分多级目录的方法

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 实现多级目录差分&#xff0c;举例说明如下&#xff1a; 假设现有的目录结构如下&#xff1a;1、2、2.1、2.2、2.3、2.4、3、4、5、6、6.1、6.1.1、6.1.2、6.1.3、6…

WinSCP下载安装并实现远程SSH本地服务器上传文件

文章目录 1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件&#xff0c;它的主要功能是在本地与远程计…