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

【Spring专题】Spring底层核心原理解析

目录

  • 前言
  • 阅读导航
  • 前置知识
    • Q1:你能描述一下JVM对象创建过程吗?
    • Q2:Spring的特性是什么?
    • 前置知识总结
  • 课程内容
    • 一、Spring容器的启动
    • 二、一般流程推测
      • 2.1 扫描
      • 2.2 IOC
      • 2.3 AOP
    • 2.4 小结
    • 三、【扫描】过程简单推测
    • 四、【IOC】过程简单推测
      • 4.1 推断构造方法过程细讲
    • 五、【AOP】过程简单推测
    • 六、Spring事务
  • 学习总结

前言

Spring啊,可以说是我们大部分Java玩家【最熟悉的陌生人】了吧。八个字形容:似懂非懂,会也不会
在这里插入图片描述
你说简单应用,我们大家都会,那真要展开说两句的话,那只能来这么两句:这是第一句,接着是第二句,好了我说完了。
在这里插入图片描述
但是啊xdm,据说Spring是一份非常非常非常优秀的源码,不但有丰富的设计模式应用场景,代码写的也很优美,有条理,所以非常推荐大家学习。除了能在日常装逼以外,还能丰富一下见识,提升自己写代码的能力。

阅读导航

阅读对象:有过Spring开发经验的人

前置知识

Q1:你能描述一下JVM对象创建过程吗?

答:看图说话:
在这里插入图片描述

  1. 类加载:在使用一个类之前,Java虚拟机需要先将类的字节码加载到内存中。类加载是Java虚拟机的核心过程,它负责查找类的字节码文件并加载到内存的方法区。类加载包括加载、验证、准备、解析和初始化这五个阶段。
  2. 分配内存:在类加载完成后,Java虚拟机会为对象分配内存空间。内存分配通常在堆(Heap)上进行,但也有一些特殊情况下的对象可以在栈(Stack)上分配内存,例如线程栈上的局部对象。
  3. 实例化(初始化零值):在分配内存后,Java虚拟机会将对象的内存空间初始化为零值。这包括基本类型的默认值(例如0、false等)和引用类型的默认值(null)。
  4. 设置对象头:Java对象在内存中的布局包括对象头和实例数据两部分。对象头存储了一些元数据,如对象的哈希码、锁状态等。在对象创建过程中,Java虚拟机会设置对象头的值。
  5. 执行构造方法:对象创建的最后一步是执行构造方法。构造方法用于初始化对象的实例数据,并执行其他必要的初始化操作。构造方法可以是类的默认构造方法,也可以是自定义的构造方法。
  6. 返回对象引用:对象创建完成后,Java虚拟机会返回一个指向该对象的引用。通过引用,程序可以操作对象的属性和方法。

PS:为什么要问这个问题?因为Spring是IOC技术,就算再怎么玩出花来,他也要按照这个基本流程来创建对象。只不过,可以提前告知大家的是,SpringIOC在这个流程之中,新增了很多槽点,通过热插拔的方式,丰富了IOC的功能!

Q2:Spring的特性是什么?

答:Spring的特性就是IOC跟AOP两大概念!甚至可以这么说:Spring就是实现了AOP技术的IOC容器(容器,容器,容器)

Q3:什么是IOC,什么是AOP?
答:下面答案来源于百度【文心一言】:

  • IOC(控制反转)是一种设计模式(思想),它允许将对象的创建和管理交给Spring容器来处理,而不是在代码中直接创建对象。通过使用IOC,可以将对象的依赖关系从代码中解耦,使得代码更加灵活、可维护和可测试。
  • AOP(面向切面编程)也是一种设计模式(思想),它通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态添加功能。AOP解决了面向对象编程中无法解决的问题,例如事务管理、安全性、日志记录等。

Spring框架通过实现IOC和AOP,使得程序更加模块化、灵活和易于维护。同时,Spring还提供了许多其他模块和功能,如DAO、ORM、WebMVC等,使得它成为一个功能强大的Java开发框架。

前置知识总结

从上面的问题里面,我们提到了一个很重要的东西,即:Spring就是实现了AOP技术的IOC容器。并且,也概括地描述了IOC跟AOP的概念。既然我们也知道了,IOC其实也管理了对象的创建,那么说到对象创建,肯定也离不开我们在Q1说的,对象创建的过程。而且,无论对象怎么创建,谁创建,都没办法离开上面的流程的。
事实上,可以提前告诉大家的是,IOC在创建对象的过程,无非就是在上面的对象创建流程中,丰富了一些细节,新增了一些拓展点,为Spring功能实现提供支持。

课程内容

为了展开对Spring源码的研究,我们这里线大致地串讲一下Spring的一些核心知识点,让大家对Spring的底层一些基础逻辑有个清晰的认知。

一、Spring容器的启动

我想,经历过SSM/SSH时代的朋友,对下面的代码都不会陌生:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!--    <import resource="引入其他bean xml配置文件" />--><bean id="userService" class="org.example.spring.bean.UserService"/>
</beans>

如果真的很陌生也没关系,下面这个可能就相对熟悉一点了:(后面也会围绕这个启动方式的Spring讲解。除了是下面的比较主流,也因为,下面这种方式使用更广、更新,内容相对丰富点!

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
System.out.println(userService);
@Component
public class UserService {public void test() {System.out.println("这是一个测试方法");}
}

哈哈,我估计很多直接进入了Java【SpringBoot】时代的朋友,可能连上面这个都没看到过。
那上面段代码是干啥的呢?很简单,就是启动一个Spring容器而已。上面两个不同的启动方式,也仅仅是Bean注册方式不一样。比如前者是通过读取xml里面的<bean>标签定义,后者是读取的注解式Bean。
到这里,想问大家一个问题,那就是,通过上面第二种方式的代码,你发现了什么?我的发现是:我仅仅只是调用了一行代码,就可以开始使用Spring定义的Bean了,什么依赖注入,AOP啥的,我都没管,直接就可以了。这证明了啥?其实很粗浅,也有点废话,那就是证明:通过这一行代码,里面就帮我完成了所有我们平时使用过的,Spring的基础能力

二、一般流程推测

根据我们之前学习过Spring相关的操作,简单推测一下,这一行代码里面干了什么。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

2.1 扫描

首先是第一点【扫描】。我们在项目中,写了这么多Bean,或者说,我项目里面这么多类,哪些是Bean,哪些是普通类,Spring是怎么识别到的?其实道理很简单的,Spring它也没那么智能,想要获取这个类的信息,Spring肯定要【亲自】去看一眼,才能知道这个类的具体信息。你有这么多少个文件,他就要扫描多少个类。关键代码如下:

// 定义需要扫描的基础包名
@ComponentScan("org.tuling.spring")
public class AppConfig {
}

2.2 IOC

扫描完了所有的文件,那基本上Spring已经能确定,哪些是Bean,哪些是普通类了。接下来,就可以开始创建Bean了,这里,就是所谓的IOC过程

2.3 AOP

AOP肯定是发生在IOC之后的,如果你们了解设计模式里面的【代理模式】的话,理解这一点并不难。毕竟,如果目标对象功能不完整,代理对象的功能也会收到影响。

2.4 小结

在这里插入图片描述

三、【扫描】过程简单推测

之前我们也说了,扫描,就是需要Spring亲自去看一看,哪些是需要被创建Bean的,哪些是不需要的。就拿我们举例用的new AnnotationConfigApplicationContext(AppConfig.class)来说,大概步骤如下:(简单推测,不详)

  1. 它需要先看AppConfig.class,读取扫描包的基础路径
  2. 根据上一步读到的基础路径,遍历包下所有的文件,如果类上存在@Component@Service等注解,则确认为是一个Bean
  3. 筛选完后将读取的Bean信息记录下来,比如说存到一个Map里面,方便后续遍历

在这里插入图片描述

四、【IOC】过程简单推测

IOC过程,其实在Spring中有个比较专业的术语,叫做:Bean的生命周期。简单的几个字,包含了很多内容。在此之前,大家先看一看【前置知识】里面【JVM对象创建过程】,加深一下印象。
其中有:

  1. 新建一个Bean对象。说到新建Bean对象,无论如何都绕不开构造方法的调用。我有多个构造函数,那我应该使用哪个呢?这在Spring内部,叫做:【推断构造方法】;
  2. Bean对象创建好了之后,当然不能忘了我们的:【依赖注入】;
  3. 有一点大家可能用的会比较少,但如果有过经验的同学可能会比较熟悉,比如:ApplicationContextAware,我们可以直接直译,叫做:ApplicationContext感知,感知ApplicationContext,所以通过设置,我们就可以在Bean里面获取到这个组件了;
  4. init方法。如果大家有过@PostConstruct以及InitializingBean使用经验的话,或许知道,创建完Bean之后,在返回Bean之前,还有这一步动作。事实上,根据我们在前言里面说的【IOC在创建对象的过程,无非就是在上面的对象创建流程中,丰富了一些细节,新增了一些拓展点】,所以,在IOC里面还有很多切入点,比如:实例化前、实例化后;初始化前、初始化后等等
  5. 最后,就是进入AOP的流程了。如果需要AOP,则进行AOP,并且返回AOP对象;否则直接返回已经创建好的对象

另外需要注意的是,Bean对象创建出来后:

  1. 如果当前Bean是单例Bean,那么会把该Bean对象存入一个Map<String, Object>,Map的key为beanName,value为Bean对象。这样下次getBean时就可以直接从Map中拿到对应的Bean对象了(实际上,在Spring源码中,这个Map就是单例池);
  2. 如果当前Bean是原型Bean,那么后续没有其他动作,不会存入一个Map,下次getBean时会再次执行上述创建过程,得到一个新的Bean对象。

在这里插入图片描述

4.1 推断构造方法过程细讲

Spring在基于某个类生成Bean的过程中,需要利用该类的构造方法来实例化得到一个对象,但是如果一个类存在多个构造方法,Spring会使用哪个呢?

Spring的判断逻辑如下:

  1. 如果一个类只有一个构造函数,不管构造函数是有参,还是无参,Spring都会使用这个构造函数创建对象,因为没得选了;
  2. 如果这个类存在多个构造函数:
    • 如果存在无参构造函数,则使用无参构造函数。因为在Java里面,无参构造函数本身就具有默认的意思在里面;
    • 如果没有无参构造函数,则看多个无参构造函数,哪个有@Autowired修饰,有就选择;没有就只能报错了

还有一个问题。如果Spring选择了一个有参的构造方法,Spring在调用这个有参构造方法时,需要传入参数,那这个参数是怎么来的呢?答案是:Spring会根据入参的类型和入参的名字去Spring中找Bean对象。
3. 先根据入参类型找,如果只找到一个,那就直接用来作为入参;
4. 如果根据类型找到多个,则再根据入参名字来确定唯一一个;
5. 最终如果没有找到,则会报错,无法创建当前Bean对象。

五、【AOP】过程简单推测

AOP就是进行动态代理,在创建一个Bean的过程中,Spring在最后一步(放入单例池之前)会去判断当前正在创建的这个Bean是不是需要进行AOP,如果需要则会进行动态代理。
那么,如何判断一个Bean是否需要被AOP代理呢?步骤如下:

  1. 找出所有的切面Bean(切面也是Bean来的,或者叫做:特殊的Bean)
  2. 遍历切面中的每个方法,看是否写了@Before、@After等注解(通知)
  3. 如果写了,则判断所对应的Pointcut是否和当前Bean对象的类是否匹配
  4. 如果匹配则表示当前Bean对象有匹配的的Pointcut,表示需要进行AOP

利用cglib进行AOP的大致流程:(看上面的代理范式大概就知道了)

  1. 新增一个代理类XxxProxy,继承自被代理对象XxxTarget,并且持有一个XxxTarget成员变量(这个成员变量需要经过一个Bean的声明周期,即,完成了IOC等)
  2. 在代理类中重写父类的方法
  3. 执行代理类的方法时,调用的代理类的方法,但同时也需要执行切面的逻辑

在这里插入图片描述

然后这里给大家一个【代理模式】的范式:

// 被代理对象
public class ProxyTarget {public void run() {System.out.println("这是普通对象的run");}
}// 代理对象
public class ProxyModel extends ProxyTarget {private ProxyTarget proxyTarget;public void setProxyTarget(ProxyTarget proxyTarget) {this.proxyTarget = proxyTarget;}@Overridepublic void run() {System.out.println("我代理对象可以在这里做加强---1");super.run();System.out.println("我代理对象也可以在这里做加强---2");}
}

六、Spring事务

当我们在某个方法上加了@Transactional注解后,就表示该方法在调用时会开启Spring事务,而这个方法所在的类所对应的Bean对象会是该类的代理对象。
Spring事务的代理对象执行某个方法时的步骤:

  1. 判断当前执行的方法是否存在@Transactional注解
  2. 如果存在,则利用事务管理器(TransactionMananger)新建一个数据库连接
  3. 修改数据库连接的autocommit为false
  4. 执行target.test(),执行程序员所写的业务逻辑代码,也就是执行sql
  5. 执行完了之后如果没有出现异常,则提交,否则回滚

Spring事务是否会失效的判断标准:某个加了@Transactional注解的方法被调用时,要判断到底是不是直接被代理对象调用的,如果是则事务会生效,如果不是则失效。(PS:这一点很容易被疏忽)
另外,还有个经典例子,那就是@Bean在有跟没有@Configuration的时候,结果是不一样的,如下:

声明Bean的方法:

@ComponentScan("org.tuling.spring")
@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService(walletService());}@Beanpublic UserService userService1() {return new UserService(walletService());}@Beanpublic WalletService walletService() {return new WalletService();}
}// UserService声明
public class UserService {private WalletService walletService;public UserService() {}public UserService(WalletService walletService) {this.walletService = walletService;}public WalletService getWalletService() {return walletService;}/*** 自我介绍*/public void selfIntroduction() {System.out.println("你好,我是阿通,我有好多钱");walletService.showMyBalance();}
}

大家看上面的声明Bean的方法,按照设想,WalletService肯定也是一个单例嘛,所以,userServiceuserService1持有的walletService对象肯定是一样的。

调用方法:

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService)context.getBean("userService");System.out.println(userService);System.out.println(userService.getWalletService());System.out.println("--------------------------------");UserService userService1 = (UserService)context.getBean("userService1");System.out.println(userService1);System.out.println(userService1.getWalletService());

结果输出如下:

org.tuling.spring.bean.UserService@2c34f934
org.tuling.spring.bean.WalletService@12d3a4e9
--------------------------------
org.tuling.spring.bean.UserService@240237d2
org.tuling.spring.bean.WalletService@12d3a4e9

看结果,没什么问题,如期输出。但如果我们把声明Bean的方法里面的@Configuration去掉,结果会变成这样:

@ComponentScan("org.tuling.spring")
//@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService(walletService());}@Beanpublic UserService userService1() {return new UserService(walletService());}@Beanpublic WalletService walletService() {return new WalletService();}
}
org.tuling.spring.bean.UserService@710726a3
org.tuling.spring.bean.WalletService@646007f4
--------------------------------
org.tuling.spring.bean.UserService@481a15ff
org.tuling.spring.bean.WalletService@78186a70

为什么,只是简单注释了一个@Configuration结果就不一样了呢?分析如下:

  1. @Bean注解可以将方法返回的对象注册为一个 Bean,并且该 Bean 会被 Spring 容器管理。仅此而已
  2. 所以,在userService()方法重调用walletService()方法,实际上就是一个普通Java调用而已,肯定会重新new WalletService()
  3. 而被@Configuration注解之后,所有方法都将被代理(暂时还没找到源码证据,等后面我看懂了再附上)

学习总结

  1. 简单学习了Spring启动的流程
  2. 通过一些常见Spring操作的串讲,大概了解了一下IOC和AOP的大致流程

相关文章:

【Spring专题】Spring底层核心原理解析

目录 前言阅读导航前置知识Q1&#xff1a;你能描述一下JVM对象创建过程吗&#xff1f;Q2&#xff1a;Spring的特性是什么&#xff1f;前置知识总结 课程内容一、Spring容器的启动二、一般流程推测2.1 扫描2.2 IOC2.3 AOP 2.4 小结三、【扫描】过程简单推测四、【IOC】过程简单推…...

出于网络安全考虑,印度启用本土操作系统”玛雅“取代Windows

据《印度教徒报》报道&#xff0c;印度将放弃微软系统&#xff0c;选择新的操作系统和端点检测与保护系统。 备受期待的 "玛雅操作系统 "将很快用于印度国防部的数字领域&#xff0c;而新的端点检测和保护系统 "Chakravyuh "也将一起面世。 不过&#xf…...

tensotflow中tf.title()和tf.broadcast()

tf.tile() 和 tf.broadcast_to() 都是 TensorFlow 中用于张量复制的函数&#xff0c;但它们的实现方式和使用场景略有不同。 tf.tile() 函数的定义如下&#xff1a; tf.tile(input, multiples, nameNone) 其中&#xff0c;input 表示要复制的张量&#xff0c;multiples 表示…...

想要延长Macbook寿命?这六个保养技巧你必须get!

Mac作为我们工作生活的伙伴&#xff0c;重要性不需要多说。但在使用的过程中&#xff0c;我们总会因不当操作导致Mac出现各种问题。 要想它长久的陪伴&#xff0c;平时的维护与保养自然不能少&#xff0c;Mac的保养很重要的两点就是硬件保养和电脑系统保养&#xff0c;硬件保养…...

mysql基础之触发器的简单使用

1.建立学生信息表 -- 触发器 -- 建立学生信息表 create table s1(id int unsigned auto_increment,name varchar(30),score tinyint unsigned,dept varchar(50),primary key(id) );2.建立学生补考信息表 -- 建立学生补考信息表 create table s2 like s1;3.建立触发器&#xf…...

Spring Boot 配置多数据源【最简单的方式】

Druid连接池 Spring Boot 配置多数据源【最简单的方式】 文章目录 Druid连接池 Spring Boot 配置多数据源【最简单的方式】 0.前言1.基础介绍2.步骤2.1. 引入依赖2.2. 配置文件2.3. 核心源码Druid数据源创建器Druid配置项 DruidConfig 3.示例项目3.1. pom3.1.1. 依赖版本定义3.…...

1、Java简介+DOS命令+编译运行+一个简单的Java程序

Java类型&#xff1a; JavaSE 标准版&#xff1a;以前称为J2SE JavaEE 企业版&#xff1a;包括技术有&#xff1a;Servlet、Jsp&#xff0c;以前称为J2EE JavaME 微型版&#xff1a;以前称为J2ME Java应用&#xff1a; Android平台应用。 大数据平台开发&#xff1a;Hadoo…...

Linux 文件与目录管理,Linux 文件内容查看

目录 Linux 文件与目录管理 处理目录的常用命令 ls (列出目录) mv (移动文件与目录,或修改名称)...

Mysql按小时进行分组统计数据

目录 前言 按1小时分组统计 按2小时分组统计 按X小时分组统计 前言 统计数据时这种是最常见的需求场景&#xff0c;今天写需求时发现按2小时进行分组统计也特别简单&#xff0c;特此记录下。 按1小时分组统计 sql&#xff1a; select hour(pass_time) …...

springboot3日志配置

简介 Spring 使用commons-logging作为内部日志&#xff0c;但是底层日志实现是开放的&#xff0c;可以对接其他日志框架 spring5以及以后common-logging被spring直接自己写了 支持jul, log4j2,logback&#xff0c;springBoot提供了默认的控制台输出配置&#xff0c;也可以配置…...

7款轻量级平面图设计软件推荐

平面图设计的痕迹体现在日常生活的方方面面&#xff0c;如路边传单、杂志、产品包装袋或手机开屏海报等&#xff0c;平面设计软件层出不穷。Photoshop是大多数平面图设计初学者的入门软件&#xff0c;但随着设计师需求的不断提高&#xff0c;平面图设计软件Photoshop逐渐显示出…...

SpringCloud实用篇5——elasticsearch基础

目录 1.初识elasticsearch1.1 了解ES1.1.1 elasticsearch的作用1.1.2 ELK技术栈1.1.3 elasticsearch和lucene1.1.4 总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3 es的一些概念1.3.1 文档和字段1.3.2 索引和映射1.3.3 mysql与elasticsearch 1.4 部署单点…...

SpringCloud整体架构概览

什么是SpringCloud 目标 协调任何服务&#xff0c;简化分布式系统开发。 简介 构建分布式系统不应该是复杂的&#xff0c;SpringCloud对常见的分布式系统模式提供了简单易用的编程模型&#xff0c;帮助开发者构建弹性、可靠、协调的应用程序。SpringCloud是在SpringBoot的基…...

(el-switch)操作(不使用 ts):Element-plus 中 Switch 将默认值修改为 “true“ 与 “false“(字符串)来控制开关

Ⅰ、Element-plus 提供的 Switch 开关组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供 Switch 组件情况&#xff1a; 其一、Element-ui 自提供的 Switch 代码情况为(示例的代码)&#xff1a; // Element-plus 自提供的代码&#xff1a; // 此时是使用了 ts 语言环…...

AI绘画网站都有哪些比较好用?

人工智能绘画网站是一种利用人工智能技术进行图像处理和创作的网站。这些绘画网站通常可以帮助艺术家以人工智能绘画的形式快速生成有趣、美丽和独特的绘画作品。无论你是专业的艺术家还是对人工智能绘画感兴趣的普通人&#xff0c;人工智能绘画网站都可以为你提供新的创作灵感…...

Android应用开发(35)SufaceView基本用法

Android应用开发学习笔记——目录索引 参考Android官网&#xff1a;https://developer.android.com/reference/android/view/SurfaceView 一、SurfaceView简介 SurfaceView派生自View&#xff0c;提供嵌入视图层次结构内部的专用绘图表面&#xff0c;SurfaceView可以在主线程之…...

原生JS手写扫雷小游戏

场景 实现一个完整的扫雷游戏需要一些复杂的逻辑和界面交互。我将为你提供一个简化版的扫雷游戏示例&#xff0c;帮助你入门。请注意&#xff0c;这只是一个基本示例&#xff0c;你可以根据自己的需求进行扩展和改进。 思路 创建游戏板&#xff08;Grid&#xff09;&#xff1…...

网络安全进阶学习第十五课——Oracle SQL注入

文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异&#xff1a;三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…...

线程池死循环系统卡住

案例&#xff1a; 同一个线程池。 首先核心线程数是8&#xff0c;我一次提交了 > 8个主任务&#xff0c;然后主任务又各自开启了几个子任务。 所以子任务没有核心线程来跑&#xff0c;只能放进阻塞队列等。 但主任务又等待子任务的结果&#xff0c;不释放占用线程&#xff…...

多用户微商城多端智慧生态电商系统搭建

多用户微商城多端智慧生态电商系统的搭建步骤如下&#xff1a; 系统规划&#xff1a;在搭建多用户微商城多端智慧生态电商系统之前&#xff0c;需要进行系统规划。包括确定系统的目标、功能、架构、技术选型、开发流程等方面。市场调研&#xff1a;进行市场调研&#xff0c;了…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...