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

SpringBoot源码深度解析

        今天,聊聊SpringBoot的源码,本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2,可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系,因为版本越新,新增的功能越多,反而对SpringBoot源码的研究带来更多的困难,我觉得没必要刻意追求最新,只要掌握其核心流程即可,万变不离其宗。另外,前面我花了大量的时间,一共写了六篇博客,也是为了讲SpringBoot框架做铺垫,Spring/SpringMVC的原理,如果没看的话,建议先看这部分的博客(《Spring源码深度解析(上)、《SpringMVC源码深度解析(上)》),不然直接看SpringBoot源码,会有一定难度。因为我理解的SpringBoot框架,是对Spring FrameWork框架的进一步封装。OK,话不多说,进入正题。

        先看看项目的层级目录:

        依赖也很简单,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-project</artifactId><version>2.0.3.RELEASE</version></parent><modelVersion>4.0.0</modelVersion><artifactId>my-spring-boot</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency></dependencies></project>

        使用过SpringBoot框架的朋友都知道,SpringBoot会有一个启动类,启动类是被@SpringBootApplication注解修饰的。看看App.class的代码:

        那就以SpringApplication这个类最为切入点讲解。先看@SpringBootApplication注解,代码如下:

        可以看出,@SpringBootApplication注解也是可以添加包扫描路径的,最终添加的包扫描路径会设置到@ComponentScan注解中的scanBasePackages或者scanBasePackageClasses属性中去。但是我们一般不会指定,默认扫描的包路径为:App类所在的包及其子包。然后,在@SpringBootApplication注解注解上,还添加了几个注解,分别是:@ComponentScan、@SpringBootConfiguration、@EnableAutoConfiguration等。@ComponentScan自不必说,@SpringBootConfiguration注解实际上又是被@Configuration注解修饰的,如果把@SpringBootConfiguration注解替换成@Configuration注解,也是没有任何问题的。

因此,被@SpringBootApplication注解修饰的类可以直接当配置类使用,也就是可以在类中添加其它类到Spring容器中。重点来了,就是@EnableAutoConfiguration注解,这是SpringBoot实现自动装配的关键,代码如下:

        可以看出,@EnableAutoConfiguration注解可以排除一些类,除此之外,这个注解上面也被其他注解所修饰,分别是:@AutoConfigurationPackage注解和@Import注解。代码中的注释对@AutoConfigurationPackage注解,我说的很清楚:作用就是往Spring容器中注入BasePackages对象,该类存有扫描的包信息,用于其他框架整合SpringBoot的时候,方便获取到这个v包,进行它自己的扫描,需要这样操作的框架还挺多的,如Mybatis、Dubbo、Open Feign等。当然,其他框架用不用这个类是它们的事,但是SpringBoot有提供这样的方式。

        再看看@Import注解,熟悉Spring框架的朋友应该对这个注解很熟悉,它的作用是向Spring容器中注入@Import注解配置的Class。看看AutoConfigurationImportSelector类,代码如下:

        可以看出,AutoConfigurationImportSelector实现了DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware等接口,Spring框架在初始化AutoConfigurationImportSelector的时候,会多次调用回调方法,比如给AutoConfigurationImportSelector设置ConfigurableListableBeanFactory、Environment、ClassLoader、ResourceLoader等对象。其中,DeferredImportSelector接口很重要,根据这个接口的特点:当Spring在解析配置类的时候,当解析完这一轮配置类后,才回调用DeferredImportSelector#selectImports()方法,由于有着一个延迟解析的特点,才能实现这样一个功能:比如Servlet容器有很多种,如Tomcat、Jetty、Undertow等,默认使用Tomcat作为Servlet容器,如果此时开发人员不想用Tomcat,想用Jetty,那应该怎么做呢?很简单,引入Jetty的依赖,排除Tomcat相关依赖即可。这里涉及到ServletWebServerFactoryConfiguration类,代码如下:

        除了我刚刚说的,引入Jetty的依赖,再排除Tomcat相关依赖,可以改成使用Jetty服务器;还有一个方法也可以做到,即不用排除Tomcat的依赖,只需要再引入Jetty的依赖,在自己的配置类中添加JettyServletWebServerFactory即可。因为ServletWebServerFactoryConfiguration这个配置类是Spring在解析完程序员自定义的配置类后再解析的,因此通过@ConditionalOnMissingBean注解进行判断的时候会发现,此时Spring容器中已经有了之前注入的JettyServletWebServerFactory对象了,因此,ServletWebServerFactoryConfiguration中配置三个ServletWebServerFactory对象都不会注入到Spring容器中,最后调用ServletWebServerFactory#getWebServer()方法,得到的只有JettyWebServer,代码如下:

        因此可以知道,AutoConfigurationImportSelector实现DeferredImportSelector接口的作用就是保证程序员的配置大于默认配置!当然,讲到这里,其实还不是SpingBoot的自动装配,自动装配的话,还是要看AutoConfigurationImportSelector#selectImports()方法,代码如下:

        再看看AutoConfigurationImportSelectorget#CandidateConfigurations()方法,看看它是如何获取配置类的,代码如下:

可以看出,上面的逻辑是,通过ClassLoader读取classpath下的META-INF/spring.factories文件,获取文件中的内容,看看spring.factories,如下:

        其中有一个EnableAutoConfiguration类的全限定名,而且确实方法中也传入了EnableAutoConfiguration类的全限定名,因此可以猜到:程序读取spring.factories文件,并通过EnableAutoConfiguration类的全限定名作为Key,获取对用的value,也就是截图中的一大堆类的全限定名,并返回,这就是所有待解析的配置类。其中也不乏我们熟悉的类,如:RabbitAutoConfiguration、AopAutoConfiguration、ElasticsearchDataAutoConfiguration等等。那是不是把这些配置类全部返回,并进行加载解析就行了呢?当然不行,准确的说是没必要,因为这些配置类都是要有相关的依赖,才会起作用,因此需要过滤,当然,如果通过@SpringBootApplication配置,排除配置类或者配置类名,这种也需要过滤。

        以上就是SpringBoot自动装备的原理。如果我们自己要写一个工具,怎么与SpringBoot整合呢?其实很简单,自己写一个项目,在META-INF目录下建一个spring.factories文件,目录为:

在该文件中内容为:org.springframework.boot.autoconfigure.EnableAutoConfiguration=你的配置的全限定名。如果有多个配置类,就用","隔开。这样在pom文件中,引入你写的工具的依赖,SpringBoot就会加载这个配置类,再配合配置上加的条件注解即可。

        回到App类中,看看main方法:

        在该方法中,核心的类是SpringApplication,先看看它的有参构造方法,传入的是App.class,代码如下:

        将传入的App.class存入LinkedHashSet,并赋值给primarySources属性。然后调用SpringApplication#deduceWebApplicationType(),推断应用类型,代码如下:

        再调用SpringApplication#getSpringFactoriesInstances()方法,传入ApplicationContextInitializer.class,加载ApplicationContextInitializer.class接口的实现类,代码如下:

        并将获取到的ApplicationContextInitializer对象的集合,赋值给SpringApplication的initializers属性,代码如下:

        同理,从spring.factories中获取到所有ApplicationListener对象的集合,赋值给SpringApplication的listeners属性中。最后调用SpringApplication#deduceMainApplicationClass()方法,推断主类,代码如下:

         最终获取到的也是App.calss,并赋值给SpringApplication的mainApplicationClass属性。

        以上,就是SpringApplication的有参构造方法。这也这只是完成了SpringApplication初始化工作,但是要让服务跑以来,核心的就是调用SpringApplication#run(String[] args)方法,代码如下:

        其实我的注释写的很详细的,不过我还是带着大家看看。首先是看看SpringApplication#getRunListeners()方法,代码如下:

        还是通过通过spring.factories文件获取SpringApplicationRunListener对象的集合,实际上只有一个实现类,就是 EventPublishingRunListener,在创建这个对象的时候,会调用它的有参构造,传入 SpringApplication对象,有参构造的代码为:

        最终将EventPublishingRunListener对象在设置到SpringApplicationRunListeners对象中,后续在进行时间发布的时候,调用的是SpringApplicationRunListeners的某些方法,代码如下:

        回到SpringApplication#run()方法,接着就是调用SpringApplicationRunListeners#starting()方法,发布ApplicationStartingEvent,调用ApplicationListener#onApplicationEvent()方法,调用之前会先判断,哪些ApplicationListener对象是对ApplicationStartingEvent事件“感兴趣”的。这里没有太多好说的,就不说了,继续往下看,再调用SpringApplication#prepareEnvironment(),这里是处理环境变量,配置就是在这个方法中解析读取的,需要重点看看,代码如下:

        先看看SpringApplication#getOrCreateEnvironment()方法,代码如下:

        看看StandardServletEnvironment的类继承图,如下:

        看看父类的构造,发现AbstractEnvironment父类构造中有做一些初始化的操作,代码如下:

        到这里,可以知道,此时在环境变量中,应该设置了四种属性,顺序(顺序代表着优先级)分别是:StubPropertySource(servletConfigInitParams)、StubPropertySource(servletContextInitParams)、MapPropertySource(systemProperties)、SystemEnvironmentPropertySource(systemEnvironment),只不过前两个,此时还没有任何值,毕竟还没有设置值。打断点看看,我说的是否正确:

        要想获取main方法中的args参数,需要先设置在Idea中设置,设置如下:

        可以知道,最终通过main方法传入的args参数,封装成SimpleCommandLinePropertySource对象,并放入环境变量属性的最前面,此时环境变量有五种属性了。打断点看看:

        再看看SpringApplication#configureProfiles()方法,代码如下:

        重点看看SpringApplicationRunListeners#environmentPrepared()方法,代码如下:

        看这个事件名,可以猜到是处理配置相关的,继续往下看,代码如下:

        这里我可以明确的告诉你,调用的是ConfigFileApplicationListener#onApplicationEvent()方法(我看过SpringBoot v2.6的版本,这个版本中没有使用ConfigFileApplicationListener来解析配置文件了,最低是什么版本就没有再使用ConfigFileApplicationListener类了,这我就不确定了),这个方法会处理配置文件,该方法的代码如下:

        可以知道,会创建RandomValuePropertySource(random)对象,放在SystemEnvironmentPropertySource(systemEnvironment)后面,到目前为止,环境变量一共有六种属性了,打断点看看,代码如下:

        OK,再看看Loader#Loader()方法,代码如下:

        再看看Loader#initializeProfiles()方法:

        可以知道,此时在profiles中有两个对象,也是个空的Set对象,另一个是Profile(default)。回到Loader#Loader()方法继续往下看,接着就是遍历profiles对象,代码如下:

        核心是调用重载方法 Loader#Loader(),代码如下 :

        因此从源码可以知道,环境变量设置:spring.config.location、spring.config.additional-location,可以指定读取文件的路径,如果没有设置的话,默认读取的路径为:file:./config/ 、file:./、classpath:/config/、classpath:/ 等四个路径(顺序即为读取路径的优先级)。并且设置 spring.profiles.active,可以设置文件后缀,如设置为 dev,最后读取的文件为:xx-dev。

然后就是对这四个路径进行遍历,判断那个路径下,有配置文件。除了知道文件路径外,还要知道读取的文件名叫什么,这个也有默认值,当然也可以通过配置去修改默认的文件名,代码如下:

        有了路径和文件名,就可以准备读取了,代码如下:

        看看PropertySourceLoader是如何赋值的,代码如下:

        可以知道,也是从spring.factories文件中读取PropertySourceLoader接口的实现类并实例化,一共有两个,分别是PropertiesPropertySourceLoader 和 YamlPropertySourceLoader。前者用于解析xml和propeties后缀的文件,后者解析yml和yaml后缀的文件,如下:

        继续往下看,代码如下:

        看看Loader#loadDocuments()方法,代码如下:

        到这里就行了,感兴趣的可以自己研究,回到Loader#load()方法,代码如下:

        调用consumer#accept()方法,也就是前面传入的λ表达式,即:

        到目前为止解析的还是application,由于我在application.yml配置了profile,因此还会继续读取:

        回到Loader#load()方法,由于在前面已经读取到application.yml中设置的dev了,并放入Loader的profiles属性中,而且还是在遍历profiles,因此最终会解析application-dev.yml文件,代码如下:

        读取配置的逻辑一样,这里不再赘述,到现在为止,读取的配置还只是存在Loader的loaded属性中,需要放如环境变量中,也就是调用下面的代码,代码如下:

        在读取配置的时候,会多次调用Collections.reverse()方法,改变顺序,其实这就是配置优先级的关键,继续往下看:

        到现在为止,环境变量中已经有八个配置了,其中application-dev.yml的配置在application.yml之前。如果通过环境变量取值的话,就是按照这个顺序来取值的,也就是说,只有前面七个配置中找不到,才会到第八个配置中找!到目前为止,我觉得SpringBoot配置读取这块,应该是讲的很详细了。

        回到SpringApplication#run()方法,继续往下看,代码如下:

        再看看SpringApplication#prepareContext()方法,代码如下:

        看看BeanDefinitionLoader#load()方法,代码如下:

        回到SpringApplication#run()方法,再看看SpringApplication#refreshContext()方法,代码如下:

        看看它的继承关系图:

        AbstractApplicationContext#refresh()方法有多重要,想必就不用我多说了吧,这块的代码在我之前的博客(《Spring源码深度解析(上)》)讲的很详细了,有兴趣的可以看看,其中有两个方法,即onRefresh()方法和finishRefresh()方法,需要我说一下,先看看onRefresh()方法,代码如下:

        其中ServletWebServerApplicationContext#initPropertySources()方法,会将ServletContext属性值设置到环境变量中,代码如下:

        再看ServletWebServerApplicationContext#createWebServer()方法,代码如下:

        再看看finishRefresh()方法,代码如下:

        最后再回到SpringApplication#run()方法看看剩下的代码,如下:

        到这里位置SpringBoot框架的源码算是讲完了,我个人觉得应该是讲的很全面的,如果在讲解的过程中,有漏讲或者讲错的,欢迎指出,感谢~

相关文章:

SpringBoot源码深度解析

今天&#xff0c;聊聊SpringBoot的源码&#xff0c;本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2&#xff0c;可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系&#xff0c;因为版本越新&#xff0c;新增的功能越多&#xff0c;反而对Spri…...

【Qt】常用控件

文章目录 QWidgetenabledgeometrywindow framewindowTitlewindowIconqrc资源管理windowOpacitycursorfonttoolTipfocusPolicystyleSheet 按钮类PushButtonRadioButtonCheckBoxSignals 显示类LabelLCDNumberProgressBarCalendar 输入类LineEditTextEditComboBoxSpinBoxDateTimeE…...

electron 主进程和渲染进程通信

在Electron中,主进程(main process)和渲染进程(renderer process)之间的通信是非常重要的,因为Electron应用通常会将用户界面(由Web技术如HTML, CSS, 和JavaScript构建)和原生功能(如系统对话框、文件I/O等)分开处理。主进程管理应用的生命周期和创建渲染进程,而渲染…...

【ARM】MDK-解决CMSIS_DAP.DLL missing报错

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 记录解决CMSIS_DAP.DLL missing的报错情况&#xff0c;对应相关报错信息&#xff0c;供后续客户参考&#xff0c;快速解决客户问题。 2、 问题场景 客户进行硬件调试时&#xff0c;发现Target设置内有CMSIS_DAP.DL…...

CSS 的环境变量函数env()

在CSS中&#xff0c;env() 函数并不是传统意义上的“环境变量”函数&#xff0c;如你在编程语言中可能遇到的那样。相反&#xff0c;env() 是CSS中的一个函数&#xff0c;它用于访问由宿主环境&#xff08;如浏览器&#xff09;提供给CSS的自定义属性&#xff08;也称为环境变量…...

数学建模--国赛备赛---TOPSIS算法

目录 1.准备部分 1.1提交材料 1.2MD5码相关要求 2.TOPSIS算法 2.1算法概述 2.2基本概念 2.3算法核心思想 2.4拓展思考 3.适用赛题 3.1适用赛题说明 3.2适用赛题举例 4.赛题分析 4.1指标的分类 4.2数据预处理 4.2.1区间型属性的变换 4.2.2向量规范化 4.3数据加…...

均值滤波算法及实现

均值滤波器的使用场景&#xff1a; 均值滤波器使用于处理一些如上述蓝色线的高斯噪声场景 红色曲线是经过均值滤波处理后的数据。主要因为均值滤波设置数据缓冲区&#xff08;也即延时周期&#xff09;&#xff0c;使得测量值经过缓冲不会出现特别大的变化。 黄色曲线为高斯噪声…...

【Apache Doris】周FAQ集锦:第 16 期

【Apache Doris】周FAQ集锦&#xff1a;第 16 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户…...

单例模式_Golang

目录 一、单例模式 1.1 基本概念 1.2 使用场景 二、Golang实现 2.1 懒汉模式&#xff08;Lazy Loading&#xff09; 一、单例模式 1.1 基本概念 一个类只能生成一个实例&#xff0c;且该类能自行创建这个实例的一种模式,这个定义个人感觉可以拆的通俗一些,在项目的生命周…...

代码随想录 day 18 二叉树

第六章 二叉树part06 详细布置 530.二叉搜索树的最小绝对差 需要领悟一下二叉树遍历上双指针操作&#xff0c;优先掌握递归 题目链接/文章讲解&#xff1a;https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%B…...

降雨量预测 | Matlab基于ARIMA-RBF降雨量预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 降雨量预测 | Matlab基于ARIMA-RBF降雨量预测 注&#xff1a;程序和数据放在一个文件夹。 程序语言为matlab&#xff0c;程序可出预测效果图&#xff0c;指标图; 代码特点&#xff1a;参数化编程、参数可方便更改、代…...

包含示例和模板的流程文档指南

当您的业务扩展时&#xff0c;您会得到越来越多的移动部件&#xff0c;并且需要有人来跟踪复杂性。人员和任务需要以尽可能最高效的方式进行组织&#xff0c;并且您必须找到某种方法让员工知道如何执行有效完成工作所需的流程。 为了使流程可重复&#xff0c;需要对其进行记录…...

51单片机嵌入式开发:15、STC89C52RC操作蜂鸣器实现一个music音乐播放器的音乐盒

STC89C52RC操作蜂鸣器实现一个music音乐播放器的音乐盒 1 概述2 蜂鸣器操作方法3 蜂鸣器发出音声4 硬件电路5 软件实现6 整体工程&#xff1a;7 总结 1 概述 要实现一个基于STC89C52RC单片机的音乐盒&#xff0c;可以按照以下步骤进行&#xff1a; &#xff08;1&#xff09;硬…...

B树(B-Tree)数据结构

1. 什么是B树&#xff1f; B树&#xff08;B-Tree&#xff09;是一种多路搜索树&#xff0c;用于存储和检索大量数据。它是自适应的&#xff0c;适用于各种存储设备和各种数据量。B树的特点是高效的搜索、插入和删除操作&#xff0c;且可以在各种情况下保持树的平衡。 2. B树…...

【BUG】已解决:ModuleNotFoundError: No module named ‘torch‘

已解决&#xff1a;ModuleNotFoundError: No module named ‘torch‘ 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市…...

数据结构——队列(链式结构)

一、队列链式结构定义 队列的链式存储结构是一种用链表实现的队列,它不像顺序存储结构那样需要预先分配固定大小的空间。链式存储结构的队列由节点组成,每个节点包括数据和指向下一个节点的指针。队列的链式存储结构可以动态地分配内存,更灵活地处理数据。在链式存储结构中…...

解决GoLand添加GOROOT提示The selected directory is not a valid home for Go Sdk的问题

现象 解决 在Go安装路径下找到zversion.go文件&#xff0c;我的在D:\Program Files\Go1.21.1\src\runtime\internal\sys下面 打开文件&#xff0c;添加如下内容&#xff1a; const TheVersion go1.21.1保存后再重新添加GOROOT即可...

51单片机(STC8H8K64U/STC8051U34K64)_RA8889驱动TFT大屏_I2C_HW参考代码(v1.3) 硬件I2C方式

本篇介绍单片机使用硬件I2C方式控制RA8889驱动彩屏。 提供STC8H8K64U和STC8051U34K64的参考代码。 【硬件部份】STC8H8K64U/STC8051U34K64 RA8889开发板 7寸TFT 800x480 1. 实物连接图&#xff1a;STC8H8K64URA8889开发板&#xff0c;使用P2口I2C接口&#xff1a; 2.实物连…...

【Python其他检查字符串占字节数的方法】

在Python中&#xff0c;检查字符串在特定编码下占用的字节数&#xff0c;最标准且常用的方法是通过字符串的.encode()方法将字符串转换为字节串&#xff0c;然后使用len()函数来获取这个字节串的长度。这是因为字符串&#xff08;在Python 3中&#xff09;是以Unicode形式存储的…...

梧桐数据库: 数据库技术中的重写子查询技术

数据库技术中的重写子查询技术&#xff0c;是数据库查询优化的一种重要手段。该技术主要通过改变子查询的形式&#xff0c;使其在执行效率和性能上得到优化。以下是对重写子查询技术的详细解析&#xff1a; 一、定义与目的 定义&#xff1a;重写子查询技术是指在数据库查询优…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

小智AI+MCP

什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析&#xff1a;AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github&#xff1a;https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...